/*==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 <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(); }