You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
844 lines
26 KiB
844 lines
26 KiB
/*==LICENSE==* |
|
|
|
CyanWorlds.com Engine - MMOG client, server and tools |
|
Copyright (C) 2011 Cyan Worlds, Inc. |
|
|
|
This program is free software: you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation, either version 3 of the License, or |
|
(at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
|
|
You should have received a copy of the GNU General Public License |
|
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
|
|
Additional permissions under GNU GPL version 3 section 7 |
|
|
|
If you modify this Program, or any covered work, by linking or |
|
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, |
|
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent |
|
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK |
|
(or a modified version of those libraries), |
|
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, |
|
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG |
|
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the |
|
licensors of this Program grant you additional |
|
permission to convey the resulting work. Corresponding Source for a |
|
non-source form of such a combination shall include the source code for |
|
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered |
|
work. |
|
|
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com |
|
or by snail mail at: |
|
Cyan Worlds, Inc. |
|
14617 N Newport Hwy |
|
Mead, WA 99021 |
|
|
|
*==LICENSE==*/ |
|
#include <algorithm> |
|
#include "hsTimer.h" |
|
#include "hsTemplates.h" |
|
#include "hsStream.h" |
|
#include "plSDL.h" |
|
|
|
#include "plNetMessage/plNetMessage.h" |
|
#include "pnNetCommon/plNetApp.h" |
|
|
|
const plString plSDL::kAgeSDLObjectName = "AgeSDLHook"; |
|
|
|
// static |
|
const uint8_t plStateDataRecord::kIOVersion=6; |
|
|
|
// |
|
// helper |
|
// |
|
void plSDL::VariableLengthRead(hsStream* s, int size, int* val) |
|
{ |
|
// hsAssert(size, "unexpected size"); |
|
|
|
if (size < (1<<8)) |
|
*val = s->ReadByte(); |
|
else |
|
if (size < (1<<16)) |
|
*val = s->ReadLE16(); |
|
else |
|
*val = s->ReadLE32(); |
|
} |
|
|
|
// |
|
// helper |
|
// |
|
void plSDL::VariableLengthWrite(hsStream* s, int size, int val) |
|
{ |
|
// hsAssert(size, "unexpected size"); |
|
|
|
if (size < (1<<8)) |
|
{ |
|
hsAssert(val < (1<<8), "SDL data loss"); |
|
s->WriteByte(val); |
|
} |
|
else |
|
if (size < (1<<16)) |
|
{ |
|
hsAssert(val < (1<<16), "SDL data loss"); |
|
s->WriteLE16(val); |
|
} |
|
else |
|
s->WriteLE32(val); |
|
} |
|
|
|
///////////////////////////////////////////////////////////////////////////////// |
|
// State Data |
|
///////////////////////////////////////////////////////////////////////////////// |
|
plStateDataRecord::plStateDataRecord(const plString& name, int version) : fFlags(0) |
|
, fDescriptor( nil ) |
|
{ |
|
SetDescriptor(name, version); |
|
} |
|
|
|
plStateDataRecord::plStateDataRecord(plStateDescriptor* sd) : fFlags(0) |
|
, fDescriptor( nil ) |
|
{ |
|
IInitDescriptor(sd); |
|
} |
|
|
|
plStateDataRecord::~plStateDataRecord() |
|
{ |
|
IDeleteVarsList(fVarsList); |
|
IDeleteVarsList(fSDVarsList); |
|
} |
|
|
|
void plStateDataRecord::SetDescriptor(const plString& name, int version) |
|
{ |
|
IInitDescriptor(name, version); |
|
} |
|
|
|
|
|
void plStateDataRecord::IDeleteVarsList(VarsList& vars) |
|
{ |
|
std::for_each( vars.begin(), vars.end(), |
|
[](plStateVariable* var) { delete var; } |
|
); |
|
vars.clear(); |
|
} |
|
|
|
void plStateDataRecord::IInitDescriptor(const plString& name, int version) |
|
{ |
|
plStateDescriptor* sd = plSDLMgr::GetInstance()->FindDescriptor(name, version); |
|
//hsAssert( sd, plString::Format("Failed to find sdl descriptor: %s,%d. Missing legacy descriptor?", name.c_str(), version ).c_str() ); |
|
if (sd) |
|
IInitDescriptor(sd); |
|
} |
|
|
|
// |
|
// Point to descriptor. |
|
// setup state variables which correspond to the state descriptor. |
|
// |
|
void plStateDataRecord::IInitDescriptor(const plStateDescriptor* sd) |
|
{ |
|
// pt to state desc |
|
fDescriptor=sd; |
|
|
|
// delete old vars |
|
IDeleteVarsList(fVarsList); |
|
IDeleteVarsList(fSDVarsList); |
|
|
|
// create vars defined by state desc |
|
if (sd) |
|
{ |
|
for(int i = 0; i < sd->GetNumVars(); ++i) |
|
{ |
|
if (plVarDescriptor* vd = sd->GetVar(i)) |
|
{ |
|
if (vd->GetAsSDVarDescriptor()) |
|
{ // it's a var which references another state descriptor. |
|
fSDVarsList.push_back(new plSDStateVariable(vd->GetAsSDVarDescriptor())); |
|
} |
|
else |
|
{ |
|
hsAssert(vd->GetAsSimpleVarDescriptor(), "var class problem"); |
|
fVarsList.push_back(new plSimpleStateVariable(vd->GetAsSimpleVarDescriptor())); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
/////////////////// |
|
// DIRTY VARS |
|
/////////////////// |
|
int plStateDataRecord::IGetNumDirtyVars(const VarsList& vars) const |
|
{ |
|
int i, cnt=0; |
|
for(i=0;i<vars.size();i++) |
|
if (vars[i]->IsDirty()) |
|
cnt++; |
|
return cnt; |
|
} |
|
|
|
int plStateDataRecord::IGetDirtyVars(const VarsList& varsIn, VarsList* varsOut) const |
|
{ |
|
int i; |
|
for(i=0;i<varsIn.size();i++) |
|
if (varsIn[i]->IsDirty()) |
|
varsOut->push_back(varsIn[i]); |
|
return varsOut->size(); |
|
} |
|
|
|
bool plStateDataRecord::IHasDirtyVars(const VarsList& vars) const |
|
{ |
|
int i; |
|
for(i=0;i<vars.size();i++) |
|
if (vars[i]->IsDirty()) |
|
return true; |
|
return false; |
|
} |
|
|
|
//////////////// |
|
// USED VARS |
|
//////////////// |
|
int plStateDataRecord::IGetNumUsedVars(const VarsList& vars) const |
|
{ |
|
int i, cnt=0; |
|
for(i=0;i<vars.size();i++) |
|
if (vars[i]->IsUsed()) |
|
cnt++; |
|
return cnt; |
|
} |
|
|
|
int plStateDataRecord::IGetUsedVars(const VarsList& varsIn, VarsList* varsOut) const |
|
{ |
|
int i; |
|
for(i=0;i<varsIn.size();i++) |
|
if (varsIn[i]->IsUsed()) |
|
varsOut->push_back(varsIn[i]); |
|
return varsOut->size(); |
|
} |
|
|
|
bool plStateDataRecord::IHasUsedVars(const VarsList& vars) const |
|
{ |
|
int i; |
|
for(i=0;i<vars.size();i++) |
|
if (vars[i]->IsUsed()) |
|
return true; |
|
return false; |
|
} |
|
|
|
/////////////////////////////////////// |
|
// IO |
|
/////////////////////////////////////// |
|
|
|
// |
|
// read state vars and indices, return true on success |
|
// |
|
bool plStateDataRecord::Read(hsStream* s, float timeConvert, uint32_t readOptions) |
|
{ |
|
fFlags = s->ReadLE16(); |
|
uint8_t ioVersion = s->ReadByte(); |
|
if (ioVersion != kIOVersion) |
|
return false; |
|
|
|
// |
|
// read simple var data |
|
// |
|
hsAssert(fDescriptor, "State Data Record has nil SDL descriptor"); |
|
if (!fDescriptor) |
|
return false; |
|
|
|
int num; |
|
plSDL::VariableLengthRead(s, fDescriptor->GetNumVars(), &num ); |
|
|
|
// if we are readeing the entire list, we don't need to read each index |
|
bool all = (num==fVarsList.size()); |
|
|
|
int i; |
|
try |
|
{ |
|
for(i=0;i<num;i++) |
|
{ |
|
int idx; |
|
if (!all) |
|
plSDL::VariableLengthRead(s, fDescriptor->GetNumVars(), &idx ); |
|
else |
|
idx=i; |
|
if (idx>=fVarsList.size() || !fVarsList[idx]->ReadData(s, timeConvert, readOptions)) |
|
{ |
|
if (plSDLMgr::GetInstance()->GetNetApp()) |
|
plSDLMgr::GetInstance()->GetNetApp()->ErrorMsg("Failed reading SDL, desc %s", |
|
fDescriptor ? fDescriptor->GetName().c_str("?") : "?"); |
|
return false; |
|
} |
|
} |
|
} |
|
catch(...) |
|
{ |
|
hsAssert( false, |
|
plFormat("Something bad happened while reading simple var data, desc:{}", |
|
fDescriptor ? fDescriptor->GetName() : "?").c_str()); |
|
return false; |
|
} |
|
|
|
// |
|
// read nested var data |
|
// |
|
plSDL::VariableLengthRead(s, fDescriptor->GetNumVars(), &num ); |
|
|
|
// if we are readeing the entire list, we don't need to write each index |
|
all = (num==fSDVarsList.size()); |
|
|
|
try |
|
{ |
|
for(i=0;i<num;i++) |
|
{ |
|
int idx; |
|
if (!all) |
|
plSDL::VariableLengthRead(s, fDescriptor->GetNumVars(), &idx ); |
|
else |
|
idx=i; |
|
if (idx>=fSDVarsList.size() || !fSDVarsList[idx]->ReadData(s, timeConvert, readOptions)) // calls plStateDataRecord::Read recursively |
|
{ |
|
if (plSDLMgr::GetInstance()->GetNetApp()) |
|
plSDLMgr::GetInstance()->GetNetApp()->ErrorMsg("Failed reading nested SDL, desc %s", |
|
fDescriptor ? fDescriptor->GetName().c_str("?") : "?"); |
|
return false; |
|
} |
|
} |
|
} |
|
catch(...) |
|
{ |
|
hsAssert( false, |
|
plFormat("Something bad happened while reading nested var data, desc:{}", |
|
fDescriptor ? fDescriptor->GetName() : "?").c_str()); |
|
return false; |
|
} |
|
|
|
// convert to latest descriptor |
|
// Only really need to do this the first time this descriptor is read... |
|
plStateDescriptor* latestDesc=plSDLMgr::GetInstance()->FindDescriptor(fDescriptor->GetName(), plSDL::kLatestVersion); |
|
hsAssert(latestDesc, plFormat("Failed to find latest sdl descriptor for: {}", fDescriptor->GetName()).c_str()); |
|
bool forceConvert = (readOptions&plSDL::kForceConvert)!=0; |
|
if ( latestDesc && ( forceConvert || ( fDescriptor->GetVersion()!=latestDesc->GetVersion() ) ) ) |
|
{ |
|
DumpToObjectDebugger( "PreConvert" ); |
|
ConvertTo( latestDesc, forceConvert ); |
|
DumpToObjectDebugger( "PostConvert" ); |
|
} |
|
|
|
return true; // ok |
|
} |
|
|
|
// |
|
// write out the state vars, along with their index |
|
// |
|
void plStateDataRecord::Write(hsStream* s, float timeConvert, uint32_t writeOptions) const |
|
{ |
|
#ifdef HS_DEBUGGING |
|
if ( !plSDLMgr::GetInstance()->AllowTimeStamping() && (writeOptions & plSDL::kWriteTimeStamps) ) |
|
{ |
|
hsAssert( false, "SDL behavior flags disallow var timestamping on write.\nRemoving kWriteTimeStamps flag from writeOptions..." ); |
|
writeOptions &= ~plSDL::kWriteTimeStamps; |
|
} |
|
#endif |
|
|
|
s->WriteLE16((uint16_t)fFlags); |
|
s->WriteByte(kIOVersion); |
|
|
|
// |
|
// write simple vars |
|
// |
|
bool dirtyOnly = (writeOptions & plSDL::kDirtyOnly) != 0; |
|
int num = dirtyOnly ? GetNumDirtyVars() : GetNumUsedVars(); |
|
plSDL::VariableLengthWrite(s, fDescriptor->GetNumVars(), num ); // write affected vars count |
|
|
|
// if we are writing he entire list, we don't need to write each index |
|
bool all = (num==fVarsList.size()); |
|
|
|
int i; |
|
for(i=0;i<fVarsList.size(); i++) |
|
{ |
|
if ( (dirtyOnly && fVarsList[i]->IsDirty()) || |
|
(!dirtyOnly && fVarsList[i]->IsUsed()) ) |
|
{ |
|
if (!all) |
|
plSDL::VariableLengthWrite(s, fDescriptor->GetNumVars(), i ); // index |
|
fVarsList[i]->WriteData(s, timeConvert, writeOptions); // data |
|
} |
|
} |
|
|
|
// |
|
// write nested vars |
|
// |
|
num = dirtyOnly ? GetNumDirtySDVars() : GetNumUsedSDVars(); |
|
plSDL::VariableLengthWrite(s, fDescriptor->GetNumVars(), num ); // write affected vars count |
|
|
|
// if we are writing he entire list, we don't need to write each index |
|
all = (num==fSDVarsList.size()); |
|
|
|
for(i=0;i<fSDVarsList.size(); i++) |
|
{ |
|
if ( (dirtyOnly && fSDVarsList[i]->IsDirty()) || |
|
(!dirtyOnly && fSDVarsList[i]->IsUsed()) ) |
|
{ |
|
if (!all) |
|
plSDL::VariableLengthWrite(s, fDescriptor->GetNumVars(), i ); // index |
|
fSDVarsList[i]->WriteData(s, timeConvert, writeOptions); // data, calls plStateDataRecord::Write recursively |
|
} |
|
} |
|
} |
|
|
|
// |
|
// STATIC - read prefix header. returns true on success |
|
// |
|
bool plStateDataRecord::ReadStreamHeader(hsStream* s, plString* name, int* version, plUoid* objUoid) |
|
{ |
|
uint16_t savFlags; |
|
s->ReadLE(&savFlags); |
|
if (!(savFlags & plSDL::kAddedVarLengthIO)) // using to establish a new version in the header, can delete in 8/03 |
|
{ |
|
*name = ""; |
|
return false; // bad version |
|
} |
|
|
|
*name = s->ReadSafeString(); |
|
*version = s->ReadLE16(); |
|
|
|
if (objUoid) |
|
{ |
|
hsAssert(savFlags & plSDL::kHasUoid, "SDL state data rec expecting to read a uoid, but there isn't one"); |
|
} |
|
|
|
if (savFlags & plSDL::kHasUoid) |
|
{ |
|
if (objUoid) |
|
objUoid->Read(s); |
|
else |
|
{ |
|
plUoid tmp; |
|
tmp.Read(s); |
|
} |
|
} |
|
|
|
return true; // ok |
|
} |
|
|
|
// |
|
// non-static - write prefix header. helper fxn |
|
// |
|
void plStateDataRecord::WriteStreamHeader(hsStream* s, plUoid* objUoid) const |
|
{ |
|
uint16_t savFlags=plSDL::kAddedVarLengthIO; // using to establish a new version in the header, can delete in 8/03 |
|
if (objUoid) |
|
savFlags |= plSDL::kHasUoid; |
|
|
|
s->WriteLE(savFlags); |
|
s->WriteSafeString(GetDescriptor()->GetName()); |
|
s->WriteLE16((int16_t)GetDescriptor()->GetVersion()); |
|
if (objUoid) |
|
objUoid->Write(s); |
|
} |
|
|
|
// |
|
// create and prepare a net msg with this data |
|
// |
|
plNetMsgSDLState* plStateDataRecord::PrepNetMsg(float timeConvert, uint32_t writeOptions) const |
|
{ |
|
// save to stream |
|
hsRAMStream stream; |
|
WriteStreamHeader(&stream); |
|
Write(&stream, timeConvert, writeOptions); |
|
|
|
// fill in net msg |
|
plNetMsgSDLState* msg; |
|
if (writeOptions & plSDL::kBroadcast) |
|
msg = new plNetMsgSDLStateBCast; |
|
else |
|
msg = new plNetMsgSDLState; |
|
|
|
msg->StreamInfo()->CopyStream(&stream); |
|
return msg; |
|
} |
|
|
|
// |
|
// Destroys 'this' and makes a total copy of other |
|
// |
|
void plStateDataRecord::CopyFrom(const plStateDataRecord& other, uint32_t writeOptions/*=0*/) |
|
{ |
|
fFlags = other.GetFlags(); |
|
IInitDescriptor(other.GetDescriptor()); |
|
int i; |
|
for(i=0;i<other.GetNumVars();i++) |
|
{ |
|
if (other.GetVar(i)->IsUsed()) |
|
GetVar(i)->CopyData(other.GetVar(i),writeOptions); |
|
} |
|
|
|
for(i=0;i<other.GetNumSDVars();i++) |
|
{ |
|
if (other.GetSDVar(i)->IsUsed()) |
|
GetSDVar(i)->CopyFrom(other.GetSDVar(i),writeOptions); |
|
} |
|
} |
|
|
|
// |
|
// Find the data items which are dirty in 'other' and |
|
// copy them to my corresponding item. |
|
// Requires that records have the same descriptor. |
|
// |
|
void plStateDataRecord::UpdateFrom(const plStateDataRecord& other, uint32_t writeOptions/*=0*/) |
|
{ |
|
if ( GetDescriptor()->GetVersion()!=other.GetDescriptor()->GetVersion() ) |
|
{ |
|
plStateDescriptor* sd=plSDLMgr::GetInstance()->FindDescriptor( other.GetDescriptor()->GetName(), other.GetDescriptor()->GetVersion() ); |
|
hsAssert(sd, plFormat("Failed to find sdl descriptor {},{}. Missing legacy descriptor?", |
|
other.GetDescriptor()->GetName(), other.GetDescriptor()->GetVersion()).c_str()); |
|
ConvertTo( sd ); |
|
} |
|
|
|
hsAssert(other.GetDescriptor()==fDescriptor, |
|
plFormat("descriptor mismatch in UpdateFromDirty, SDL={},{} version {} {}", |
|
GetDescriptor()->GetName(), other.GetDescriptor()->GetName(), |
|
GetDescriptor()->GetVersion(), other.GetDescriptor()->GetVersion()).c_str()); |
|
|
|
bool dirtyOnly = (writeOptions & plSDL::kDirtyOnly); |
|
|
|
int i; |
|
for(i=0;i<other.GetNumVars();i++) |
|
{ |
|
if ( (dirtyOnly && other.GetVar(i)->IsDirty()) || (!dirtyOnly && other.GetVar(i)->IsUsed()) ) |
|
{ |
|
GetVar(i)->NotifyStateChange(other.GetVar(i), GetDescriptor()->GetName()); // see if there is enough difference to send state chg notification |
|
GetVar(i)->CopyData(other.GetVar(i), writeOptions ); // simple vars get copied completely, non-partial |
|
} |
|
} |
|
|
|
for(i=0;i<other.GetNumSDVars();i++) |
|
{ |
|
if ( (dirtyOnly && other.GetSDVar(i)->IsDirty()) || (!dirtyOnly && other.GetSDVar(i)->IsUsed()) ) |
|
GetSDVar(i)->UpdateFrom(other.GetSDVar(i), writeOptions); |
|
} |
|
} |
|
|
|
// |
|
// dirty my items which are different from the corresponding one in 'other'. |
|
// Requires that records have the same descriptor. |
|
// |
|
void plStateDataRecord::FlagDifferentState(const plStateDataRecord& other) |
|
{ |
|
if (other.GetDescriptor()==fDescriptor) |
|
{ |
|
int i; |
|
for(i=0;i<other.GetNumVars();i++) |
|
{ |
|
bool diff = (GetVar(i)->IsUsed() && ! (*other.GetVar(i) == *GetVar(i)) ); |
|
GetVar(i)->SetDirty(diff); |
|
} |
|
|
|
for(i=0;i<other.GetNumSDVars();i++) |
|
{ |
|
bool diff = (GetSDVar(i)->IsUsed() && ! (*other.GetSDVar(i) == *GetSDVar(i)) ); |
|
GetSDVar(i)->SetDirty(diff); |
|
} |
|
} |
|
else |
|
{ |
|
hsAssert(false, plFormat("descriptor mismatch in FlagDifferentState, mine {} {}, other {} {}", |
|
fDescriptor->GetName(), fDescriptor->GetVersion(), |
|
other.GetDescriptor()->GetName(), other.GetDescriptor()->GetVersion()).c_str()); |
|
} |
|
} |
|
|
|
// |
|
// dirty my items which are flagged as alwaysNew. |
|
// |
|
void plStateDataRecord::FlagAlwaysNewState() |
|
{ |
|
int i; |
|
for(i=0;i<GetNumVars();i++) |
|
{ |
|
bool newer= (GetVar(i)->IsUsed() && GetVar(i)->GetVarDescriptor()->IsAlwaysNew()); // flagged to be always new |
|
GetVar(i)->SetDirty(newer); |
|
} |
|
|
|
for(i=0;i<GetNumSDVars();i++) |
|
{ |
|
if (GetSDVar(i)->IsUsed()) |
|
{ |
|
GetSDVar(i)->FlagAlwaysNewState(); |
|
} |
|
} |
|
} |
|
|
|
// |
|
// dirty my items which are newer than the corresponding one in 'other'. |
|
// Requires that records have the same descriptor. |
|
// |
|
void plStateDataRecord::FlagNewerState(const plStateDataRecord& other, bool respectAlwaysNew) |
|
{ |
|
if (other.GetDescriptor()==fDescriptor) |
|
{ |
|
int i; |
|
for(i=0;i<other.GetNumVars();i++) |
|
{ |
|
bool newer= (GetVar(i)->IsUsed() && |
|
(GetVar(i)->GetTimeStamp() > other.GetVar(i)->GetTimeStamp() || // later timestamp |
|
(respectAlwaysNew && GetVar(i)->GetVarDescriptor()->IsAlwaysNew())) ); // flagged to be always new |
|
GetVar(i)->SetDirty(newer); |
|
} |
|
|
|
for(i=0;i<other.GetNumSDVars();i++) |
|
{ |
|
if (GetSDVar(i)->IsUsed()) |
|
{ |
|
GetSDVar(i)->FlagNewerState(*other.GetSDVar(i), respectAlwaysNew); |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
hsAssert(false, plFormat("descriptor mismatch in FlagNewerState, mine {} {}, other {} {}", |
|
fDescriptor->GetName(), fDescriptor->GetVersion(), |
|
other.GetDescriptor()->GetName(), other.GetDescriptor()->GetVersion()).c_str()); |
|
} |
|
} |
|
|
|
// |
|
// assumes matching state descriptors |
|
// |
|
bool plStateDataRecord::operator==(const plStateDataRecord &other) const |
|
{ |
|
// hsAssert(other.GetDescriptor()==fDescriptor, "descriptor mismatch in equality check"); |
|
if (other.GetDescriptor()!=fDescriptor) |
|
return false; |
|
|
|
int i; |
|
for(i=0;i<other.GetNumVars();i++) |
|
{ |
|
if (! (*other.GetVar(i) == *GetVar(i)) ) |
|
return false; |
|
} |
|
|
|
for(i=0;i<other.GetNumSDVars();i++) |
|
{ |
|
if (! (*other.GetSDVar(i) == *GetSDVar(i)) ) |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
// |
|
// Converting from a var in an older dtor to a corresponding var in a newer dtor. |
|
// If possible, use the value from fromVar (type converted, if necessary, to the type of toVar), |
|
// o/wise use toVar's default value. |
|
// Also handle the possiblity of different counts. |
|
// Put the final value in the otherData buffer. |
|
// return false on err |
|
// |
|
bool plStateDataRecord::IConvertVar(plSimpleStateVariable* fromVar, plSimpleStateVariable* toVar /* empty */, bool force) |
|
{ |
|
// first fill the dst using it's default value |
|
toVar->SetFromDefaults(false); |
|
|
|
if (!fromVar) |
|
return true; // no corresponding var in the old state, done |
|
|
|
// Copy the value to the dst, converting if necessary |
|
fromVar->ConvertTo(toVar->GetSimpleVarDescriptor(),force); |
|
toVar->CopyData(fromVar, plSDL::kWriteTimeStamps | plSDL::kKeepDirty); |
|
|
|
return true; // ok |
|
} |
|
|
|
plStateVariable* plStateDataRecord::IFindVar(const VarsList& vars, const plString& name) const |
|
{ |
|
for (int i = 0; i < vars.size(); i++) |
|
{ |
|
if (!vars[i]->GetVarDescriptor()->GetName().CompareI(name)) |
|
return vars[i]; |
|
} |
|
|
|
if (plSDLMgr::GetInstance()->GetNetApp()) |
|
plSDLMgr::GetInstance()->GetNetApp()->ErrorMsg("Failed to find SDL var %s", name.c_str()); |
|
|
|
return nil; |
|
} |
|
|
|
// |
|
// try to convert our data (old version) to other's (new) version. |
|
// return false on err. |
|
// |
|
bool plStateDataRecord::ConvertTo( plStateDescriptor* other, bool force ) |
|
{ |
|
if (!other && !force) |
|
return false; // err |
|
|
|
hsAssert(!fDescriptor->GetName().CompareI(other->GetName()), "descriptor mismatch"); |
|
|
|
if ( !force && (other == fDescriptor || other->GetVersion()==fDescriptor->GetVersion())) |
|
return true; // ok, nothing to do |
|
|
|
hsAssert(other->GetVersion()>=fDescriptor->GetVersion(), "converting to an older state descriptor version?"); |
|
|
|
hsLogEntry( plNetApp::StaticDebugMsg( "SDR(%p) converting sdl record %s from version %d to %d (force:%d)", |
|
this, fDescriptor->GetName().c_str(), fDescriptor->GetVersion(), other->GetVersion(), force ) ); |
|
|
|
// make other StateData to represent other descriptor, |
|
// this will be the destination for the convert operation |
|
plStateDataRecord otherStateData(other); |
|
|
|
// for each var in the other dtor, |
|
// use the corresponding value in mine (type converted if necessary), |
|
// or use other's default value. Put the final value in the otherData buffer |
|
int i; |
|
for(i=0;i<otherStateData.GetNumVars(); i++) |
|
{ |
|
// get other var info |
|
plSimpleStateVariable* otherVar = otherStateData.GetVar(i); |
|
plString otherVarName = otherVar->GetVarDescriptor()->GetName(); |
|
|
|
// find corresponding var in my data |
|
plSimpleStateVariable* myVar=FindVar(otherVarName); |
|
IConvertVar(myVar /* fromVar */, otherVar /* toVar */, force); |
|
} |
|
|
|
// for each nested stateDesc in the other guy, |
|
// run the convert operation on it recursively. |
|
for(i=0;i<otherStateData.GetNumSDVars(); i++) |
|
{ |
|
plSDStateVariable* otherSDVar = otherStateData.GetSDVar(i); |
|
plString otherSDVarName = otherSDVar->GetVarDescriptor()->GetName(); |
|
|
|
// find corresponding var in my data |
|
plSDStateVariable* mySDVar=FindSDVar(otherSDVarName); |
|
if (mySDVar) |
|
{ |
|
mySDVar->ConvertTo( otherSDVar, force ); |
|
otherSDVar->CopyFrom( mySDVar ); |
|
} |
|
} |
|
|
|
// adopt new descriptor and data |
|
CopyFrom(otherStateData, plSDL::kWriteTimeStamps | plSDL::kKeepDirty); |
|
|
|
return true; |
|
} |
|
|
|
// |
|
// |
|
// |
|
void plStateDataRecord::DumpToObjectDebugger(const char* msg, bool dirtyOnly, int level) const |
|
{ |
|
plNetObjectDebuggerBase* dbg = plNetObjectDebuggerBase::GetInstance(); |
|
if (!dbg) |
|
return; |
|
|
|
plString pad = plString::Fill(level * 3, ' '); |
|
|
|
int numVars = dirtyOnly ? GetNumDirtyVars() : GetNumUsedVars(); |
|
int numSDVars = dirtyOnly ? GetNumDirtySDVars() : GetNumUsedSDVars(); |
|
|
|
dbg->LogMsg(fAssocObject.IsValid() ? fAssocObject.GetObjectName().c_str() : " "); |
|
if (msg) |
|
dbg->LogMsg(plFormat("{}{}", pad, msg).c_str()); |
|
|
|
dbg->LogMsg(plFormat("{}SDR(0x{x}), desc={}, showDirty={}, numVars={}, vol={}", |
|
pad, (uintptr_t)this, fDescriptor->GetName(), dirtyOnly, numVars+numSDVars, fFlags&kVolatile).c_str()); |
|
|
|
// dump simple vars |
|
for (size_t i=0; i<fVarsList.size(); i++) |
|
{ |
|
if ( (dirtyOnly && fVarsList[i]->IsDirty()) || (!dirtyOnly && fVarsList[i]->IsUsed()) ) |
|
{ |
|
fVarsList[i]->DumpToObjectDebugger(dirtyOnly, level+1); |
|
} |
|
} |
|
|
|
// dump nested vars |
|
for (size_t i=0; i<fSDVarsList.size(); i++) |
|
{ |
|
if ( (dirtyOnly && fSDVarsList[i]->IsDirty()) || (!dirtyOnly && fSDVarsList[i]->IsUsed()) ) |
|
{ |
|
fSDVarsList[i]->DumpToObjectDebugger(dirtyOnly, level+1); |
|
} |
|
} |
|
} |
|
|
|
void plStateDataRecord::DumpToStream(hsStream* stream, const char* msg, bool dirtyOnly, int level) const |
|
{ |
|
plString pad = plString::Fill(level * 3, ' '); |
|
|
|
int numVars = dirtyOnly ? GetNumDirtyVars() : GetNumUsedVars(); |
|
int numSDVars = dirtyOnly ? GetNumDirtySDVars() : GetNumUsedSDVars(); |
|
|
|
plString logStr = fAssocObject.IsValid() ? fAssocObject.GetObjectName() : " "; |
|
|
|
stream->Write(logStr.GetSize(), logStr.c_str()); |
|
if (msg) |
|
{ |
|
logStr = plFormat("{}{}", pad, msg); |
|
stream->Write(logStr.GetSize(), logStr.c_str()); |
|
} |
|
|
|
logStr = plFormat("{}SDR(0x{x}), desc={}, showDirty={}, numVars={}, vol={}", |
|
pad, (uintptr_t)this, fDescriptor->GetName(), dirtyOnly, numVars+numSDVars, fFlags&kVolatile); |
|
stream->Write(logStr.GetSize(), logStr.c_str()); |
|
|
|
// dump simple vars |
|
for (size_t i=0; i<fVarsList.size(); i++) |
|
{ |
|
if ( (dirtyOnly && fVarsList[i]->IsDirty()) || (!dirtyOnly && fVarsList[i]->IsUsed()) ) |
|
{ |
|
fVarsList[i]->DumpToStream(stream, dirtyOnly, level+1); |
|
} |
|
} |
|
|
|
// dump nested vars |
|
for (size_t i=0; i<fSDVarsList.size(); i++) |
|
{ |
|
if ( (dirtyOnly && fSDVarsList[i]->IsDirty()) || (!dirtyOnly && fSDVarsList[i]->IsUsed()) ) |
|
{ |
|
fSDVarsList[i]->DumpToStream(stream, dirtyOnly, level+1); |
|
} |
|
} |
|
|
|
logStr = "\n"; |
|
stream->Write(logStr.GetSize(), logStr.c_str()); |
|
} |
|
|
|
void plStateDataRecord::SetFromDefaults(bool timeStampNow) |
|
{ |
|
int i; |
|
// set simple vars |
|
for(i=0;i<fVarsList.size(); i++) |
|
{ |
|
fVarsList[i]->SetFromDefaults(timeStampNow); |
|
} |
|
|
|
// set nested vars |
|
for(i=0;i<fSDVarsList.size(); i++) |
|
{ |
|
fSDVarsList[i]->SetFromDefaults(timeStampNow); |
|
} |
|
} |
|
|
|
void plStateDataRecord::TimeStampDirtyVars() |
|
{ |
|
int i; |
|
// set simple vars |
|
for(i=0;i<fVarsList.size(); i++) |
|
{ |
|
if ( fVarsList[i]->IsDirty() ) |
|
fVarsList[i]->TimeStamp(); |
|
} |
|
|
|
// set nested vars |
|
for(i=0;i<fSDVarsList.size(); i++) |
|
{ |
|
if ( fVarsList[i]->IsDirty() ) |
|
fSDVarsList[i]->TimeStamp(); |
|
} |
|
}
|
|
|