/*==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 .
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==*/
#ifndef PL_SYNCHEDVALUE_inc
#define PL_SYNCHEDVALUE_inc
#include "plSynchedObject.h"
#ifdef USE_SYNCHED_VALUES
#include "hsTypes.h"
#include "hsStream.h"
#include "hsBitVector.h"
#include "hsMemory.h"
//
// Defines a class for variable types which need to automatically be synchronized
// (replicated) over the network.
// current size is 8 bytes
//
//
// -----------------------------------------------------
// SYNCHED VALUE BASE CLASS
// -----------------------------------------------------
//
class hsResMgr;
class plSceneNode;
class plSceneObject;
class plCoordinateInterface;
class plSynchedValueBase
{
public:
enum Flags // 16 bits
{
kValueIsDirty = 0x1,
kValueSendOnlyOnce = 0x2, // perm flag
kValueHasBeenSent = 0x4, // perm flag
kRegistered = 0x8, // perm flag
kDontDirty = 0x10 // perm flag
};
protected:
Int16 fSynchedObjectAddrOffset; // this could represent dwords instead of byte offsets
UInt16 fFlags;
void IConstruct() // too bad this can't be virtual (because it's called from ctor)
{
// The synchMgr for the class that owns us is constructed first and the staticMgr
// is set to his address so we can automatically get at it during construction.
fFlags=0;
Int32 off = (Int32)plSynchedObject::GetStaticSynchedObject() - (Int32)this;
if ( hsABS(off) < (1<<(sizeof(fSynchedObjectAddrOffset)<<3)) )
fSynchedObjectAddrOffset = (Int16)off;
else
fSynchedObjectAddrOffset=-1;
}
hsBool32 IOKToDirty()
{
if (fFlags & (kDontDirty | kValueIsDirty))
return false;
return GetSynchedObject() ? GetSynchedObject()->IOKToDirty() : false;
}
virtual void ISaveOrLoad(hsBool32 save, hsStream* stream, hsResMgr* mgr) = 0;
// save/load methods for different types
const plKey ISaveOrLoad(const plKey key, hsBool32 save, hsStream* stream, hsResMgr* mgr);
hsKeyedObject* ISaveOrLoad(hsKeyedObject* obj, hsBool32 save, hsStream* stream, hsResMgr* mgr);
plSceneNode* ISaveOrLoad(plSceneNode* obj, hsBool32 save, hsStream* stream, hsResMgr* mgr);
plSceneObject* ISaveOrLoad(plSceneObject* obj, hsBool32 save, hsStream* stream, hsResMgr* mgr);
Int32 ISaveOrLoad(Int32 v, hsBool32 save, hsStream* stream, hsResMgr* mgr);
UInt32 ISaveOrLoad(UInt32 v, hsBool32 save, hsStream* stream, hsResMgr* mgr);
bool ISaveOrLoad(bool v, hsBool32 save, hsStream* stream, hsResMgr* mgr);
int ISaveOrLoad(int v, hsBool32 save, hsStream* stream, hsResMgr* mgr); // or hsBool32
hsScalar ISaveOrLoad(hsScalar v, hsBool32 save, hsStream* stream, hsResMgr* mgr);
double ISaveOrLoad(double v, hsBool32 save, hsStream* stream, hsResMgr* mgr);
hsBitVector ISaveOrLoad(hsBitVector& v, hsBool32 save, hsStream* stream, hsResMgr* mgr);
plCoordinateInterface* ISaveOrLoad(const plCoordinateInterface* cInt, hsBool32 save, hsStream* stream, hsResMgr* mgr);
public:
plSynchedValueBase() { IConstruct(); }
virtual ~plSynchedValueBase() {}
// getters
virtual plSynchedObject* GetSynchedObject()
{
hsAssert(fSynchedObjectAddrOffset!=-1, "invalid synchedObject address offset");
plSynchedObject* so = fSynchedObjectAddrOffset == -1 ? nil : (plSynchedObject*)((Int32)this+fSynchedObjectAddrOffset);
if (!(fFlags & kRegistered) && so)
{
so->RegisterSynchedValue(this);
fFlags |= kRegistered;
}
return so;
}
UInt16 GetFlags() { return fFlags; }
// setters
void SetFlags(UInt16 f) { fFlags=f; }
void MakeDirty() { SetFlags(GetFlags() | kValueIsDirty); }
void MakeClean() { SetFlags(GetFlags() & ~kValueIsDirty); }
void DirtyIfNecessary()
{
if (IOKToDirty())
{
MakeDirty(); // dirty value
if (GetSynchedObject())
GetSynchedObject()->DirtySynchState(nil, 0); // dirty owner
}
}
// save/load
static void Save(plSynchedValueBase& obj, hsStream* stream, hsResMgr* mgr) { obj.ISaveOrLoad(true, stream, mgr); }
static void Load(plSynchedValueBase& obj, hsStream* stream, hsResMgr* mgr) { obj.ISaveOrLoad(false, stream, mgr); }
};
//
// -----------------------------------
// SYNCHED VALUE TEMPLATE
// -----------------------------------
//
template
class plSynchedValue : public plSynchedValueBase
{
protected:
T fValue;
void ISaveOrLoad(hsBool32 save, hsStream* stream, hsResMgr* mgr)
{ fValue=(T)plSynchedValueBase::ISaveOrLoad(fValue, save, stream, mgr); } // default method
public:
plSynchedValue() {}
plSynchedValue(const T& v) : plSynchedValueBase() { fValue=v; }
plSynchedValue(const plSynchedValue& pRHS) : plSynchedValueBase() { fValue=pRHS.GetValue(); } // copy ctor
// conversion operators
operator T() const { return fValue; }
T* operator &() const { return &fValue; }
T* operator &() { return &fValue; }
// equality
hsBool32 operator==(const T& other) const { return fValue==(T)other; }
hsBool32 operator!=(const T& other) const { return !(*this == other); }
// other operators
T operator++() { DirtyIfNecessary(); return ++fValue; }
T operator++(int) { DirtyIfNecessary(); return fValue++; } // postfix
T operator--() { DirtyIfNecessary(); return --fValue; }
T operator--(int) { DirtyIfNecessary(); return fValue--; } // postfix
T operator+=(const T& other) { return SetValue(fValue+other); }
T operator*=(const T& other) { return SetValue(fValue*other); }
T operator/=(const T& other) { return SetValue(fValue/other); }
// these return reference in the event they are bitvector types
T& operator&=(const T& other) { return SetValue(fValue&other); }
T& operator|=(const T& other) { return SetValue(fValue|other); }
T& operator^=(const T& other) { return SetValue(fValue^other); }
T& operator-=(const T& other) { return SetValue(fValue-other); }
const T& operator=(const T& v){ return SetValue(v); }
#if HS_BUILD_FOR_WIN32
#pragma warning( push )
#pragma warning( disable : 4284 ) // disable annoying warnings in release build for non pointer types
#endif
// for pointer types, which are allowed to change the object pointed to
T operator->(void) { return fValue; }
#if HS_BUILD_FOR_WIN32
#pragma warning( pop )
#endif
// asignment, can use instead of setvalue
const T& operator=(const plSynchedValue& pRHS ) { return SetValue(pRHS.GetValue()); }
// setters
T& SetValue(const T& v) // return true if changed value
{
if (v != fValue) // dont dirty unless value changes
{
fValue=v;
DirtyIfNecessary();
}
return fValue;
}
// getters
const T& GetValue() const { return fValue; }
// for hsBitVector
hsBool32 IsBitSet(UInt32 which) const { return fValue.IsBitSet(which); }
hsBool32 SetBit(UInt32 which, hsBool32 on = true)
{ hsBool32 bitSet = IsBitSet(which);
if ( (on && !bitSet) || (!on && bitSet) )
DirtyIfNecessary();
return fValue.SetBit(which, on);
}
void Read(hsStream* s) { fValue.Read(s); }
void Write(hsStream* s) const { fValue.Write(s); }
void Clear() { DirtyIfNecessary(); fValue.Clear(); }
hsBool32 ClearBit(UInt32 which) { if (fValue.IsBitSet(which)) DirtyIfNecessary(); return fValue.ClearBit(which); }
void Reset() { if (fValue.GetSize()!=0) DirtyIfNecessary(); fValue.Reset(); }
hsBool32 ToggleBit(UInt32 which) { DirtyIfNecessary(); return fValue.ToggleBit(which); }
UInt32 GetSize() { return fValue.GetSize(); }
};
//////////////////////////////////////
// Synched Value Friend - allows a synched value to be contained
// in an object which is not a synchedObject. Uses a pointer instead
// of an computer the addr offset of it's associated synchedObject.
// This one is 4 bytes bigger than regular synched values.
//////////////////////////////////////
template
class plSynchedValueFriend : public plSynchedValue
{
protected:
plSynchedObject** fSynchedObject;
public:
plSynchedValueFriend() : fSynchedObject(nil) { }
// this is explicit so it won't be invoked instead of operator()=
explicit plSynchedValueFriend(const T& v) : plSynchedValue(v),fSynchedObject(nil) { }
plSynchedValueFriend(const plSynchedValueFriend& pRHS) : plSynchedValue(pRHS)
{ fSynchedObject = pRHS.fSynchedObject; }
~plSynchedValueFriend()
{
if (GetSynchedObject())
GetSynchedObject()->RemoveSynchedValue(this);
}
// this isn't inherited for some reason
const T& operator=(const T& v) { return SetValue(v); }
plSynchedObject* GetSynchedObject()
{
hsAssert(fSynchedObject, "nil synched object, need to SetSynchedObjectPtrAddr?");
if (*fSynchedObject && !(fFlags & kRegistered))
{
(*fSynchedObject)->RegisterSynchedValueFriend(this);
fFlags |= kRegistered;
}
return *fSynchedObject;
}
void SetSynchedObjectPtrAddr(plSynchedObject** so)
{
hsAssert(!(fFlags & kRegistered), "SynchedValueFriend already registered?");
fSynchedObject=so;
}
};
/////////////////////////////////////
// Synched TArray Template
/////////////////////////////////////
#include "hsTemplates.h"
template
class plSynchedTArray : public plSynchedValueBase
{
private:
hsTArray fValueList;
void ISaveOrLoad(hsBool32 save, hsStream* stream, hsResMgr* mgr);
public:
enum { kMissingIndex = hsTArray::kMissingIndex };
plSynchedTArray() {}
~plSynchedTArray() {}
// conversion operators
operator T() const { return fValueList; }
// common methods
const T& operator[](int i) const { return Get(i); }
const T& Get(int i) const { return fValueList.Get(i); }
void Set(int i, const T& item) { if (fValueList[i] != item) DirtyIfNecessary(); fValueList[i]=item; }
void Append(const T& item) { fValueList.Append(item); DirtyIfNecessary(); }
T* Insert(int index) { fValueList.Insert(index); DirtyIfNecessary(); }
void Remove(int index) { fValueList.Remove(index); DirtyIfNecessary(); }
int Count() const { return fValueList.Count(); }
int GetCount() const { return Count(); }
void Reset() { if (fValueList.GetCount() != 0) DirtyIfNecessary(); fValueList.Reset(); }
void SetCountAndZero(int count) { if (count || GetCount()) DirtyIfNecessary(); fValueList.SetCountAndZero(count); }
void SetCount(int count) { if (count || GetCount()) DirtyIfNecessary(); fValueList.SetCount(count); }
void ExpandAndZero(int count) { if (count || GetCount()) DirtyIfNecessary(); fValueList.ExpandAndZero(count); }
int Find(const T& item) const { return fValueList.Find(item); }
T* Push() { DirtyIfNecessary(); return fValueList.Push(); }
void Push(const T& item) { DirtyIfNecessary(); return fValueList.Push(item); }
T Pop() { DirtyIfNecessary(); return fValueList.Pop(); }
const T& Peek() const { return fValue.Peek(); }
T* DetachArray() { DirtyIfNecessary(); return fValueList.DetachArray(); }
T* AcquireArray() { DirtyIfNecessary(); return fValueList.AcquireArray(); }
};
//
// inlines
//
template inline
void plSynchedTArray::ISaveOrLoad(hsBool32 save, hsStream* stream, hsResMgr* mgr)
{
if (save)
{
// write out size of array
Int32 i, num = fValueList.GetCount();
stream->WriteLE(num);
for(i=0;iReadLE(&num);
for(i=0;i
class plSynchedTArrayFriend : public plSynchedTArray
{
protected:
plSynchedObject** fSynchedObject;
public:
plSynchedTArrayFriend() : fSynchedObject(nil) { }
plSynchedObject* GetSynchedObject()
{
hsAssert(fSynchedObject, "nil synched object, need to SetSynchedObjectPtrAddr?");
if (*fSynchedObject && !(fFlags & kRegistered))
{
(*fSynchedObject)->RegisterSynchedValueFriend(this);
fFlags |= kRegistered;
}
return *fSynchedObject;
}
void SetSynchedObjectPtrAddr(plSynchedObject** so)
{
hsAssert(!(fFlags & kRegistered), "SynchedValueTArrayFriend already registered?");
fSynchedObject=so;
}
#if 0
//
// redefine operators since they are not inherited
//
// conversion operators
operator T() const { return fValueList; }
// common methods
const T& operator[](int i) const { return Get(i); }
#endif
};
#endif // USE_SYNCHED_VALUES
#endif // PL_SYNCHEDVALUE_inc