/*==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==*/
#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->WriteSwap(num);
for(i=0;iReadSwap(&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