/*==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 . 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 #include "hsStream.h" #include "hsTimer.h" #include "hsStlUtils.h" #include "plSDL.h" #include "../pnProduct/pnProduct.h" #include "../pnFactory/plCreatable.h" #include "../pnKeyedObject/plUoid.h" #include "../pnKeyedObject/plKey.h" #include "../pnKeyedObject/plKeyImp.h" #include "../pnNetCommon/plNetApp.h" #include "../pnNetCommon/pnNetCommon.h" #include "../plResMgr/plResManager.h" #include "../plResMgr/plKeyFinder.h" #include "../plUnifiedTime/plClientUnifiedTime.h" #include "../plResMgr/plResManager.h" #include "../plUnifiedTime/plClientUnifiedTime.h" /***************************************************************************** * * VALIDATE_WITH_FALSE_RETURN * Used in var getters and setters to validate incoming parameters and * bail in a non-fatal way on error. * ***/ #define VALIDATE_WITH_FALSE_RETURN(cond) \ if (!(cond)) { \ plSDLMgr::GetInstance()->GetNetApp()->DebugMsg("SDL var. Validation failed: "#cond); \ ASSERT(!(cond)); \ return false; \ } // // // generic creatable which holds any kind of creatable blob // class plSDLCreatableStub : public plCreatable { private: UInt16 fClassIndex; public: void* fData; int fDataLen; plSDLCreatableStub(UInt16 classIndex, int len) : fClassIndex(classIndex),fData(nil),fDataLen(len) {} ~plSDLCreatableStub() { delete [] fData; } const char* ClassName() const { return "SDLCreatable"; } UInt16 ClassIndex() const { return fClassIndex; } void Read(hsStream* s, hsResMgr* mgr) { delete [] fData; fData = TRACKED_NEW char[fDataLen]; s->Read(fDataLen, fData); } void Write(hsStream* s, hsResMgr* mgr) { s->Write(fDataLen, fData); } }; ///////////////////////////////////////////////////// // plStateVarNotificationInfo ///////////////////////////////////////////////////// void plStateVarNotificationInfo::Read(hsStream* s, UInt32 readOptions) { UInt8 saveFlags=s->ReadByte(); // unused char* hint=s->ReadSafeString(); if (hint && !(readOptions & plSDL::kSkipNotificationInfo)) fHintString = (const char*)hint; // we're done with it... delete [] hint; } void plStateVarNotificationInfo::Write(hsStream* s, UInt32 writeOptions) const { UInt8 saveFlags=0; // unused s->WriteSwap(saveFlags); s->WriteSafeString(fHintString.c_str()); } ///////////////////////////////////////////////////// // plStateVariable ///////////////////////////////////////////////////// bool plStateVariable::ReadData(hsStream* s, float timeConvert, UInt32 readOptions) { UInt8 saveFlags; s->ReadSwap(&saveFlags); if (saveFlags & plSDL::kHasNotificationInfo) { GetNotificationInfo().Read(s, readOptions); } return true; } bool plStateVariable::WriteData(hsStream* s, float timeConvert, UInt32 writeOptions) const { bool writeNotificationInfo = ((writeOptions & plSDL::kSkipNotificationInfo)==0); UInt8 saveFlags=0; if (writeNotificationInfo) saveFlags |= plSDL::kHasNotificationInfo; s->WriteSwap(saveFlags); if (writeNotificationInfo) { GetNotificationInfo().Write(s, writeOptions); } return true; } ///////////////////////////////////////////////////// // plSimpleStateVariable ///////////////////////////////////////////////////// void plSimpleStateVariable::IInit() { SetDirty(false); SetUsed(false); fBy=nil; fS=nil; fI=nil; fF=nil; fD=nil; fB=nil; fU=nil; fS32=nil; fC=nil; fT=nil; fTimeStamp.ToEpoch(); } // // delete memory // #define DEALLOC(type, var) \ case type: \ delete [] var; \ break; void plSimpleStateVariable::IDeAlloc() { int cnt = fVar.GetAtomicCount()*fVar.GetCount(); int type = fVar.GetAtomicType(); switch (type) { DEALLOC (plVarDescriptor::kInt, fI) DEALLOC (plVarDescriptor::kAgeTimeOfDay, fF) DEALLOC (plVarDescriptor::kShort, fS) DEALLOC (plVarDescriptor::kByte, fBy) DEALLOC (plVarDescriptor::kFloat, fF) DEALLOC (plVarDescriptor::kTime, fT) DEALLOC (plVarDescriptor::kDouble, fD) DEALLOC (plVarDescriptor::kBool, fB) DEALLOC (plVarDescriptor::kKey, fU) DEALLOC (plVarDescriptor::kString32, fS32) case plVarDescriptor::kCreatable: { if(fC) { int i; // delete each creatable for(i=0;i1) str = str + "("; plVarDescriptor::Type type=fVar.GetAtomicType(); switch(type) { case plVarDescriptor::kAgeTimeOfDay: case plVarDescriptor::kTime: case plVarDescriptor::kDouble: case plVarDescriptor::kFloat: case plVarDescriptor::kInt: case plVarDescriptor::kByte: case plVarDescriptor::kShort: { // handles value in the form "(i,j,k)" for vectors int i=idx*fVar.GetAtomicCount(); for(j=0;jGetType(); int cnt = toVar->GetCount() ? toVar->GetCount() : fVar.GetCount(); if (cnt > fVar.GetCount()) { #if BUILD_TYPE == BUILD_TYPE_DEV FATAL("SDL Convert: array size increased, conversion loses data"); #endif // Reallocate new memory (destroys existing variable state) Alloc(cnt); // match types now fVar.SetCount(cnt); fVar.SetType(toVar->GetType()); fVar.SetAtomicType(toVar->GetAtomicType()); return true; } fVar.SetCount(cnt); // convert count // types are already the same, done. if (fVar.GetType()==newType ) return true; hsLogEntry( plNetApp::StaticDebugMsg( "SSV(%p) converting %s from %s to %s", this, fVar.GetName(), fVar.GetTypeString(), toVar->GetTypeString() ) ); switch(fVar.GetType()) // original type { // FROM RGB case plVarDescriptor::kRGB: if (!IConvertFromRGB(newType)) return false; break; // FROM RGBA case plVarDescriptor::kRGBA: if (!IConvertFromRGBA(newType)) return false; break; // FROM RGB8 case plVarDescriptor::kRGB8: if (!IConvertFromRGB8(newType)) return false; break; // FROM RGBA8 case plVarDescriptor::kRGBA8: if (!IConvertFromRGBA8(newType)) return false; break; // FROM INT case plVarDescriptor::kInt: if (!IConvertFromInt(newType)) return false; break; // FROM SHORT case plVarDescriptor::kShort: if (!IConvertFromShort(newType)) return false; break; // FROM Byte case plVarDescriptor::kByte: if (!IConvertFromByte(newType)) return false; break; // FROM FLOAT case plVarDescriptor::kFloat: if (!IConvertFromFloat(newType)) return false; break; // FROM DOUBLE case plVarDescriptor::kDouble: if (!IConvertFromDouble(newType)) return false; break; // FROM BOOL case plVarDescriptor::kBool: if (!IConvertFromBool(newType)) return false; break; // FROM STRING32 case plVarDescriptor::kString32: if (!IConvertFromString(newType)) return false; break; default: return false; // err } // match types now fVar.SetType(toVar->GetType()); fVar.SetAtomicType(toVar->GetAtomicType()); return true; // ok } ///////////////////////////////////////////////////////////// // SETTERS ///////////////////////////////////////////////////////////// bool plSimpleStateVariable::Set(float v, int idx) { VALIDATE_WITH_FALSE_RETURN(idx < fVar.GetCount()); if (fVar.GetType()==plVarDescriptor::kAgeTimeOfDay) { hsAssert(false, "AgeTime variables are read-only, can't set"); } else if (fVar.GetType()==plVarDescriptor::kFloat) { fF[idx]=v; IVarSet(); return true; } hsAssert(false, "passing wrong value type to SDL variable"); return false; } bool plSimpleStateVariable::Set(double v, int idx) { VALIDATE_WITH_FALSE_RETURN(idx < fVar.GetCount()); if (fVar.GetType()==plVarDescriptor::kDouble) { fD[idx]=v; IVarSet(); return true; } if (fVar.GetType()==plVarDescriptor::kTime) { // convert from, fT[idx].SetFromGameTime(v, hsTimer::GetSysSeconds()); IVarSet(); return true; } hsAssert(false, "passing wrong value type to SDL variable"); return false; } // floatvector bool plSimpleStateVariable::Set(float* v, int idx) { VALIDATE_WITH_FALSE_RETURN(idx < fVar.GetCount()); if (fVar.GetType()==plVarDescriptor::kAgeTimeOfDay) { hsAssert(false, "AgeTime variables are read-only, can't set"); } else if (fVar.GetAtomicType()==plVarDescriptor::kFloat) { int i; int cnt=fVar.GetAtomicCount()*idx; for(i=0;iGetUoid(); } else { fU[idx] = plUoid(); } IVarSet(); return true; } hsAssert(false, "passing wrong value type to SDL variable"); return false; } bool plSimpleStateVariable::Set(plCreatable* v, int idx) { VALIDATE_WITH_FALSE_RETURN(idx < fVar.GetCount()); if (fVar.GetType()==plVarDescriptor::kCreatable) { // copy creatable via stream hsRAMStream stream; if(v) { hsgResMgr::ResMgr()->WriteCreatable(&stream, v); stream.Rewind(); } plCreatable* copy = v ? hsgResMgr::ResMgr()->ReadCreatable(&stream): nil; hsAssert(!v || copy, "failed to create creatable copy"); fC[idx]=copy; IVarSet(); return true; } hsAssert(false, "passing wrong value type to SDL variable"); return false; } ///////////////////////////////////////////////////////////// // GETTERS ///////////////////////////////////////////////////////////// bool plSimpleStateVariable::Get(int* value, int idx) const { VALIDATE_WITH_FALSE_RETURN(idx < fVar.GetCount()); if (fVar.GetAtomicType()==plVarDescriptor::kInt) { int i; int cnt=fVar.GetAtomicCount()*idx; for(i=0;iGetCurrentAgeTimeOfDayPercent(); value[i]=fF[cnt+i]; } return true; } if (fVar.GetAtomicType()==plVarDescriptor::kFloat) { int i; int cnt=fVar.GetAtomicCount()*idx; for(i=0;iFindKey(fU[idx]); if (*value) { const plUoid& newUoid = (*value)->GetUoid(); if (stricmp(newUoid.GetObjectName(), fU[idx].GetObjectName()) != 0) { // uoid names don't match... chances are the key changed in the local data after the key was written to the sdl // do a search by name, which takes longer, to get the correct key std::vector foundKeys; plKeyFinder::Instance().ReallyStupidSubstringSearch(fU[idx].GetObjectName(), fU[idx].GetClassType(), foundKeys, fU[idx].GetLocation()); // not really sure what we can do if it finds MORE then one (they are supposed to be unique names), so just grab the // first one and return it if (foundKeys.size() >= 1) *value = foundKeys[0]; else *value = nil; } } } else { *value = nil; } return true; } hsAssert(false, "passing wrong value type to SDL variable"); return false; } bool plSimpleStateVariable::Get(char value[], int idx) const { VALIDATE_WITH_FALSE_RETURN(idx < fVar.GetCount()); if (fVar.GetType()==plVarDescriptor::kString32) { hsStrcpy(value, fS32[idx]); return true; } hsAssert(false, "passing wrong value type to SDL variable"); return false; } bool plSimpleStateVariable::Get(plCreatable** value, int idx) const { VALIDATE_WITH_FALSE_RETURN(idx < fVar.GetCount()); if (fVar.GetAtomicType()==plVarDescriptor::kCreatable) { *value = nil; plCreatable* v = fC[idx]; if (v) { *value = plFactory::Create(v->ClassIndex()); hsAssert(*value, "failed to create creatable copy"); hsRAMStream stream; v->Write(&stream, hsgResMgr::ResMgr()); stream.Rewind(); (*value)->Read(&stream, hsgResMgr::ResMgr()); } return true; } hsAssert(false, "passing wrong value type to SDL variable"); return false; } ///////////////////////////////////////////////////////////// const char* plSimpleStateVariable::GetKeyName(int idx) const { if (fVar.GetAtomicType()==plVarDescriptor::kKey) { if(!(fU[idx] == plUoid())) // compare to default "nil uoid" { return fU[idx].GetObjectName(); } } hsAssert(false, "passing wrong value type to SDL variable"); return "(nil)"; } #pragma optimize( "g", off ) // disable float optimizations bool plSimpleStateVariable::IWriteData(hsStream* s, float timeConvert, int idx, UInt32 writeOptions) const { #ifdef HS_DEBUGGING if (!IsUsed()) { // hsAssert(false, "plSimpleStateVariable::WriteData doesn't contain data?"); plNetApp::StaticWarningMsg("plSimpleStateVariable::WriteData Var %s doesn't contain data?", GetName()); } #endif int j=idx*fVar.GetAtomicCount(); int i; switch(fVar.GetAtomicType()) { case plVarDescriptor::kAgeTimeOfDay: // don't need to write out ageTime, since it's computed on the fly when Get is called break; case plVarDescriptor::kInt: for(i=0;iWriteSwap32(fI[j+i]); break; case plVarDescriptor::kShort: for(i=0;iWriteSwap16(fS[j+i]); break; case plVarDescriptor::kByte: for(i=0;iWriteByte(fBy[j+i]); break; case plVarDescriptor::kFloat: for(i=0;iWriteSwapScalar(fF[j+i]); break; case plVarDescriptor::kTime: for(i=0;iWriteSwapDouble(fD[j+i]); break; case plVarDescriptor::kBool: for(i=0;iWritebool(fB[j+i]); break; case plVarDescriptor::kKey: for(i=0;iWrite(32, fS32[j+i]); break; case plVarDescriptor::kCreatable: { hsAssert(fVar.GetAtomicCount()==1, "invalid atomic count"); plCreatable* cre = fC[j]; s->WriteSwap16(cre ? cre->ClassIndex() : 0x8000); // creatable class index if (cre) { hsRAMStream ramStream; cre->Write(&ramStream, hsgResMgr::ResMgr()); s->WriteSwap32(ramStream.GetEOF()); // write length cre->Write(s, hsgResMgr::ResMgr()); // write data } } break; } return true; } bool plSimpleStateVariable::IReadData(hsStream* s, float timeConvert, int idx, UInt32 readOptions) { int j=idx*fVar.GetAtomicCount(); int i; switch(fVar.GetAtomicType()) { case plVarDescriptor::kAgeTimeOfDay: // don't need to read in ageTime, since it's computed on the fly when Get is called break; case plVarDescriptor::kInt: for(i=0;iReadSwap32(); break; case plVarDescriptor::kShort: for(i=0;iReadSwap16(); break; case plVarDescriptor::kByte: for(i=0;iReadByte(); break; case plVarDescriptor::kFloat: for(i=0;iReadSwapScalar(); break; case plVarDescriptor::kTime: for(i=0;i=0, "negative unified time"); fT[j+i].SetSecsDouble(newUt); } } break; case plVarDescriptor::kDouble: for(i=0;iReadSwapDouble(); break; case plVarDescriptor::kBool: for(i=0;iReadbool(); break; case plVarDescriptor::kKey: for(i=0;iRead(32, fS32[j+i]); break; case plVarDescriptor::kCreatable: { hsAssert(fVar.GetAtomicCount()==1, "invalid atomic count"); UInt16 hClass = s->ReadSwap16(); // class index if (hClass != 0x8000) { UInt32 len = s->ReadSwap32(); // length if (plFactory::CanCreate(hClass)) { delete fC[j]; fC[j] = plFactory::Create(hClass); } else { plSDLCreatableStub* stub = TRACKED_NEW plSDLCreatableStub(hClass, len); fC[j] = stub; } fC[j]->Read(s, hsgResMgr::ResMgr()); // data } } break; default: hsAssert(false, "invalid atomic type"); return false; } return true; } #pragma optimize( "", on ) // restore optimizations to their defaults bool plSimpleStateVariable::WriteData(hsStream* s, float timeConvert, UInt32 writeOptions) const { #ifdef HS_DEBUGGING if (!IsUsed()) { // hsAssert(false, "plSimpleStateVariable::WriteData Var doesn't contain data?"); plNetApp::StaticWarningMsg("plSimpleStateVariable::WriteData Var %s doesn't contain data?", GetName()); } #endif // write base class data plStateVariable::WriteData(s, timeConvert, writeOptions); // check if the same as default bool sameAsDefaults=false; if (!GetVarDescriptor()->IsVariableLength()) { plSimpleStateVariable def; def.fVar.CopyFrom(&fVar); // copy descriptor def.Alloc(); // and rest def.SetFromDefaults(false /* timeStamp */); // may do nothing if nor default sameAsDefaults = (def == *this); } bool writeTimeStamps = (writeOptions & plSDL::kWriteTimeStamps)!=0; bool writeDirtyFlags = (writeOptions & plSDL::kDontWriteDirtyFlag)==0; bool forceDirtyFlags = (writeOptions & plSDL::kMakeDirty)!=0; bool wantTimeStamp = (writeOptions & plSDL::kTimeStampOnRead)!=0; bool needTimeStamp = (writeOptions & plSDL::kTimeStampOnWrite)!=0; forceDirtyFlags = forceDirtyFlags || (!sameAsDefaults && (writeOptions & plSDL::kDirtyNonDefaults)!=0); // write save flags UInt8 saveFlags = 0; saveFlags |= writeTimeStamps ? plSDL::kHasTimeStamp : 0; saveFlags |= forceDirtyFlags || (writeDirtyFlags && IsDirty()) ? plSDL::kHasDirtyFlag : 0; saveFlags |= wantTimeStamp ? plSDL::kWantTimeStamp : 0; saveFlags |= needTimeStamp ? plSDL::kHasTimeStamp : 0; if (sameAsDefaults) saveFlags |= plSDL::kSameAsDefault; s->WriteSwap(saveFlags); if (needTimeStamp) { // timestamp on write fTimeStamp.ToCurrentTime(); fTimeStamp.Write(s); } else if (writeTimeStamps) { // write time stamps fTimeStamp.Write(s); } // write var data if (!sameAsDefaults) { // list size if (GetVarDescriptor()->IsVariableLength()) s->WriteSwap32(GetVarDescriptor()->GetCount()); // have to write out as long since we don't know how big the list is // list int i; for(i=0;iReadSwap(&saveFlags); bool isDirty = ( saveFlags & plSDL::kHasDirtyFlag )!=0; bool setDirty = ( isDirty && ( readOptions & plSDL::kKeepDirty ) ) || ( readOptions & plSDL::kMakeDirty ); bool wantTimestamp = isDirty && plSDLMgr::GetInstance()->AllowTimeStamping() && ( ( saveFlags & plSDL::kWantTimeStamp ) || ( readOptions & plSDL::kTimeStampOnRead ) ); if (saveFlags & plSDL::kHasTimeStamp) ut.Read(s); else if ( wantTimestamp ) ut.ToCurrentTime(); if (!(saveFlags & plSDL::kSameAsDefault)) { setDirty = setDirty || ( readOptions & plSDL::kDirtyNonDefaults )!=0; // read list size if (GetVarDescriptor()->IsVariableLength()) { UInt32 cnt; s->ReadSwap(&cnt); // have to read as long since we don't know how big the list is if (cnt>=0 && cnt ut) return true; if ( (saveFlags & plSDL::kHasTimeStamp) || (readOptions & plSDL::kTimeStampOnRead) ) TimeStamp(ut); // read list if (!(saveFlags & plSDL::kSameAsDefault)) { int i; for(i=0;iWriteData(&stream, 0, writeOptions); stream.Rewind(); ReadData(&stream, 0, writeOptions); } // // send notification msg if necessary, called internally // #define NOTIFY_CHECK(type, var) \ case type: \ for(i=0;ivar[i])>d) \ { \ notify=true; \ break; \ } \ break; void plSimpleStateVariable::NotifyStateChange(const plSimpleStateVariable* other, const char* sdlName) { if (fChangeNotifiers.size()==0) return; bool different=!(*this == *other); bool notify=false; int numNotifiers=0; if (different) { StateChangeNotifiers::iterator it=fChangeNotifiers.begin(); for( ; it!=fChangeNotifiers.end(); it++) { float d=(*it).fDelta; if (d==0 && different) // delta of 0 means notify on any change { notify=true; } else { int i; int cnt = fVar.GetAtomicCount()*fVar.GetCount(); switch(fVar.GetAtomicType()) { NOTIFY_CHECK(plVarDescriptor::kInt, fI) NOTIFY_CHECK(plVarDescriptor::kShort, fS) NOTIFY_CHECK(plVarDescriptor::kByte, fBy) NOTIFY_CHECK(plVarDescriptor::kFloat, fF) NOTIFY_CHECK(plVarDescriptor::kDouble, fD) } } if (notify) { numNotifiers += (*it).fKeys.size(); (*it).SendNotificationMsg(other /* src */, this /* dst */, sdlName); } } } if (plNetObjectDebuggerBase::GetInstance() && plNetObjectDebuggerBase::GetInstance()->GetDebugging()) { plNetObjectDebuggerBase::GetInstance()->LogMsg( xtl::format("Var %s did %s send notification difference. Has %d notifiers with %d recipients.", GetName(), !notify ? "NOT" : "", fChangeNotifiers.size(), numNotifiers).c_str()); } } // // Checks to see if data contents are the same on two matching vars. // #define EQ_CHECK(type, var) \ case type: \ for(i=0;iGetType(), "type mismatch in equality check"); hsAssert(fVar.GetAtomicCount() == other.GetVarDescriptor()->GetAsSimpleVarDescriptor()->GetAtomicCount(), "atomic cnt mismatch in equality check"); if (GetCount() != other.GetCount()) return false; int i; int cnt = fVar.GetAtomicCount()*fVar.GetCount(); switch(fVar.GetAtomicType()) { EQ_CHECK(plVarDescriptor::kAgeTimeOfDay, fF) EQ_CHECK(plVarDescriptor::kInt, fI) EQ_CHECK(plVarDescriptor::kFloat, fF) EQ_CHECK(plVarDescriptor::kTime, fT) EQ_CHECK(plVarDescriptor::kDouble, fD) EQ_CHECK(plVarDescriptor::kBool, fB) EQ_CHECK(plVarDescriptor::kKey, fU) EQ_CHECK(plVarDescriptor::kCreatable, fC) EQ_CHECK(plVarDescriptor::kShort, fS) EQ_CHECK(plVarDescriptor::kByte, fBy) case plVarDescriptor::kString32: for(i=0;i1) { dbg->LogMsg(logMsg.c_str()); // it's going to be a long msg, so print it on its own line logMsg = ""; } pad += "\t"; for(i=0;iLogMsg(logMsg.c_str()); logMsg = ""; } } void plSimpleStateVariable::DumpToStream(hsStream* stream, bool dirtyOnly, int level) const { std::string pad; int i; for(i=0;i1) { stream->WriteString(logMsg.c_str()); // it's going to be a long msg, so print it on its own line logMsg = ""; } pad += "\t"; for(i=0;iWriteString(logMsg.c_str()); logMsg = ""; } } // // set var to its defalt value // void plSimpleStateVariable::SetFromDefaults(bool timeStampNow) { int i; for(i=0;iGetDefault(), i, timeStampNow); } /////////////////////////////////////////////////////////////////////////////// // plSDStateVariable /////////////////////////////////////////////////////////////////////////////// plSDStateVariable::plSDStateVariable(plSDVarDescriptor* sdvd) : fVarDescriptor(nil) { Alloc(sdvd); } plSDStateVariable::~plSDStateVariable() { IDeInit(); } // // resize the array of state data records // void plSDStateVariable::Resize(int cnt) { int origCnt=GetCount(); // when sending, this is always freshly allocated. if you then set it to zero, // the change won't be sent, even though the version on the server might not be zero // for now, we're just not going to do this optimization // we could, however, change it to (origCnt==cnt==0), because for sizes other // than zero the bug won't happen // if (origCnt==cnt) // return; // no work to do // shrinking if (cntorigCnt) { int i; for(i=origCnt;iGetStateDescriptor()); } SetDirty(true); SetUsed(true); } // // create/allocate data records based on the given SDVarDesc // void plSDStateVariable::Alloc(plSDVarDescriptor* sdvd, int listSize) { if (sdvd==fVarDescriptor) { // trick to not have to delete and recreate fVarDescriptor fVarDescriptor=nil; IDeInit(); fVarDescriptor=sdvd; } else IDeInit(); // will delete fVarDescriptor if (sdvd) { if (fVarDescriptor==nil) { fVarDescriptor = TRACKED_NEW plSDVarDescriptor; fVarDescriptor->CopyFrom(sdvd); } int cnt = listSize==-1 ? sdvd->GetCount() : listSize; fDataRecList.resize(cnt); int j; for (j=0;jGetStateDescriptor()), j); } } // // help alloc fxn // void plSDStateVariable::Alloc(int listSize) { Alloc(fVarDescriptor, listSize); } // // delete all records // void plSDStateVariable::IDeInit() { DataRecList::iterator it; for (it=fDataRecList.begin(); it != fDataRecList.end(); it++) delete *it; fDataRecList.clear(); delete fVarDescriptor; fVarDescriptor=nil; } // // Make 'this' into a copy of 'other'. // void plSDStateVariable::CopyFrom(plSDStateVariable* other, UInt32 writeOptions/*=0*/) { // IDeInit(); Alloc(other->GetSDVarDescriptor(), other->GetCount()); int i; for(i=0; iGetCount(); i++) fDataRecList[i]->CopyFrom(*other->GetStateDataRecord(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 plSDStateVariable::UpdateFrom(plSDStateVariable* other, UInt32 writeOptions/*=0*/) { hsAssert(!stricmp(other->GetSDVarDescriptor()->GetName(), fVarDescriptor->GetName()), xtl::format("var descriptor mismatch in UpdateFrom, name %s,%s ver %d,%d", GetName(), other->GetName()).c_str()); Resize(other->GetCount()); // make sure sizes match bool dirtyOnly = (writeOptions & plSDL::kDirtyOnly); int i; for(i=0; iGetCount(); i++) { if ( (dirtyOnly && other->GetStateDataRecord(i)->IsDirty()) || (!dirtyOnly &&other->GetStateDataRecord(i)->IsUsed()) ) fDataRecList[i]->UpdateFrom(*other->GetStateDataRecord(i), writeOptions); } } // // Convert all my stateDataRecords to the type defined by the 'other var' // void plSDStateVariable::ConvertTo(plSDStateVariable* otherSDVar, bool force ) { plStateDescriptor* otherSD=otherSDVar->GetSDVarDescriptor()->GetStateDescriptor(); hsLogEntry( plNetApp::StaticDebugMsg( "SDSV(%p) converting %s from %s to %s (force:%d)", this, fVarDescriptor->GetName(), fVarDescriptor->GetTypeString(), otherSDVar->GetSDVarDescriptor()->GetTypeString(), force ) ); int j; for(j=0;jConvertTo( otherSD, force ); } } bool plSDStateVariable::IsDirty() const { if (plStateVariable::IsDirty()) return true; int j; for(j=0;jIsDirty()) return true; return false; } int plSDStateVariable::GetDirtyCount() const { int cnt=0; int j; for(j=0;jIsDirty()) cnt++; return cnt; } bool plSDStateVariable::IsUsed() const { if (plStateVariable::IsUsed()) return true; int j; for(j=0;jIsUsed()) return true; return false; } int plSDStateVariable::GetUsedCount() const { int cnt=0; int j; for(j=0;jIsUsed()) cnt++; return cnt; } void plSDStateVariable::GetUsedDataRecords(ConstDataRecList* recList) const { recList->clear(); int j; for(j=0;jIsUsed()) recList->push_back(GetStateDataRecord(j)); } void plSDStateVariable::GetDirtyDataRecords(ConstDataRecList* recList) const { recList->clear(); int j; for(j=0;jIsDirty()) recList->push_back(GetStateDataRecord(j)); } // // read all SDVars // bool plSDStateVariable::ReadData(hsStream* s, float timeConvert, UInt32 readOptions) { plStateVariable::ReadData(s, timeConvert, readOptions); UInt8 saveFlags; s->ReadSwap(&saveFlags); // unused // read total list size if (GetVarDescriptor()->IsVariableLength()) { UInt32 total; s->ReadSwap(&total); Resize(total); } // read dirty list size int cnt; plSDL::VariableLengthRead(s, GetVarDescriptor()->IsVariableLength() ? 0xffffffff : GetVarDescriptor()->GetCount(), &cnt); // if we are reading the entire list in, then we don't need to read each index bool all = (cnt==fDataRecList.size()); // read list int i; for(i=0;iIsVariableLength() ? 0xffffffff : GetVarDescriptor()->GetCount(), &idx); else idx=i; if (idxRead(s, timeConvert, readOptions); else return false; } return true; } // // write all SDVars // bool plSDStateVariable::WriteData(hsStream* s, float timeConvert, UInt32 writeOptions) const { plStateVariable::WriteData(s, timeConvert, writeOptions); UInt8 saveFlags=0; // unused s->WriteSwap(saveFlags); // write total list size UInt32 total=GetCount(); if (GetVarDescriptor()->IsVariableLength()) s->WriteSwap(total); // write dirty list size bool dirtyOnly = (writeOptions & plSDL::kDirtyOnly) != 0; int writeCnt = dirtyOnly ? GetDirtyCount() : GetUsedCount(); plSDL::VariableLengthWrite(s, GetVarDescriptor()->IsVariableLength() ? 0xffffffff : GetVarDescriptor()->GetCount(), writeCnt); // if we are writing the entire list in, then we don't need to read each index bool all = (writeCnt==fDataRecList.size()); // write list int i, written=0; for(i=0;iIsDirty()) || (!dirtyOnly && fDataRecList[i]->IsUsed()) ) { if (!all) plSDL::VariableLengthWrite(s, GetVarDescriptor()->IsVariableLength() ? 0xffffffff : GetVarDescriptor()->GetCount(), i); // idx fDataRecList[i]->Write(s, timeConvert, dirtyOnly); // item written++; } } hsAssert(writeCnt==written, "write mismatch"); return true; } // // // void plSDStateVariable::DumpToObjectDebugger(bool dirtyOnly, int level) const { plNetObjectDebuggerBase* dbg = plNetObjectDebuggerBase::GetInstance(); if (!dbg) return; std::string pad; int i; for(i=0;iLogMsg(xtl::format( "%sSDVar, name:%s dirtyOnly:%d count:%d", pad.c_str(), GetName(), dirtyOnly, cnt).c_str()); for(i=0;iIsDirty()) || (!dirtyOnly && fDataRecList[i]->IsUsed()) ) { fDataRecList[i]->DumpToObjectDebugger(nil, dirtyOnly, level+1); } } } void plSDStateVariable::DumpToStream(hsStream* stream, bool dirtyOnly, int level) const { std::string pad; int i; for(i=0;iWriteString(xtl::format( "%sSDVar, name:%s dirtyOnly:%d count:%d", pad.c_str(), GetName(), dirtyOnly, cnt).c_str()); for(i=0;iIsDirty()) || (!dirtyOnly && fDataRecList[i]->IsUsed()) ) { fDataRecList[i]->DumpToStream(stream, nil, dirtyOnly, level+1); } } } // // Checks to see if data contents are the same on two matching vars. // bool plSDStateVariable::operator==(const plSDStateVariable &other) const { hsAssert(GetSDVarDescriptor()->GetStateDescriptor() == other.GetSDVarDescriptor()->GetStateDescriptor(), "SD var descriptor mismatch in equality check"); if (GetCount() != other.GetCount()) return false; // different list sizes int i; for(i=0;iSetFromDefaults(timeStampNow); } void plSDStateVariable::TimeStamp( const plUnifiedTime & ut/*=plUnifiedTime::GetCurrentTime()*/ ) { hsAssert( false, "not impl" ); } void plSDStateVariable::FlagNewerState(const plSDStateVariable& other, bool respectAlwaysNew) { int i; for(i=0;iFlagNewerState(*other.GetStateDataRecord(i), respectAlwaysNew); } void plSDStateVariable::FlagAlwaysNewState() { int i; for(i=0;iFlagAlwaysNewState(); }