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