/*==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/>. 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 <float.h> #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;i<cnt; i++) delete fC[i]; // delete creatable array delete [] fC; } } break; default: hsAssert(false, xtl::format("undefined atomic type:%d var:%s cnt:%d", type, GetName() ? GetName() : "?", GetCount()).c_str()); break; }; } // // alloc memory // #define SDLALLOC(typeName, type, var) \ case typeName: \ var = TRACKED_NEW type[cnt]; \ break; void plSimpleStateVariable::Alloc(int listSize) { if (listSize != -1) fVar.SetCount(listSize); IDeAlloc(); IInit(); int cnt = fVar.GetAtomicCount()*fVar.GetCount(); if (cnt) { switch (fVar.GetAtomicType()) { SDLALLOC(plVarDescriptor::kInt, int, fI) SDLALLOC(plVarDescriptor::kAgeTimeOfDay, float, fF) SDLALLOC(plVarDescriptor::kByte, byte, fBy) SDLALLOC(plVarDescriptor::kShort, short, fS) SDLALLOC(plVarDescriptor::kFloat, float, fF) SDLALLOC(plVarDescriptor::kDouble, double, fD) SDLALLOC(plVarDescriptor::kBool, bool, fB) SDLALLOC(plVarDescriptor::kCreatable, plCreatable*, fC) case plVarDescriptor::kTime: fT = TRACKED_NEW plClientUnifiedTime[cnt]; break; case plVarDescriptor::kKey: fU = TRACKED_NEW plUoid[cnt]; break; case plVarDescriptor::kString32: fS32 = TRACKED_NEW plVarDescriptor::String32[cnt]; break; default: hsAssert(false, "undefined atomic type"); break; }; } Reset(); } #define RESET(typeName, type, var) \ case typeName: \ for(i=0;i<cnt;i++) \ var[i]=0; \ break; void plSimpleStateVariable::Reset() { int i, cnt = fVar.GetAtomicCount()*fVar.GetCount(); if (cnt) { switch (fVar.GetAtomicType()) { RESET(plVarDescriptor::kInt, int, fI) RESET(plVarDescriptor::kAgeTimeOfDay, float, fF) RESET(plVarDescriptor::kByte, byte, fBy) RESET(plVarDescriptor::kShort, short, fS) RESET(plVarDescriptor::kFloat, float, fF) RESET(plVarDescriptor::kDouble, double, fD) RESET(plVarDescriptor::kBool, bool, fB) RESET(plVarDescriptor::kCreatable, plCreatable*, fC) case plVarDescriptor::kTime: break; case plVarDescriptor::kKey: break; case plVarDescriptor::kString32: for(i=0;i<cnt;i++) *fS32[i]=0; break; default: hsAssert(false, "undefined atomic type"); break; }; } } // // Copy the descriptor settings and allocate list // void plSimpleStateVariable::CopyFrom(plVarDescriptor* v) { if (v) { plSimpleVarDescriptor* simV=(plSimpleVarDescriptor*)v; if (simV) fVar.CopyFrom(simV); else fVar.CopyFrom(v); // copy base class Alloc(); } } void plSimpleStateVariable::TimeStamp( const plUnifiedTime & ut/*=plUnifiedTime::GetCurrentTime()*/ ) { fTimeStamp = ut; } // // Set value from string. Used to set default values which are specified as strings. // bool plSimpleStateVariable::SetFromString(const char* valueConst, int idx, bool timeStampNow) { if (!valueConst) return false; std::string value = valueConst; plVarDescriptor::Type type=fVar.GetAtomicType(); switch(type) { case plVarDescriptor::kAgeTimeOfDay: case plVarDescriptor::kTime: case plVarDescriptor::kDouble: case plVarDescriptor::kFloat: case plVarDescriptor::kInt: case plVarDescriptor::kShort: case plVarDescriptor::kByte: { // handles value in the form "(i,j,k)" for vectors static char seps[] = "( ,)"; char* ptr = strtok( (char*)value.c_str(), seps ); int i=idx*fVar.GetAtomicCount(); while (ptr) { if ((type==plVarDescriptor::kInt) && fI) fI[i++] = atoi(ptr); else if (type==plVarDescriptor::kShort && fS) fS[i++] = (short)atoi(ptr); else if (type==plVarDescriptor::kByte && fBy) fBy[i++] = (byte)atoi(ptr); else if ( (type==plVarDescriptor::kFloat || type==plVarDescriptor::kAgeTimeOfDay) && fF) fF[i++] = (float)atof(ptr); else if ( (type==plVarDescriptor::kDouble || type==plVarDescriptor::kTime) && fD) fD[i++] = atof(ptr); ptr = strtok(nil, seps); } } break; case plVarDescriptor::kBool: { // handles value in the form "(i,j,k)" for things like vectors static char seps[] = "( ,)"; char* ptr = strtok( (char*)value.c_str(), seps ); int i=idx*fVar.GetAtomicCount(); while (ptr) { if (!stricmp(ptr, "true")) fB[i++]=true; else if (!stricmp(ptr, "false")) fB[i++]=false; else fB[i++] = (atoi(ptr) != 0); ptr = strtok(nil, seps); } } break; case plVarDescriptor::kString32: { // handles value in the form "(i,j,k)" for things like vectors static char seps[] = "( ,)"; char* ptr = strtok( (char*)value.c_str(), seps ); int i=idx*fVar.GetAtomicCount(); while (ptr) { hsStrncpy(fS32[i++], ptr, 32); ptr = strtok(nil, seps); } } break; default: return false; // err } IVarSet(timeStampNow); return true; // ok } // // Called when a var has been set with a value // void plSimpleStateVariable::IVarSet(bool timeStampNow/*=true*/) { if (timeStampNow) TimeStamp(); SetDirty(true); SetUsed(true); } // // Get value as string. // char* plSimpleStateVariable::GetAsString(int idx) const { int j; std::string str; if (fVar.GetAtomicCount()>1) 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;j<fVar.GetAtomicCount();j++) { if (type==plVarDescriptor::kInt) str.append( xtl::format( "%d", fI[i++]) ); else if (type==plVarDescriptor::kShort) str.append( xtl::format( "%d", fS[i++]) ); else if (type==plVarDescriptor::kByte) str.append( xtl::format( "%d", fBy[i++]) ); else if (type==plVarDescriptor::kFloat || type==plVarDescriptor::kAgeTimeOfDay) str.append( xtl::format( "%.3f", fF[i++]) ); else if (type==plVarDescriptor::kDouble) str.append( xtl::format( "%.3f", fD[i++]) ); else if (type==plVarDescriptor::kTime) { double tmp; Get(&tmp, i++); str.append( xtl::format( "%.3f", tmp) ); } if (j==fVar.GetAtomicCount()-1) { if (j) str += ")"; } else str += ","; } } break; case plVarDescriptor::kBool: { // handles value in the form "(i,j,k)" for things like vectors int i=idx*fVar.GetAtomicCount(); for(j=0;j<fVar.GetAtomicCount();j++) { str.append( xtl::format( "%s", fB[i++] ? "true" : "false") ); if (j==fVar.GetAtomicCount()-1) { if (j) str += ")"; } else str += ","; } } break; case plVarDescriptor::kString32: { // handles value in the form "(i,j,k)" for things like vectors int i=idx*fVar.GetAtomicCount(); for(j=0;j<fVar.GetAtomicCount();j++) { str.append( xtl::format( "%s", fS32[i++]) ); if (j==fVar.GetAtomicCount()-1) { if (j) str += ")"; } else str += ","; } } break; default: { // handles value in the form "(i,j,k)" for things like vectors int i=idx*fVar.GetAtomicCount(); for(j=0;j<fVar.GetAtomicCount();j++) { str.append( xtl::format( "%s", "other") ); if (j==fVar.GetAtomicCount()-1) { if (j) str += ")"; } else str += ","; } } break; } return hsStrcpy(str.c_str()); } // // return false on err // bool plSimpleStateVariable::IConvertFromRGB(plVarDescriptor::Type newType) { switch(newType) { case plVarDescriptor::kRGBA: { // rgb to rgba int i,j; float* newF = TRACKED_NEW float[fVar.GetCount()*4]; // make more space for(j=0;j<fVar.GetCount(); j++) { // recopy with alpha=0 by default for(i=0;i<3;i++) newF[j*4+i] = fF[j*fVar.GetAtomicCount()+i]; newF[j*4+3] = 0; } delete [] fF; // delete old fF = newF; // use new } break; case plVarDescriptor::kRGBA8: { // rgb to rgba8 int i,j; byte * newB = TRACKED_NEW byte [fVar.GetCount()*4]; // make more space for(j=0;j<fVar.GetCount(); j++) { // recopy with alpha=0 by default for(i=0;i<3;i++) newB[j*4+i] = byte(fF[j*fVar.GetAtomicCount()+i]*255+.5); newB[j*4+3] = 0; } delete [] fF; // delete old fBy = newB; // use new } break; case plVarDescriptor::kRGB8: { // rgb to rgb8 int i,j; byte * newB = TRACKED_NEW byte [fVar.GetCount()*3]; // make space for(j=0;j<fVar.GetCount(); j++) { // recopy with alpha=0 by default for(i=0;i<3;i++) newB[j*3+i] = byte(fF[j*fVar.GetAtomicCount()+i]*255+.5); } delete [] fF; // delete old fBy = newB; // use new } break; default: return false; // err } return true; } bool plSimpleStateVariable::IConvertFromRGB8(plVarDescriptor::Type newType) { switch(newType) { case plVarDescriptor::kRGBA: { // rgb8 to rgba int i,j; float* newF = TRACKED_NEW float[fVar.GetCount()*4]; // make more space for(j=0;j<fVar.GetCount(); j++) { // recopy with alpha=0 by default for(i=0;i<3;i++) newF[j*4+i] = fBy[j*fVar.GetAtomicCount()+i]/255.f; newF[j*4+3] = 0; } delete [] fBy; // delete old fF = newF; // use new } break; case plVarDescriptor::kRGB: { // rgb8 to rgb int i,j; float* newF = TRACKED_NEW float[fVar.GetCount()*3]; // make more space for(j=0;j<fVar.GetCount(); j++) { // recopy with alpha=0 by default for(i=0;i<3;i++) newF[j*3+i] = fBy[j*fVar.GetAtomicCount()+i]/255.f; } delete [] fBy; // delete old fF = newF; // use new } break; case plVarDescriptor::kRGBA8: { // rgb8 to rgba8 int i,j; byte * newB = TRACKED_NEW byte [fVar.GetCount()*4]; // make more space for(j=0;j<fVar.GetCount(); j++) { // recopy with alpha=0 by default for(i=0;i<3;i++) newB[j*4+i] = fBy[j*fVar.GetAtomicCount()+i]; newB[j*4+3] = 0; } delete [] fBy; // delete old fBy = newB; // use new } break; default: return false; // err } return true; } // // return false on err // bool plSimpleStateVariable::IConvertFromRGBA(plVarDescriptor::Type newType) { switch(newType) { case plVarDescriptor::kRGB: { // rgba to rgb int i,j; float* newF = TRACKED_NEW float[fVar.GetCount()*3]; // make less space for(j=0;j<fVar.GetCount(); j++) { // recopy and ignore alpha for(i=0;i<3;i++) newF[j*3+i] = fF[j*fVar.GetAtomicCount()+i]; } delete [] fF; // delete old fF = newF; // use new } break; case plVarDescriptor::kRGB8: { // rgba to rgb8 int i,j; byte* newB = TRACKED_NEW byte[fVar.GetCount()*3]; // make less space for(j=0;j<fVar.GetCount(); j++) { // recopy and ignore alpha for(i=0;i<3;i++) newB[j*3+i] = byte(fF[j*fVar.GetAtomicCount()+i]*255+.5); } delete [] fF; // delete old fBy = newB; // use new } break; case plVarDescriptor::kRGBA8: { // rgba to rgba8 int i,j; byte* newBy = TRACKED_NEW byte [fVar.GetCount()*4]; // make less space for(j=0;j<fVar.GetCount(); j++) { // recopy and ignore alpha for(i=0;i<4;i++) newBy[j*4+i] = byte(fF[j*fVar.GetAtomicCount()+i]*255+.5); } delete [] fF; // delete old fBy = newBy; // use new } break; default: return false; // err } return true; } // // return false on err // bool plSimpleStateVariable::IConvertFromRGBA8(plVarDescriptor::Type newType) { switch(newType) { case plVarDescriptor::kRGB: { // rgba8 to rgb int i,j; float* newF = TRACKED_NEW float[fVar.GetCount()*3]; // make less space for(j=0;j<fVar.GetCount(); j++) { // recopy and ignore alpha for(i=0;i<3;i++) newF[j*3+i] = fBy[j*fVar.GetAtomicCount()+i]/255.f; } delete [] fBy; // delete old fF = newF; // use new } break; case plVarDescriptor::kRGB8: { // rgba8 to rgb8 int i,j; byte* newB = TRACKED_NEW byte[fVar.GetCount()*3]; // make less space for(j=0;j<fVar.GetCount(); j++) { // recopy and ignore alpha for(i=0;i<3;i++) newB[j*3+i] = fBy[j*fVar.GetAtomicCount()+i]; } delete [] fBy; // delete old fBy = newB; // use new } break; case plVarDescriptor::kRGBA: { // rgba8 to rgba int i,j; float* newF = TRACKED_NEW float[fVar.GetCount()*4]; // make less space for(j=0;j<fVar.GetCount(); j++) { // recopy and ignore alpha for(i=0;i<4;i++) newF[j*4+i] = fBy[j*fVar.GetAtomicCount()+i]/255.f; } delete [] fBy; // delete old fF = newF; // use new } break; default: return false; // err } return true; } // // return false on err // bool plSimpleStateVariable::IConvertFromInt(plVarDescriptor::Type newType) { int j; switch(newType) { case plVarDescriptor::kFloat: { // int to float float* newF = TRACKED_NEW float[fVar.GetCount()]; for(j=0;j<fVar.GetCount(); j++) newF[j] = (float)(fI[j]); delete [] fI; fF = newF; } break; case plVarDescriptor::kShort: { // int to short short* newS = TRACKED_NEW short[fVar.GetCount()]; for(j=0;j<fVar.GetCount(); j++) newS[j] = short(fI[j]); delete [] fI; fS = newS; } break; case plVarDescriptor::kByte: { // int to byte byte* newBy = TRACKED_NEW byte[fVar.GetCount()]; for(j=0;j<fVar.GetCount(); j++) newBy[j] = byte(fI[j]); delete [] fI; fBy = newBy; } break; case plVarDescriptor::kDouble: { // int to double double * newD = TRACKED_NEW double[fVar.GetCount()]; for(j=0;j<fVar.GetCount(); j++) newD[j] = fI[j]; delete [] fI; fD = newD; } break; case plVarDescriptor::kBool: { // int to bool bool * newB = TRACKED_NEW bool[fVar.GetCount()]; for(j=0;j<fVar.GetCount(); j++) newB[j] = (fI[j]!=0); delete [] fI; fB = newB; } break; default: return false; // err } return true; } // // return false on err // bool plSimpleStateVariable::IConvertFromShort(plVarDescriptor::Type newType) { int j; switch(newType) { case plVarDescriptor::kFloat: { float* newF = TRACKED_NEW float[fVar.GetCount()]; for(j=0;j<fVar.GetCount(); j++) newF[j] = fS[j]; delete [] fS; fF = newF; } break; case plVarDescriptor::kInt: { int* newI = TRACKED_NEW int[fVar.GetCount()]; for(j=0;j<fVar.GetCount(); j++) newI[j] = short(fS[j]); delete [] fS; fI = newI; } case plVarDescriptor::kByte: { byte* newBy = TRACKED_NEW byte[fVar.GetCount()]; for(j=0;j<fVar.GetCount(); j++) newBy[j] = byte(fS[j]); delete [] fS; fBy = newBy; } break; case plVarDescriptor::kDouble: { double * newD = TRACKED_NEW double[fVar.GetCount()]; for(j=0;j<fVar.GetCount(); j++) newD[j] = fS[j]; delete [] fS; fD = newD; } break; case plVarDescriptor::kBool: { bool * newB = TRACKED_NEW bool[fVar.GetCount()]; for(j=0;j<fVar.GetCount(); j++) newB[j] = (fS[j]!=0); delete [] fS; fB = newB; } break; default: return false; // err } return true; } // // return false on err // bool plSimpleStateVariable::IConvertFromByte(plVarDescriptor::Type newType) { int j; switch(newType) { case plVarDescriptor::kFloat: { float* newF = TRACKED_NEW float[fVar.GetCount()]; for(j=0;j<fVar.GetCount(); j++) newF[j] = fBy[j]; delete [] fBy; fF = newF; } break; case plVarDescriptor::kInt: { int* newI = TRACKED_NEW int[fVar.GetCount()]; for(j=0;j<fVar.GetCount(); j++) newI[j] = short(fBy[j]); delete [] fBy; fI = newI; } case plVarDescriptor::kShort: { short* newS = TRACKED_NEW short[fVar.GetCount()]; for(j=0;j<fVar.GetCount(); j++) newS[j] = fBy[j]; delete [] fBy; fS = newS; } break; case plVarDescriptor::kDouble: { double * newD = TRACKED_NEW double[fVar.GetCount()]; for(j=0;j<fVar.GetCount(); j++) newD[j] = fBy[j]; delete [] fBy; fD = newD; } break; case plVarDescriptor::kBool: { bool * newB = TRACKED_NEW bool[fVar.GetCount()]; for(j=0;j<fVar.GetCount(); j++) newB[j] = (fBy[j]!=0); delete [] fBy; fB = newB; } break; default: return false; // err } return true; } // // return false on err // bool plSimpleStateVariable::IConvertFromFloat(plVarDescriptor::Type newType) { int j; switch(newType) { case plVarDescriptor::kInt: { int* newI = TRACKED_NEW int[fVar.GetCount()]; for(j=0;j<fVar.GetCount(); j++) newI[j] = (int)(fF[j]+.5f); // round to nearest int delete [] fF; fI = newI; } break; case plVarDescriptor::kShort: { short* newS = TRACKED_NEW short[fVar.GetCount()]; for(j=0;j<fVar.GetCount(); j++) newS[j] = (short)(fF[j]+.5f); // round to nearest int delete [] fF; fS = newS; } break; case plVarDescriptor::kByte: { byte* newBy = TRACKED_NEW byte[fVar.GetCount()]; for(j=0;j<fVar.GetCount(); j++) newBy[j] = (byte)(fF[j]+.5f); // round to nearest int delete [] fF; fBy = newBy; } break; case plVarDescriptor::kDouble: { double* newD = TRACKED_NEW double[fVar.GetCount()]; for(j=0;j<fVar.GetCount(); j++) newD[j] = fF[j]; delete [] fF; fD = newD; } break; case plVarDescriptor::kBool: { bool* newB = TRACKED_NEW bool[fVar.GetCount()]; for(j=0;j<fVar.GetCount(); j++) newB[j] = (fF[j]!=0); delete [] fF; fB = newB; } break; default: return false; // err } return true; } // // return false on err // bool plSimpleStateVariable::IConvertFromDouble(plVarDescriptor::Type newType) { int j; switch(newType) { case plVarDescriptor::kInt: { int* newI = TRACKED_NEW int[fVar.GetCount()]; for(j=0;j<fVar.GetCount(); j++) newI[j] = (int)(fD[j]+.5f); // round to nearest int delete [] fD; fI = newI; } break; case plVarDescriptor::kShort: { short* newS = TRACKED_NEW short[fVar.GetCount()]; for(j=0;j<fVar.GetCount(); j++) newS[j] = (short)(fD[j]+.5f); // round to nearest int delete [] fD; fS = newS; } break; case plVarDescriptor::kByte: { byte* newBy = TRACKED_NEW byte[fVar.GetCount()]; for(j=0;j<fVar.GetCount(); j++) newBy[j] = (byte)(fD[j]+.5f); // round to nearest int delete [] fD; fBy = newBy; } break; case plVarDescriptor::kFloat: { float* newF = TRACKED_NEW float[fVar.GetCount()]; for(j=0;j<fVar.GetCount(); j++) newF[j] = (float)(fD[j]); delete [] fD; fF = newF; } break; case plVarDescriptor::kBool: { bool* newB = TRACKED_NEW bool[fVar.GetCount()]; for(j=0;j<fVar.GetCount(); j++) newB[j] = (fD[j]!=0); delete [] fD; fB = newB; } break; default: return false; // err } return true; } // // return false on err // bool plSimpleStateVariable::IConvertFromBool(plVarDescriptor::Type newType) { int j; switch(newType) { case plVarDescriptor::kInt: { int* newI = TRACKED_NEW int[fVar.GetCount()]; for(j=0;j<fVar.GetCount(); j++) newI[j] = (fB[j] == true ? 1 : 0); delete [] fB; fI = newI; } break; case plVarDescriptor::kShort: { short* newS = TRACKED_NEW short[fVar.GetCount()]; for(j=0;j<fVar.GetCount(); j++) newS[j] = (fB[j] == true ? 1 : 0); delete [] fB; fS = newS; } break; case plVarDescriptor::kByte: { byte* newBy = TRACKED_NEW byte[fVar.GetCount()]; for(j=0;j<fVar.GetCount(); j++) newBy[j] = (fB[j] == true ? 1 : 0); delete [] fB; fBy = newBy; } break; case plVarDescriptor::kFloat: { float* newF = TRACKED_NEW float[fVar.GetCount()]; for(j=0;j<fVar.GetCount(); j++) newF[j] = (fB[j] == true ? 1.f : 0.f); delete [] fB; fF = newF; } break; case plVarDescriptor::kDouble: { double* newD= TRACKED_NEW double[fVar.GetCount()]; for(j=0;j<fVar.GetCount(); j++) newD[j] = (fB[j] == true ? 1.f : 0.f); delete [] fB; fD = newD; } break; default: return false; // err } return true; } // // return false on err // bool plSimpleStateVariable::IConvertFromString(plVarDescriptor::Type newType) { int j; switch(newType) { case plVarDescriptor::kBool: // string to bool for(j=0;j<fVar.GetCount(); j++) { if (!stricmp(fS32[j], "true") || !stricmp(fS32[j], "1")) fB[j]=true; else if (!stricmp(fS32[j], "false") || !stricmp(fS32[j], "0")) fB[j]=false; else return false; // err } break; case plVarDescriptor::kInt: // string to int for(j=0;j<fVar.GetCount(); j++) { fI[j] = atoi(fS32[j]); } break; case plVarDescriptor::kFloat: // string to float for(j=0;j<fVar.GetCount(); j++) { fF[j] = (float) atof(fS32[j]); } break; default: return false; // err } return true; } // // Try to convert my value to another type. // return false on err. // called when a data record is read in // bool plSimpleStateVariable::ConvertTo(plSimpleVarDescriptor* toVar, bool force ) { // NOTE: 'force' has no meaning here really, so it is not inforced. plVarDescriptor::Type newType = toVar->GetType(); 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;i<fVar.GetAtomicCount();i++) fF[cnt+i]=v[i]; IVarSet(); return true; } hsAssert(false, "passing wrong value type to SDL variable"); return false; } // bytevector bool plSimpleStateVariable::Set(byte* v, int idx) { VALIDATE_WITH_FALSE_RETURN(idx < fVar.GetCount()); if (fVar.GetAtomicType()==plVarDescriptor::kByte) { int i; int cnt=fVar.GetAtomicCount()*idx; for(i=0;i<fVar.GetAtomicCount();i++) fBy[cnt+i]=v[i]; 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.GetAtomicType()==plVarDescriptor::kDouble) { int i; int cnt=fVar.GetAtomicCount()*idx; for(i=0;i<fVar.GetAtomicCount();i++) fD[cnt+i]=v[i]; IVarSet(); return true; } if (fVar.GetAtomicType()==plVarDescriptor::kTime) { double secs=hsTimer::GetSysSeconds(); int i; int cnt=fVar.GetAtomicCount()*idx; for(i=0;i<fVar.GetAtomicCount();i++) fT[cnt+i].SetFromGameTime(v[i], secs); IVarSet(); return true; } hsAssert(false, "passing wrong value type to SDL variable"); return false; } bool plSimpleStateVariable::Set(int v, int idx) { VALIDATE_WITH_FALSE_RETURN(idx < fVar.GetCount()); if (fVar.GetType()==plVarDescriptor::kInt) { fI[idx]=v; IVarSet(); return true; } else if (fVar.GetType()==plVarDescriptor::kBool) return Set((bool)(v?true:false), idx); // since 'true' is coming in as an int not bool else if (fVar.GetType()==plVarDescriptor::kShort) return Set((short)v, idx); else if (fVar.GetType()==plVarDescriptor::kByte) return Set((byte)v, idx); hsAssert(false, "passing wrong value type to SDL variable"); return false; } bool plSimpleStateVariable::Set(short v, int idx) { VALIDATE_WITH_FALSE_RETURN(idx < fVar.GetCount()); if (fVar.GetType()==plVarDescriptor::kShort) { fS[idx]=v; IVarSet(); return true; } else if (fVar.GetType()==plVarDescriptor::kInt) return Set((int)v, idx); else if (fVar.GetType()==plVarDescriptor::kByte) return Set((byte)v, idx); hsAssert(false, "passing wrong value type to SDL variable"); return false; } bool plSimpleStateVariable::Set(byte v, int idx) { VALIDATE_WITH_FALSE_RETURN(idx < fVar.GetCount()); if (fVar.GetType()==plVarDescriptor::kByte) { fBy[idx]=v; IVarSet(); return true; } else if (fVar.GetType()==plVarDescriptor::kBool) return Set((bool)(v?true:false), idx); else if (fVar.GetType()==plVarDescriptor::kInt) return Set((int)v, idx); else if (fVar.GetType()==plVarDescriptor::kShort) return Set((short)v, idx); hsAssert(false, "passing wrong value type to SDL variable"); return false; } bool plSimpleStateVariable::Set(const char* v, int idx) { VALIDATE_WITH_FALSE_RETURN(idx < fVar.GetCount()); if (v && fVar.GetType()==plVarDescriptor::kString32) { hsAssert(hsStrlen(v)<32, "string length overflow"); hsStrncpy(fS32[idx], v, 32); IVarSet(); return true; } hsAssert(false, v ? "passing wrong value type to SDL variable" : "trying to set nil string"); return false; } bool plSimpleStateVariable::Set(bool v, int idx) { VALIDATE_WITH_FALSE_RETURN(idx < fVar.GetCount()); if (fVar.GetType()==plVarDescriptor::kBool) { fB[idx]=v; IVarSet(); return true; } hsAssert(false, "passing wrong value type to SDL variable"); return false; } bool plSimpleStateVariable::Set(const plKey& v, int idx) { VALIDATE_WITH_FALSE_RETURN(idx < fVar.GetCount()); if (fVar.GetType()==plVarDescriptor::kKey) { if(v) { fU[idx] = v->GetUoid(); } 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;i<fVar.GetAtomicCount();i++) value[i]=fI[cnt+i]; return true; } if (fVar.GetAtomicType()==plVarDescriptor::kShort) { int i; int cnt=fVar.GetAtomicCount()*idx; for(i=0;i<fVar.GetAtomicCount();i++) value[i]=fS[cnt+i]; return true; } if (fVar.GetAtomicType()==plVarDescriptor::kByte) { int i; int cnt=fVar.GetAtomicCount()*idx; for(i=0;i<fVar.GetAtomicCount();i++) value[i]=fBy[cnt+i]; return true; } hsAssert(false, "passing wrong value type to SDL variable"); return false; } bool plSimpleStateVariable::Get(short* value, int idx) const { VALIDATE_WITH_FALSE_RETURN(idx < fVar.GetCount()); if (fVar.GetAtomicType()==plVarDescriptor::kShort) { int i; int cnt=fVar.GetAtomicCount()*idx; for(i=0;i<fVar.GetAtomicCount();i++) value[i]=fS[cnt+i]; return true; } hsAssert(false, "passing wrong value type to SDL variable"); return false; } bool plSimpleStateVariable::Get(byte* value, int idx) const { VALIDATE_WITH_FALSE_RETURN(idx < fVar.GetCount()); if (fVar.GetAtomicType()==plVarDescriptor::kByte) { int i; int cnt=fVar.GetAtomicCount()*idx; for(i=0;i<fVar.GetAtomicCount();i++) value[i]=fBy[cnt+i]; return true; } hsAssert(false, "passing wrong value type to SDL variable"); return false; } // float or floatVector bool plSimpleStateVariable::Get(float* value, int idx) const { VALIDATE_WITH_FALSE_RETURN(idx < fVar.GetCount()); if (fVar.GetAtomicType()==plVarDescriptor::kAgeTimeOfDay) { int i; int cnt=fVar.GetAtomicCount()*idx; for(i=0;i<fVar.GetAtomicCount();i++) { if (plNetClientApp::GetInstance()) fF[cnt+i] = plNetClientApp::GetInstance()->GetCurrentAgeTimeOfDayPercent(); value[i]=fF[cnt+i]; } return true; } if (fVar.GetAtomicType()==plVarDescriptor::kFloat) { int i; int cnt=fVar.GetAtomicCount()*idx; for(i=0;i<fVar.GetAtomicCount();i++) value[i]=fF[cnt+i]; return true; } if (fVar.GetAtomicType()==plVarDescriptor::kDouble) { int i; int cnt=fVar.GetAtomicCount()*idx; for(i=0;i<fVar.GetAtomicCount();i++) value[i]=(float)fD[cnt+i]; return true; } if (fVar.GetAtomicType()==plVarDescriptor::kTime) // && fIsUsed) { double secs=hsTimer::GetSysSeconds(); int i; int cnt=fVar.GetAtomicCount()*idx; for(i=0;i<fVar.GetAtomicCount();i++) { double tmp; fT[cnt+i].ConvertToGameTime(&tmp, secs); value[i] = (float)tmp; } return true; } hsAssert(false, "passing wrong value type to SDL variable"); return false; } // double bool plSimpleStateVariable::Get(double* value, int idx) const { VALIDATE_WITH_FALSE_RETURN(idx < fVar.GetCount()); if (fVar.GetAtomicType()==plVarDescriptor::kDouble) { int i; int cnt=fVar.GetAtomicCount()*idx; for(i=0;i<fVar.GetAtomicCount();i++) value[i]=fD[cnt+i]; return true; } if (fVar.GetAtomicType()==plVarDescriptor::kTime) { double secs=hsTimer::GetSysSeconds(); int i; int cnt=fVar.GetAtomicCount()*idx; for(i=0;i<fVar.GetAtomicCount();i++) fT[cnt+i].ConvertToGameTime(&value[i], secs); return true; } hsAssert(false, "passing wrong value type to SDL variable"); return false; } bool plSimpleStateVariable::Get(bool* value, int idx) const { VALIDATE_WITH_FALSE_RETURN(idx < fVar.GetCount()); if (fVar.GetAtomicType()==plVarDescriptor::kBool) { int i; int cnt=fVar.GetAtomicCount()*idx; for(i=0;i<fVar.GetAtomicCount();i++) value[i]=fB[cnt+i]; return true; } hsAssert(false, "passing wrong value type to SDL variable"); return false; } bool plSimpleStateVariable::Get(plKey* value, int idx) const { VALIDATE_WITH_FALSE_RETURN(idx < fVar.GetCount()); if (fVar.GetAtomicType()==plVarDescriptor::kKey) { if(!(fU[idx] == plUoid())) // compare to default "nil uoid" { *value = hsgResMgr::ResMgr()->FindKey(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<plKey> 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;i<fVar.GetAtomicCount();i++) s->WriteSwap32(fI[j+i]); break; case plVarDescriptor::kShort: for(i=0;i<fVar.GetAtomicCount();i++) s->WriteSwap16(fS[j+i]); break; case plVarDescriptor::kByte: for(i=0;i<fVar.GetAtomicCount();i++) s->WriteByte(fBy[j+i]); break; case plVarDescriptor::kFloat: for(i=0;i<fVar.GetAtomicCount();i++) s->WriteSwapScalar(fF[j+i]); break; case plVarDescriptor::kTime: for(i=0;i<fVar.GetAtomicCount();i++) { if (timeConvert != 0.0) { double utDouble=fT[j+i].GetSecsDouble(); hsDoublePrecBegin utDouble += timeConvert; hsDoublePrecEnd plUnifiedTime ut(utDouble); ut.Write(s); } else fT[j+i].Write(s); } break; case plVarDescriptor::kDouble: for(i=0;i<fVar.GetAtomicCount();i++) s->WriteSwapDouble(fD[j+i]); break; case plVarDescriptor::kBool: for(i=0;i<fVar.GetAtomicCount();i++) s->Writebool(fB[j+i]); break; case plVarDescriptor::kKey: for(i=0;i<fVar.GetAtomicCount();i++) fU[j+i].Write(s); break; case plVarDescriptor::kString32: for(i=0;i<fVar.GetAtomicCount();i++) s->Write(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;i<fVar.GetAtomicCount();i++) fI[j+i]=s->ReadSwap32(); break; case plVarDescriptor::kShort: for(i=0;i<fVar.GetAtomicCount();i++) fS[j+i]=s->ReadSwap16(); break; case plVarDescriptor::kByte: for(i=0;i<fVar.GetAtomicCount();i++) fBy[j+i]=s->ReadByte(); break; case plVarDescriptor::kFloat: for(i=0;i<fVar.GetAtomicCount();i++) fF[j+i]=s->ReadSwapScalar(); break; case plVarDescriptor::kTime: for(i=0;i<fVar.GetAtomicCount();i++) { fT[j+i].Read(s); if (timeConvert != 0.0) { hsDoublePrecBegin double newUt = (fT[j+i].GetSecsDouble() + timeConvert); hsDoublePrecEnd hsAssert(newUt>=0, "negative unified time"); fT[j+i].SetSecsDouble(newUt); } } break; case plVarDescriptor::kDouble: for(i=0;i<fVar.GetAtomicCount();i++) fD[j+i]=s->ReadSwapDouble(); break; case plVarDescriptor::kBool: for(i=0;i<fVar.GetAtomicCount();i++) fB[j+i]=s->Readbool(); break; case plVarDescriptor::kKey: for(i=0;i<fVar.GetAtomicCount();i++) { fU[j+i].Invalidate(); fU[j+i].Read(s); } break; case plVarDescriptor::kString32: for(i=0;i<fVar.GetAtomicCount();i++) s->Read(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;i<fVar.GetCount();i++) if (!IWriteData(s, timeConvert, i, writeOptions)) return false; } return true; } // assumes var is created from the right type of descriptor (count, type, etc.) bool plSimpleStateVariable::ReadData(hsStream* s, float timeConvert, UInt32 readOptions) { // read base class data plStateVariable::ReadData(s, timeConvert, readOptions); plUnifiedTime ut; ut.ToEpoch(); UInt8 saveFlags; s->ReadSwap(&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<plSDL::kMaxListSize) fVar.SetCount(cnt); else return false; Alloc(); // alloc after setting count } else { hsAssert(fVar.GetCount(), "empty var?"); } } // compare timestamps if (fTimeStamp > ut) return true; if ( (saveFlags & plSDL::kHasTimeStamp) || (readOptions & plSDL::kTimeStampOnRead) ) TimeStamp(ut); // read list if (!(saveFlags & plSDL::kSameAsDefault)) { int i; for(i=0;i<fVar.GetCount();i++) if (!IReadData(s, timeConvert, i, readOptions)) return false; } else { Reset(); SetFromDefaults(false); } SetUsed( true ); SetDirty( setDirty ); return true; } void plSimpleStateVariable::CopyData(const plSimpleStateVariable* other, UInt32 writeOptions/*=0*/) { // use stream as a medium hsRAMStream stream; other->WriteData(&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;i<cnt;i++) \ if (hsABS(var[i] - other->var[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;i<cnt;i++) \ if (var[i]!=other.var[i]) \ return false; \ break; bool plSimpleStateVariable::operator==(const plSimpleStateVariable &other) const { hsAssert(fVar.GetType() == other.GetVarDescriptor()->GetType(), "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;i<cnt;i++) if (stricmp(fS32[i],other.fS32[i])) return false; break; default: hsAssert(false, "invalid atomic type"); return false; break; } return true; } // // Add and coalate // void plSimpleStateVariable::AddStateChangeNotification(plStateChangeNotifier& n) { StateChangeNotifiers::iterator it=fChangeNotifiers.begin(); for( ; it != fChangeNotifiers.end(); it++) { if ((*it).fDelta==n.fDelta) { // merged into an existing entry (*it).AddNotificationKeys(n.fKeys); return; } } // add new entry fChangeNotifiers.push_back(n); } // // remove entries with this key // void plSimpleStateVariable::RemoveStateChangeNotification(plKey notificationObj) { StateChangeNotifiers::iterator it=fChangeNotifiers.end(); for(; it != fChangeNotifiers.begin();) { it--; int size=(*it).RemoveNotificationKey(notificationObj); if (size==0) it=fChangeNotifiers.erase(it); // iterator is moved to item after this one } } // // remove entries which match // void plSimpleStateVariable::RemoveStateChangeNotification(plStateChangeNotifier n) { StateChangeNotifiers::iterator it=fChangeNotifiers.end(); for(; it != fChangeNotifiers.begin();) { it--; if ( (*it).fDelta==n.fDelta) { int size=(*it).RemoveNotificationKeys(n.fKeys); if (size==0) it=fChangeNotifiers.erase(it); // iterator is moved to item after this one } } } // // // void plSimpleStateVariable::DumpToObjectDebugger(bool dirtyOnly, int level) const { plNetObjectDebuggerBase* dbg = plNetObjectDebuggerBase::GetInstance(); if (!dbg) return; std::string pad; int i; for(i=0;i<level; i++) pad += " "; std::string logMsg = xtl::format( "%sSimpleVar, name:%s[%d]", pad.c_str(), GetName(), GetCount()); if (GetCount()>1) { 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;i<GetCount(); i++) { char* s=GetAsString(i); if (fVar.GetAtomicType() == plVarDescriptor::kTime) { const char* p=fT[i].PrintWMillis(); logMsg += xtl::format( "%sVar:%d gameTime:%s pst:%s ts:%s", pad.c_str(), i, s ? s : "?", p, fTimeStamp.Format("%c").c_str() ); } else { logMsg +=xtl::format( "%sVar:%d value:%s ts:%s", pad.c_str(), i, s ? s : "?", fTimeStamp.AtEpoch() ? "0" : fTimeStamp.Format("%c").c_str() ); } delete [] s; if ( !dirtyOnly ) logMsg += xtl::format( " dirty:%d", IsDirty() ); dbg->LogMsg(logMsg.c_str()); logMsg = ""; } } void plSimpleStateVariable::DumpToStream(hsStream* stream, bool dirtyOnly, int level) const { std::string pad; int i; for(i=0;i<level; i++) pad += " "; std::string logMsg = xtl::format( "%sSimpleVar, name:%s[%d]", pad.c_str(), GetName(), GetCount()); if (GetCount()>1) { 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;i<GetCount(); i++) { char* s=GetAsString(i); if (fVar.GetAtomicType() == plVarDescriptor::kTime) { const char* p=fT[i].PrintWMillis(); logMsg += xtl::format( "%sVar:%d gameTime:%s pst:%s ts:%s", pad.c_str(), i, s ? s : "?", p, fTimeStamp.Format("%c").c_str() ); } else { logMsg +=xtl::format( "%sVar:%d value:%s ts:%s", pad.c_str(), i, s ? s : "?", fTimeStamp.AtEpoch() ? "0" : fTimeStamp.Format("%c").c_str() ); } delete [] s; if ( !dirtyOnly ) logMsg += xtl::format( " dirty:%d", IsDirty() ); stream->WriteString(logMsg.c_str()); logMsg = ""; } } // // set var to its defalt value // void plSimpleStateVariable::SetFromDefaults(bool timeStampNow) { int i; for(i=0;i<GetCount();i++) SetFromString(GetVarDescriptor()->GetDefault(), 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 (cnt<origCnt) { int i; for(i=cnt;i<origCnt;i++) delete fDataRecList[i]; } fDataRecList.resize(cnt); // growing if (cnt>origCnt) { int i; for(i=origCnt;i<cnt;i++) fDataRecList[i] = TRACKED_NEW plStateDataRecord(fVarDescriptor->GetStateDescriptor()); } 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;j<cnt; j++) InsertStateDataRecord(TRACKED_NEW plStateDataRecord(sdvd->GetStateDescriptor()), 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; i<other->GetCount(); 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; i<other->GetCount(); 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;j<GetCount(); j++) { GetStateDataRecord(j)->ConvertTo( otherSD, force ); } } bool plSDStateVariable::IsDirty() const { if (plStateVariable::IsDirty()) return true; int j; for(j=0;j<GetCount(); j++) if (GetStateDataRecord(j)->IsDirty()) return true; return false; } int plSDStateVariable::GetDirtyCount() const { int cnt=0; int j; for(j=0;j<GetCount(); j++) if (GetStateDataRecord(j)->IsDirty()) cnt++; return cnt; } bool plSDStateVariable::IsUsed() const { if (plStateVariable::IsUsed()) return true; int j; for(j=0;j<GetCount(); j++) if (GetStateDataRecord(j)->IsUsed()) return true; return false; } int plSDStateVariable::GetUsedCount() const { int cnt=0; int j; for(j=0;j<GetCount(); j++) if (GetStateDataRecord(j)->IsUsed()) cnt++; return cnt; } void plSDStateVariable::GetUsedDataRecords(ConstDataRecList* recList) const { recList->clear(); int j; for(j=0;j<GetCount(); j++) if (GetStateDataRecord(j)->IsUsed()) recList->push_back(GetStateDataRecord(j)); } void plSDStateVariable::GetDirtyDataRecords(ConstDataRecList* recList) const { recList->clear(); int j; for(j=0;j<GetCount(); j++) if (GetStateDataRecord(j)->IsDirty()) 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;i<cnt; i++) { int idx; if (!all) plSDL::VariableLengthRead(s, GetVarDescriptor()->IsVariableLength() ? 0xffffffff : GetVarDescriptor()->GetCount(), &idx); else idx=i; if (idx<fDataRecList.size()) fDataRecList[idx]->Read(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;i<total;i++) { if ( (dirtyOnly && fDataRecList[i]->IsDirty()) || (!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;i<level; i++) pad += " "; int cnt = dirtyOnly ? GetDirtyCount() : GetUsedCount(); dbg->LogMsg(xtl::format( "%sSDVar, name:%s dirtyOnly:%d count:%d", pad.c_str(), GetName(), dirtyOnly, cnt).c_str()); for(i=0;i<GetCount();i++) { if ( (dirtyOnly && fDataRecList[i]->IsDirty()) || (!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;i<level; i++) pad += " "; int cnt = dirtyOnly ? GetDirtyCount() : GetUsedCount(); stream->WriteString(xtl::format( "%sSDVar, name:%s dirtyOnly:%d count:%d", pad.c_str(), GetName(), dirtyOnly, cnt).c_str()); for(i=0;i<GetCount();i++) { if ( (dirtyOnly && fDataRecList[i]->IsDirty()) || (!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;i<GetCount(); i++) { if (! (*GetStateDataRecord(i) == *other.GetStateDataRecord(i))) return false; } return true; } void plSDStateVariable::SetFromDefaults(bool timeStampNow) { int i; for(i=0;i<GetCount(); i++) GetStateDataRecord(i)->SetFromDefaults(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;i<GetCount(); i++) GetStateDataRecord(i)->FlagNewerState(*other.GetStateDataRecord(i), respectAlwaysNew); } void plSDStateVariable::FlagAlwaysNewState() { int i; for(i=0;i<GetCount(); i++) GetStateDataRecord(i)->FlagAlwaysNewState(); }