You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2789 lines
77 KiB
2789 lines
77 KiB
/*==LICENSE==* |
|
|
|
CyanWorlds.com Engine - MMOG client, server and tools |
|
Copyright (C) 2011 Cyan Worlds, Inc. |
|
|
|
This program is free software: you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation, either version 3 of the License, or |
|
(at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
|
|
You should have received a copy of the GNU General Public License |
|
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
|
|
Additional permissions under GNU GPL version 3 section 7 |
|
|
|
If you modify this Program, or any covered work, by linking or |
|
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, |
|
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent |
|
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK |
|
(or a modified version of those libraries), |
|
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, |
|
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG |
|
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the |
|
licensors of this Program grant you additional |
|
permission to convey the resulting work. Corresponding Source for a |
|
non-source form of such a combination shall include the source code for |
|
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered |
|
work. |
|
|
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com |
|
or by snail mail at: |
|
Cyan Worlds, Inc. |
|
14617 N Newport Hwy |
|
Mead, WA 99021 |
|
|
|
*==LICENSE==*/ |
|
#include "hsStream.h" |
|
#include "hsTimer.h" |
|
#include "plSDL.h" |
|
|
|
#include "plProduct.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" |
|
|
|
#include <type_traits> |
|
#include <cfloat> |
|
#include <cmath> |
|
|
|
/***************************************************************************** |
|
* |
|
* 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_t fClassIndex; |
|
public: |
|
void* fData; |
|
int fDataLen; |
|
|
|
plSDLCreatableStub(uint16_t classIndex, int len) : fClassIndex(classIndex),fData(nil),fDataLen(len) {} |
|
~plSDLCreatableStub() { delete[] (char*)fData; } |
|
|
|
const char* ClassName() const { return "SDLCreatable"; } |
|
uint16_t ClassIndex() const { return fClassIndex; } |
|
|
|
void Read(hsStream* s, hsResMgr* mgr) { delete[] (char*)fData; fData = new char[fDataLen]; s->Read(fDataLen, fData); } |
|
void Write(hsStream* s, hsResMgr* mgr) { s->Write(fDataLen, fData); } |
|
}; |
|
|
|
///////////////////////////////////////////////////// |
|
// plStateVarNotificationInfo |
|
///////////////////////////////////////////////////// |
|
|
|
void plStateVarNotificationInfo::Read(hsStream* s, uint32_t readOptions) |
|
{ |
|
uint8_t saveFlags=s->ReadByte(); // unused |
|
plString hint=s->ReadSafeString(); |
|
if (!hint.IsNull() && !(readOptions & plSDL::kSkipNotificationInfo)) |
|
fHintString = hint; |
|
} |
|
|
|
void plStateVarNotificationInfo::Write(hsStream* s, uint32_t writeOptions) const |
|
{ |
|
uint8_t saveFlags=0; // unused |
|
s->WriteLE(saveFlags); |
|
s->WriteSafeString(fHintString); |
|
} |
|
|
|
///////////////////////////////////////////////////// |
|
// plStateVariable |
|
///////////////////////////////////////////////////// |
|
bool plStateVariable::ReadData(hsStream* s, float timeConvert, uint32_t readOptions) |
|
{ |
|
uint8_t saveFlags; |
|
s->ReadLE(&saveFlags); |
|
if (saveFlags & plSDL::kHasNotificationInfo) |
|
{ |
|
GetNotificationInfo().Read(s, readOptions); |
|
} |
|
return true; |
|
} |
|
|
|
bool plStateVariable::WriteData(hsStream* s, float timeConvert, uint32_t writeOptions) const |
|
{ |
|
bool writeNotificationInfo = ((writeOptions & plSDL::kSkipNotificationInfo)==0); |
|
|
|
uint8_t saveFlags=0; |
|
if (writeNotificationInfo) |
|
saveFlags |= plSDL::kHasNotificationInfo; |
|
|
|
s->WriteLE(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, plFormat("undefined atomic type:{} var:{} cnt:{}", |
|
type, GetName(), GetCount()).c_str()); |
|
break; |
|
}; |
|
|
|
} |
|
|
|
// |
|
// alloc memory |
|
// |
|
|
|
#define SDLALLOC(typeName, type, var) \ |
|
case typeName: \ |
|
var = 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, uint8_t, 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 = new plClientUnifiedTime[cnt]; |
|
break; |
|
case plVarDescriptor::kKey: |
|
fU = new plUoid[cnt]; |
|
break; |
|
case plVarDescriptor::kString32: |
|
fS32 = 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, uint8_t, 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::GetCurrent()*/ ) |
|
{ |
|
fTimeStamp = ut; |
|
} |
|
|
|
// |
|
// Set value from string. Used to set default values which are specified as strings. |
|
// |
|
bool plSimpleStateVariable::SetFromString(const plString& value, int idx, bool timeStampNow) |
|
{ |
|
if (value.IsNull()) |
|
return false; |
|
|
|
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 |
|
std::vector<plString> bits = value.Tokenize("( ,)"); |
|
int i=idx*fVar.GetAtomicCount(); |
|
for (std::vector<plString>::iterator ptr = bits.begin(); ptr != bits.end(); ++ptr) |
|
{ |
|
if ((type==plVarDescriptor::kInt) && fI) |
|
fI[i++] = ptr->ToInt(); |
|
else if (type==plVarDescriptor::kShort && fS) |
|
fS[i++] = static_cast<short>(ptr->ToInt()); |
|
else if (type==plVarDescriptor::kByte && fBy) |
|
fBy[i++] = static_cast<uint8_t>(ptr->ToInt()); |
|
else if ( (type==plVarDescriptor::kFloat || type==plVarDescriptor::kAgeTimeOfDay) && fF) |
|
fF[i++] = static_cast<float>(ptr->ToFloat()); |
|
else if ( (type==plVarDescriptor::kDouble || type==plVarDescriptor::kTime) && fD) |
|
fD[i++] = ptr->ToDouble(); |
|
} |
|
} |
|
break; |
|
case plVarDescriptor::kBool: |
|
{ |
|
// handles value in the form "(i,j,k)" for things like vectors |
|
std::vector<plString> bits = value.Tokenize("( ,)"); |
|
int i=idx*fVar.GetAtomicCount(); |
|
for (std::vector<plString>::iterator ptr = bits.begin(); ptr != bits.end(); ++ptr) |
|
{ |
|
if (!ptr->CompareI("true")) |
|
fB[i++]=true; |
|
else if (!ptr->CompareI("false")) |
|
fB[i++]=false; |
|
else |
|
fB[i++] = (ptr->ToInt() != 0); |
|
} |
|
} |
|
break; |
|
case plVarDescriptor::kString32: |
|
{ |
|
// handles value in the form "(i,j,k)" for things like vectors |
|
std::vector<plString> bits = value.Tokenize("( ,)"); |
|
int i=idx*fVar.GetAtomicCount(); |
|
for (std::vector<plString>::iterator ptr = bits.begin(); ptr != bits.end(); ++ptr) |
|
{ |
|
hsStrncpy(fS32[i++], ptr->c_str(), 32); |
|
} |
|
} |
|
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. |
|
// |
|
plString plSimpleStateVariable::GetAsString(int idx) const |
|
{ |
|
int j; |
|
plStringStream str; |
|
if (fVar.GetAtomicCount()>1) |
|
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 << fI[i++]; |
|
else if (type==plVarDescriptor::kShort) |
|
str << fS[i++]; |
|
else if (type==plVarDescriptor::kByte) |
|
str << fBy[i++]; |
|
else if (type==plVarDescriptor::kFloat || type==plVarDescriptor::kAgeTimeOfDay) |
|
str << plFormat("{.3f}", fF[i++]); |
|
else if (type==plVarDescriptor::kDouble) |
|
str << plFormat("{.3f}", fD[i++]); |
|
else if (type==plVarDescriptor::kTime) |
|
{ |
|
double tmp; |
|
Get(&tmp, i++); |
|
str << plFormat("{.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 << (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 << 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 << "other"; |
|
|
|
if (j==fVar.GetAtomicCount()-1) |
|
{ |
|
if (j) |
|
str << ')'; |
|
} |
|
else |
|
str << ','; |
|
} |
|
} |
|
break; |
|
} |
|
|
|
return str.GetString(); |
|
} |
|
|
|
// |
|
// return false on err |
|
// |
|
bool plSimpleStateVariable::IConvertFromRGB(plVarDescriptor::Type newType) |
|
{ |
|
switch(newType) |
|
{ |
|
case plVarDescriptor::kRGBA: |
|
{ |
|
// rgb to rgba |
|
int i,j; |
|
float* newF = 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; |
|
uint8_t * newB = new uint8_t [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] = uint8_t(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; |
|
uint8_t * newB = new uint8_t [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] = uint8_t(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 = 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 = 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; |
|
uint8_t * newB = new uint8_t [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 = 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; |
|
uint8_t* newB = new uint8_t[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] = uint8_t(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; |
|
uint8_t* newBy = new uint8_t [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] = uint8_t(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 = 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; |
|
uint8_t* newB = new uint8_t[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 = 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 = 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 = 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 |
|
uint8_t* newBy = new uint8_t[fVar.GetCount()]; |
|
for(j=0;j<fVar.GetCount(); j++) |
|
newBy[j] = uint8_t(fI[j]); |
|
delete [] fI; |
|
fBy = newBy; |
|
} |
|
break; |
|
case plVarDescriptor::kDouble: |
|
{ |
|
// int to double |
|
double * newD = 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 = 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 = 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 = new int[fVar.GetCount()]; |
|
for(j=0;j<fVar.GetCount(); j++) |
|
newI[j] = short(fS[j]); |
|
delete [] fS; |
|
fI = newI; |
|
} |
|
break; |
|
case plVarDescriptor::kByte: |
|
{ |
|
uint8_t* newBy = new uint8_t[fVar.GetCount()]; |
|
for(j=0;j<fVar.GetCount(); j++) |
|
newBy[j] = uint8_t(fS[j]); |
|
delete [] fS; |
|
fBy = newBy; |
|
} |
|
break; |
|
case plVarDescriptor::kDouble: |
|
{ |
|
double * newD = 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 = 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 = 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 = new int[fVar.GetCount()]; |
|
for(j=0;j<fVar.GetCount(); j++) |
|
newI[j] = short(fBy[j]); |
|
delete [] fBy; |
|
fI = newI; |
|
} |
|
break; |
|
case plVarDescriptor::kShort: |
|
{ |
|
short* newS = 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 = 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 = 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 = 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 = 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: |
|
{ |
|
uint8_t* newBy = new uint8_t[fVar.GetCount()]; |
|
for(j=0;j<fVar.GetCount(); j++) |
|
newBy[j] = (uint8_t)(fF[j]+.5f); // round to nearest int |
|
delete [] fF; |
|
fBy = newBy; |
|
} |
|
break; |
|
case plVarDescriptor::kDouble: |
|
{ |
|
double* newD = 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 = 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 = 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 = 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: |
|
{ |
|
uint8_t* newBy = new uint8_t[fVar.GetCount()]; |
|
for(j=0;j<fVar.GetCount(); j++) |
|
newBy[j] = (uint8_t)(fD[j]+.5f); // round to nearest int |
|
delete [] fD; |
|
fBy = newBy; |
|
} |
|
break; |
|
case plVarDescriptor::kFloat: |
|
{ |
|
float* newF = 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 = 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 = 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 = 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: |
|
{ |
|
uint8_t* newBy = new uint8_t[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 = 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= 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()) { |
|
#ifndef PLASMA_EXTERNAL_RELEASE |
|
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().c_str(), fVar.GetTypeString().c_str(), toVar->GetTypeString().c_str() ) ); |
|
|
|
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(uint8_t* 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((uint8_t)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((uint8_t)v, idx); |
|
|
|
hsAssert(false, "passing wrong value type to SDL variable"); |
|
return false; |
|
} |
|
|
|
bool plSimpleStateVariable::Set(uint8_t 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(strlen(v)<32, "string length overflow"); |
|
strncpy(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(uint8_t* 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 (newUoid.GetObjectName().Compare(fU[idx].GetObjectName(), plString::kCaseInsensitive) != 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; |
|
} |
|
|
|
///////////////////////////////////////////////////////////// |
|
|
|
plString 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_t 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().c_str()); |
|
} |
|
#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->WriteLE32(fI[j+i]); |
|
break; |
|
case plVarDescriptor::kShort: |
|
for(i=0;i<fVar.GetAtomicCount();i++) |
|
s->WriteLE16(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->WriteLEScalar(fF[j+i]); |
|
break; |
|
case plVarDescriptor::kTime: |
|
for(i=0;i<fVar.GetAtomicCount();i++) |
|
{ |
|
if (timeConvert != 0.0) |
|
{ |
|
double utDouble=fT[j+i].GetSecsDouble(); |
|
utDouble += timeConvert; |
|
plUnifiedTime ut(utDouble); |
|
ut.Write(s); |
|
} |
|
else |
|
fT[j+i].Write(s); |
|
} |
|
break; |
|
case plVarDescriptor::kDouble: |
|
for(i=0;i<fVar.GetAtomicCount();i++) |
|
s->WriteLEDouble(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->WriteLE16(cre ? cre->ClassIndex() : 0x8000); // creatable class index |
|
if (cre) |
|
{ |
|
hsRAMStream ramStream; |
|
cre->Write(&ramStream, hsgResMgr::ResMgr()); |
|
s->WriteLE32(ramStream.GetEOF()); // write length |
|
cre->Write(s, hsgResMgr::ResMgr()); // write data |
|
} |
|
} |
|
break; |
|
default: |
|
break; |
|
} |
|
return true; |
|
} |
|
|
|
bool plSimpleStateVariable::IReadData(hsStream* s, float timeConvert, int idx, uint32_t 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->ReadLE32(); |
|
break; |
|
case plVarDescriptor::kShort: |
|
for(i=0;i<fVar.GetAtomicCount();i++) |
|
fS[j+i]=s->ReadLE16(); |
|
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->ReadLEScalar(); |
|
break; |
|
case plVarDescriptor::kTime: |
|
for(i=0;i<fVar.GetAtomicCount();i++) |
|
{ |
|
fT[j+i].Read(s); |
|
if (timeConvert != 0.0) |
|
{ |
|
double newUt = (fT[j+i].GetSecsDouble() + timeConvert); |
|
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->ReadLEDouble(); |
|
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_t hClass = s->ReadLE16(); // class index |
|
if (hClass != 0x8000) |
|
{ |
|
uint32_t len = s->ReadLE32(); // length |
|
if (plFactory::CanCreate(hClass)) |
|
{ |
|
delete fC[j]; |
|
fC[j] = plFactory::Create(hClass); |
|
} |
|
else |
|
{ |
|
plSDLCreatableStub* stub = 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_t 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().c_str()); |
|
} |
|
#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_t 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->WriteLE(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->WriteLE32(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_t readOptions) |
|
{ |
|
// read base class data |
|
plStateVariable::ReadData(s, timeConvert, readOptions); |
|
|
|
plUnifiedTime ut; |
|
ut.ToEpoch(); |
|
|
|
uint8_t saveFlags; |
|
s->ReadLE(&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_t cnt; |
|
s->ReadLE(&cnt); // have to read as long since we don't know how big the list is |
|
|
|
if (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_t 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 |
|
// |
|
|
|
template <typename _T> |
|
typename std::enable_if<std::is_floating_point<_T>::value, _T>::type |
|
_generic_abs(_T value) |
|
{ |
|
return fabs(value); |
|
} |
|
|
|
template <typename _T> |
|
typename std::enable_if<std::is_integral<_T>::value, _T>::type |
|
_generic_abs(_T value) |
|
{ |
|
return abs(value); |
|
} |
|
|
|
#define NOTIFY_CHECK(type, var) \ |
|
case type: \ |
|
for(i=0;i<cnt;i++) \ |
|
if (_generic_abs(var[i] - other->var[i]) > d) \ |
|
{ \ |
|
notify=true; \ |
|
break; \ |
|
} \ |
|
break; |
|
|
|
void plSimpleStateVariable::NotifyStateChange(const plSimpleStateVariable* other, const plString& 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) |
|
default: break; |
|
} |
|
} |
|
if (notify) |
|
{ |
|
numNotifiers += (*it).fKeys.size(); |
|
(*it).SendNotificationMsg(other /* src */, this /* dst */, sdlName); |
|
} |
|
} |
|
} |
|
|
|
if (plNetObjectDebuggerBase::GetInstance() && plNetObjectDebuggerBase::GetInstance()->GetDebugging()) |
|
{ |
|
plNetObjectDebuggerBase::GetInstance()->LogMsg( |
|
plFormat("Var {} did {} send notification difference. Has {} notifiers with {} 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; |
|
|
|
plString pad = plString::Fill(level * 3, ' '); |
|
|
|
plStringStream logMsg; |
|
logMsg << pad << "SimpleVar, name:" << GetName() << '[' << GetCount() << ']'; |
|
|
|
if (GetCount()>1) |
|
{ |
|
dbg->LogMsg(logMsg.GetString().c_str()); // it's going to be a long msg, so print it on its own line |
|
logMsg.Truncate(); |
|
} |
|
|
|
pad += "\t"; |
|
for (int i=0; i<GetCount(); i++) |
|
{ |
|
logMsg << pad; |
|
logMsg << "Var:" << i; |
|
|
|
plString s=GetAsString(i); |
|
if (fVar.GetAtomicType() == plVarDescriptor::kTime) |
|
{ |
|
logMsg << " gameTime:" << s; |
|
logMsg << " pst:" << fT[i].PrintWMillis(); |
|
logMsg << " ts:" << fTimeStamp.Format("%c").c_str(); |
|
} |
|
else |
|
{ |
|
logMsg << " value:" << s; |
|
logMsg << " ts:" << (fTimeStamp.AtEpoch() ? "0" : fTimeStamp.Format("%c").c_str()); |
|
} |
|
|
|
if (!dirtyOnly) |
|
{ |
|
logMsg << " dirty:"; |
|
logMsg << (IsDirty() ? 0 : 1); |
|
} |
|
|
|
dbg->LogMsg(logMsg.GetString().c_str()); |
|
logMsg.Truncate(); |
|
} |
|
} |
|
|
|
void plSimpleStateVariable::DumpToStream(hsStream* stream, bool dirtyOnly, int level) const |
|
{ |
|
plString pad = plString::Fill(level * 3, ' '); |
|
|
|
plStringStream logMsg; |
|
logMsg << pad << "SimpleVar, name:" << GetName() << '[' << GetCount() << ']'; |
|
|
|
if (GetCount()>1) |
|
{ |
|
stream->WriteString(logMsg.GetString()); // it's going to be a long msg, so print it on its own line |
|
logMsg.Truncate(); |
|
} |
|
|
|
pad += "\t"; |
|
for (int i=0; i<GetCount(); i++) |
|
{ |
|
logMsg << pad; |
|
logMsg << "Var:" << i; |
|
|
|
plString s = GetAsString(i); |
|
if (fVar.GetAtomicType() == plVarDescriptor::kTime) |
|
{ |
|
logMsg << " gameTime:" << s; |
|
logMsg << " pst:" << fT[i].PrintWMillis(); |
|
logMsg << " ts:" << fTimeStamp.Format("%c").c_str(); |
|
} |
|
else |
|
{ |
|
logMsg << " value:" << s; |
|
logMsg << " ts:" << (fTimeStamp.AtEpoch() ? "0" : fTimeStamp.Format("%c").c_str()); |
|
} |
|
|
|
if (!dirtyOnly) |
|
{ |
|
logMsg << " dirty:"; |
|
logMsg << (IsDirty() ? 0 : 1); |
|
} |
|
|
|
stream->WriteString(logMsg.GetString()); |
|
logMsg.Truncate(); |
|
} |
|
} |
|
|
|
// |
|
// 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] = 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 = new plSDVarDescriptor; |
|
fVarDescriptor->CopyFrom(sdvd); |
|
} |
|
|
|
int cnt = listSize==-1 ? sdvd->GetCount() : listSize; |
|
fDataRecList.resize(cnt); |
|
int j; |
|
for (j=0;j<cnt; j++) |
|
InsertStateDataRecord(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_t 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_t writeOptions/*=0*/) |
|
{ |
|
hsAssert(!other->GetSDVarDescriptor()->GetName().CompareI(fVarDescriptor->GetName()), |
|
plFormat("var descriptor mismatch in UpdateFrom, name {},{}", |
|
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().c_str(), fVarDescriptor->GetTypeString().c_str(), |
|
otherSDVar->GetSDVarDescriptor()->GetTypeString().c_str(), 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_t readOptions) |
|
{ |
|
plStateVariable::ReadData(s, timeConvert, readOptions); |
|
|
|
uint8_t saveFlags; |
|
s->ReadLE(&saveFlags); // unused |
|
|
|
// read total list size |
|
if (GetVarDescriptor()->IsVariableLength()) |
|
{ |
|
uint32_t total; |
|
s->ReadLE(&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_t writeOptions) const |
|
{ |
|
plStateVariable::WriteData(s, timeConvert, writeOptions); |
|
|
|
uint8_t saveFlags=0; // unused |
|
s->WriteLE(saveFlags); |
|
|
|
// write total list size |
|
uint32_t total=GetCount(); |
|
if (GetVarDescriptor()->IsVariableLength()) |
|
s->WriteLE(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; |
|
|
|
plString pad = plString::Fill(level * 3, ' '); |
|
|
|
int cnt = dirtyOnly ? GetDirtyCount() : GetUsedCount(); |
|
dbg->LogMsg(plFormat("{}SDVar, name:{} dirtyOnly:{} count:{}", |
|
pad, GetName(), dirtyOnly, cnt).c_str()); |
|
|
|
for (size_t 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(plFormat("{}SDVar, name:{} dirtyOnly:{} count:{}", |
|
pad, GetName(), dirtyOnly, cnt)); |
|
|
|
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::GetCurrent()*/ ) |
|
{ |
|
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(); |
|
}
|
|
|