/*==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_SDL_inc #define PL_SDL_inc // // Code for the State Description Language (SDL) // #include "plSDLDescriptor.h" #include "hsUtils.h" #include "hsStlUtils.h" #include "pnFactory/plCreatable.h" #include "pnKeyedObject/plKey.h" #include "pnKeyedObject/plUoid.h" #include "plUnifiedTime/plUnifiedTime.h" namespace plSDL { typedef std::list DescriptorList; enum GeneralPurpose { kLatestVersion = -1, // used when requesting the latest version of a state descriptor kMaxListSize = 9999 // maximum size of var lists }; enum ContentsFlags // or saveFlags, 16 bits { kHasUoid = 0x1, kHasNotificationInfo = 0x2, kHasTimeStamp = 0x4, kSameAsDefault = 0x8, kHasDirtyFlag = 0x10, kWantTimeStamp = 0x20, kAddedVarLengthIO = 0x8000, // using to establish a new version in the header, can delete in 8/03 kHasMaximumValue= 0xffff, }; enum RWOptions { kDirtyOnly = 1<< 0, // write option kSkipNotificationInfo = 1<< 1, // read/write option kBroadcast = 1<< 2, // send option kWriteTimeStamps = 1<< 3, // write out time stamps kTimeStampOnRead = 1<< 4, // read: timestamp each var when it gets read. write: request that the reader timestamp the dirty vars. kTimeStampOnWrite = 1<< 5, // read: n/a. write: timestamp each var when it gets written. kKeepDirty = 1<< 6, // don't clear dirty flag on read kDontWriteDirtyFlag = 1<< 7, // write option. don't write var dirty flag. kMakeDirty = 1<< 8, // read/write: set dirty flag on var read/write. kDirtyNonDefaults = 1<< 9, // dirty the var if non default value. kForceConvert = 1<<10, // always try to convert rec to latest on read }; enum BehaviorFlags { kDisallowTimeStamping = 0x1, }; extern const char* kAgeSDLObjectName; void VariableLengthRead(hsStream* s, int size, int* val); void VariableLengthWrite(hsStream* s, int size, int val); }; class plStateVarNotificationInfo { private: std::string fHintString; public: void SetHintString(const char* c) { fHintString=c; } const char* GetHintString() const { return fHintString.c_str(); } void Read(hsStream* s, UInt32 readOptions); void Write(hsStream* s, UInt32 writeOptions) const; }; // // Base class for a state variable. // A state var is a var descriptor and it's value (contents) // class plSimpleStateVariable; class plSDStateVariable; class plStateVariable { public: enum Flags { kDirty = 0x1, // true when someone sets the value using Set(...), can be cleared after writing kUsed = 0x2 // true when it contains some value (either by Set(...) or Read() ) }; protected: UInt32 fFlags; plStateVarNotificationInfo fNotificationInfo; public: plStateVariable() : fFlags(0) {} virtual ~plStateVariable() {} const char* GetName() const { return GetVarDescriptor()->GetName(); } bool IsNamed(const char* n) const { return (n && !stricmp(GetName(), n)); } virtual int GetCount() const = 0; // conversion ops virtual plSimpleStateVariable* GetAsSimpleStateVar() = 0; virtual plSDStateVariable* GetAsSDStateVar() = 0; virtual plVarDescriptor* GetVarDescriptor() = 0; virtual const plVarDescriptor* GetVarDescriptor() const = 0; virtual void Alloc(int cnt=-1 /* -1 means don't change count */) = 0; virtual bool IsDirty() const { return (fFlags & kDirty) != 0; } virtual bool IsUsed() const { return (fFlags & kUsed) != 0; } void SetDirty(bool d) { if (d) fFlags |= kDirty; else fFlags &= ~kDirty; } void SetUsed(bool d) { if (d) fFlags |= kUsed; else fFlags &= ~kUsed; } virtual void SetFromDefaults(bool timeStampNow) = 0; virtual void TimeStamp( const plUnifiedTime & ut=plUnifiedTime::GetCurrentTime() ) = 0; virtual const plUnifiedTime& GetTimeStamp() const = 0; plStateVarNotificationInfo& GetNotificationInfo() { return fNotificationInfo; } const plStateVarNotificationInfo& GetNotificationInfo() const { return fNotificationInfo; } virtual void DumpToObjectDebugger(bool dirtyOnly, int level) const {} virtual void DumpToStream(hsStream* stream, bool dirtyOnly, int level) const {} // IO virtual bool ReadData(hsStream* s, float timeConvert, UInt32 readOptions); virtual bool WriteData(hsStream* s, float timeConvert, UInt32 writeOptions) const; }; // // Change Notifier. // When a plSimpleStateVariable changes it's value by more than the given delta value, // a notification msg will be sent to the objects that registered interest. // class plStateChangeNotifier { friend class plSimpleStateVariable; private: float fDelta; typedef std::vector KeyList; KeyList fKeys; // the objects to notify on change>delta static UInt32 fCurrentPlayerID; void IAddKey(plKey k); int IRemoveKey(plKey k); public: plStateChangeNotifier(); plStateChangeNotifier(float i, plKey k); void AddNotificationKey(plKey k) { IAddKey(k); } void AddNotificationKeys(KeyList keys); int RemoveNotificationKey(plKey k); // returns number of keys left after removal int RemoveNotificationKeys(KeyList keys); // returns number of keys left after removal void SendNotificationMsg(const plSimpleStateVariable* srcVar, const plSimpleStateVariable* dstVar, const char* sdlName); bool GetValue(float* i) const; bool SetValue(float i); static UInt32 GetCurrentPlayerID() { return fCurrentPlayerID; } static void SetCurrentPlayerID(UInt32 p) { fCurrentPlayerID=p; } bool operator==(const plStateChangeNotifier &) const; }; // // A (non-nested) variable descriptor and its data contents. // class plUoid; class plKey; class plClientUnifiedTime; typedef unsigned char byte; class plSimpleStateVariable : public plStateVariable { protected: union { int* fI; // array of int short* fS; // array of short byte* fBy; // array of byte float* fF; // array of float double* fD; // array of double bool* fB; // array of bool plUoid* fU; // array of uoid plCreatable** fC; // array of plCreatable ptrs plVarDescriptor::String32* fS32; // array of strings plClientUnifiedTime* fT; // array of Times }; mutable plUnifiedTime fTimeStamp; // the last time the var was changed plSimpleVarDescriptor fVar; typedef std::vector StateChangeNotifiers; StateChangeNotifiers fChangeNotifiers; void IDeAlloc(); void IInit(); // initize vars void IVarSet(bool timeStampNow=false); // converter fxns bool IConvertFromBool(plVarDescriptor::Type newType); bool IConvertFromInt(plVarDescriptor::Type newType); bool IConvertFromByte(plVarDescriptor::Type newType); bool IConvertFromShort(plVarDescriptor::Type newType); bool IConvertFromFloat(plVarDescriptor::Type newType); bool IConvertFromDouble(plVarDescriptor::Type newType); bool IConvertFromString(plVarDescriptor::Type newType); bool IConvertFromRGB(plVarDescriptor::Type newType); bool IConvertFromRGBA(plVarDescriptor::Type newType); bool IConvertFromRGB8(plVarDescriptor::Type newType); bool IConvertFromRGBA8(plVarDescriptor::Type newType); bool IReadData(hsStream* s, float timeConvert, int idx, UInt32 readOptions); bool IWriteData(hsStream* s, float timeConvert, int idx, UInt32 writeOptions) const; public: plSimpleStateVariable() { IInit(); } plSimpleStateVariable(plVarDescriptor* vd) { IInit(); CopyFrom(vd); } ~plSimpleStateVariable() { IDeAlloc(); } // conversion ops plSimpleStateVariable* GetAsSimpleStateVar() { return this; } plSDStateVariable* GetAsSDStateVar() { return nil; } bool operator==(const plSimpleStateVariable &other) const; // assumes matching var descriptors void TimeStamp( const plUnifiedTime & ut=plUnifiedTime::GetCurrentTime() ); void CopyFrom(plVarDescriptor* v); void CopyData(const plSimpleStateVariable* other, UInt32 writeOptions=0); bool SetFromString(const char* value, int idx, bool timeStampNow); // set value from string, type. return false on err char* GetAsString(int idx) const; bool ConvertTo(plSimpleVarDescriptor* toVar, bool force=false); // return false on err void Alloc(int cnt=-1 /* -1 means don't change count */); // alloc memory after setting type void Reset(); // setters bool Set(float v, int idx=0); bool Set(float* v, int idx=0); // floatVector bool Set(double v, int idx=0); bool Set(double* v, int idx=0); // doubleVector bool Set(int v, int idx=0); bool Set(int* v, int idx=0) { return Set(*v, idx); } // helper since there is no int vec type bool Set(byte v, int idx=0); bool Set(byte* v, int idx=0); // byteVector bool Set(short v, int idx=0); bool Set(short* v, int idx=0) { return Set(*v, idx); } // helper since there is no short vec type bool Set(bool v, int idx=0); bool Set(bool* v, int idx=0) { return Set(*v, idx); } // helper since there is no bool vec type bool Set(const char* v, int idx=0); // string bool Set(const plKey& v, int idx=0); bool Set(plCreatable*, int idx=0); // only SDL generated by the server is allowed to use this type. void SetFromDefaults(bool timeStampNow); // getters bool Get(int* value, int idx=0) const; bool Get(short* value, int idx=0) const; bool Get(byte* value, int idx=0) const; // returns byte or byteVector bool Get(float* value, int idx=0) const; // returns float or floatVector bool Get(double* value, int idx=0) const; // returns double or doubleVector bool Get(bool* value, int idx=0) const; bool Get(char value[], int idx=0) const; bool Get(plKey* value, int idx=0) const; bool Get(plCreatable** value, int idx=0) const; const plUnifiedTime& GetTimeStamp() const { return fTimeStamp; } // Special backdoor so the KI Manager can get the key name without having a ResMgr const char* GetKeyName(int idx=0) const; int GetCount() const { return fVar.GetCount(); } // helper plVarDescriptor* GetVarDescriptor() { return &fVar; } plSimpleVarDescriptor* GetSimpleVarDescriptor() { return fVar.GetAsSimpleVarDescriptor(); } const plVarDescriptor* GetVarDescriptor() const { return &fVar; } const plSimpleVarDescriptor* GetSimpleVarDescriptor() const { return fVar.GetAsSimpleVarDescriptor(); } // State Change Notification void AddStateChangeNotification(plStateChangeNotifier& n); void RemoveStateChangeNotification(plKey notificationObj); // remove all with this key void RemoveStateChangeNotification(plStateChangeNotifier n); // remove ones which match void NotifyStateChange(const plSimpleStateVariable* other, const char* sdlName); // send notification msg if necessary, internal use void DumpToObjectDebugger(bool dirtyOnly, int level) const; void DumpToStream(hsStream* stream, bool dirtyOnly, int level) const; // IO bool ReadData(hsStream* s, float timeConvert, UInt32 readOptions); bool WriteData(hsStream* s, float timeConvert, UInt32 writeOptions) const; }; // // A list of state data records, all of which are the same kind. // Corresponds to a SD var descriptor. // class plStateDataRecord; class plSDStateVariable : public plStateVariable { public: typedef std::vector ConstDataRecList; protected: typedef std::vector DataRecList; DataRecList fDataRecList; plSDVarDescriptor* fVarDescriptor; void IDeInit(); public: plSDStateVariable(plSDVarDescriptor* sdvd); ~plSDStateVariable(); // delete all records // conversion ops plSimpleStateVariable* GetAsSimpleStateVar() { return nil; } plSDStateVariable* GetAsSDStateVar() { return this; } bool operator==(const plSDStateVariable &other) const; // assumes matching var descriptors void ConvertTo(plSDStateVariable* otherSDVar, bool force=false); void CopyFrom(plSDStateVariable* other, UInt32 writeOptions=0); void UpdateFrom(plSDStateVariable* other, UInt32 writeOptions=0); void AddStateDataRecord(plStateDataRecord *sdr) { fDataRecList.push_back(sdr); SetDirty(true); SetUsed(true); } void InsertStateDataRecord(plStateDataRecord *sdr, int i) { fDataRecList[i] = sdr; SetDirty(true); SetUsed(true);} void SetFromDefaults(bool timeStampNow); void TimeStamp( const plUnifiedTime & ut=plUnifiedTime::GetCurrentTime() ); const plUnifiedTime& GetTimeStamp() const { static plUnifiedTime foo; return foo; } void Alloc(int cnt=-1 /* -1 means don't change count */); // wipe and re-create void Alloc(plSDVarDescriptor* sdvd, int cnt=-1); // wipe and re-create void Resize(int cnt); bool IsDirty() const; bool IsUsed() const; // getters plStateDataRecord* GetStateDataRecord(int i) { return fDataRecList[i]; } const plStateDataRecord* GetStateDataRecord(int i) const { return fDataRecList[i]; } int GetCount() const { return fDataRecList.size(); } int GetUsedCount() const; int GetDirtyCount() const; void GetUsedDataRecords(ConstDataRecList*) const; void GetDirtyDataRecords(ConstDataRecList*) const; plVarDescriptor* GetVarDescriptor() { return fVarDescriptor; } plSDVarDescriptor* GetSDVarDescriptor() { return fVarDescriptor->GetAsSDVarDescriptor(); } const plVarDescriptor* GetVarDescriptor() const { return fVarDescriptor; } const plSDVarDescriptor* GetSDVarDescriptor() const { return fVarDescriptor->GetAsSDVarDescriptor(); } void FlagNewerState(const plSDStateVariable&, bool respectAlwaysNew); void FlagAlwaysNewState(); void DumpToObjectDebugger(bool dirtyOnly, int level) const; void DumpToStream(hsStream* stream, bool dirtyOnly, int level) const; // IO bool ReadData(hsStream* s, float timeConvert, UInt32 readOptions); bool WriteData(hsStream* s, float timeConvert, UInt32 writeOptions) const; }; // // Contains the actual data contents and points to its associated descriptor // class plNetMsgSDLState; class plStateDataRecord : public plCreatable { public: typedef std::vector SimpleVarsList; typedef std::vector SDVarsList; enum Flags { kVolatile = 0x1 }; protected: typedef std::vector VarsList; const plStateDescriptor* fDescriptor; plUoid fAssocObject; // optional VarsList fVarsList; // list of variables VarsList fSDVarsList; // list of nested data records UInt32 fFlags; static const UInt8 kIOVersion; // I/O Version void IDeleteVarsList(VarsList& vars); void IInitDescriptor(const char* name, int version); // or plSDL::kLatestVersion void IInitDescriptor(const plStateDescriptor* sd); void IReadHeader(hsStream* s); void IWriteHeader(hsStream* s) const; bool IConvertVar(plSimpleStateVariable* fromVar, plSimpleStateVariable* toVar, bool force); plStateVariable* IFindVar(const VarsList& vars, const char* name) const; int IGetNumUsedVars(const VarsList& vars) const; int IGetUsedVars(const VarsList& varsOut, VarsList *varsIn) const; // build a list of vars that have data bool IHasUsedVars(const VarsList& vars) const; int IGetNumDirtyVars(const VarsList& vars) const; int IGetDirtyVars(const VarsList& varsOut, VarsList *varsIn) const; // build a list of vars that are dirty bool IHasDirtyVars(const VarsList& vars) const; public: CLASSNAME_REGISTER( plStateDataRecord ); GETINTERFACE_ANY( plStateDataRecord, plCreatable); plStateDataRecord(const char* sdName, int version=plSDL::kLatestVersion); plStateDataRecord(plStateDescriptor* sd); plStateDataRecord(const plStateDataRecord &other, UInt32 writeOptions=0 ):fFlags(0) { CopyFrom(other, writeOptions); } plStateDataRecord():fFlags(0) {} ~plStateDataRecord(); bool ConvertTo(plStateDescriptor* other, bool force=false ); bool operator==(const plStateDataRecord &other) const; // assumes matching state descriptors UInt32 GetFlags() const { return fFlags; } void SetFlags(UInt32 f) { fFlags =f; } plSimpleStateVariable* FindVar(const char* name) const { return (plSimpleStateVariable*)IFindVar(fVarsList, name); } plSDStateVariable* FindSDVar(const char* name) const { return (plSDStateVariable*)IFindVar(fSDVarsList, name); } plStateDataRecord& operator=(const plStateDataRecord& other) { CopyFrom(other); } void CopyFrom(const plStateDataRecord& other, UInt32 writeOptions=0); void UpdateFrom(const plStateDataRecord& other, UInt32 writeOptions=0); void SetFromDefaults(bool timeStampNow); void TimeStampDirtyVars(); int GetNumVars() const { return fVarsList.size(); } plSimpleStateVariable* GetVar(int i) const { return (plSimpleStateVariable*)fVarsList[i]; } int GetNumSDVars() const { return fSDVarsList.size(); } plSDStateVariable* GetSDVar(int i) const { return (plSDStateVariable*)fSDVarsList[i]; } // Used vars bool IsUsed() const { return (HasUsedVars() || HasUsedSDVars()); } int GetNumUsedVars() const { return IGetNumUsedVars(fVarsList); } int GetUsedVars(SimpleVarsList *vars) const { return IGetUsedVars(fVarsList, (VarsList*)vars); } // build a list of vars that have data bool HasUsedVars() const { return IHasUsedVars(fVarsList); } int GetNumUsedSDVars() const { return IGetNumUsedVars(fSDVarsList); } int GetUsedSDVars(SDVarsList *vars) const { return IGetUsedVars(fSDVarsList, (VarsList*)vars); } // build a list of SD vars that have data bool HasUsedSDVars() const { return IHasUsedVars(fSDVarsList); } // Dirty Vars bool IsDirty() const { return (HasDirtyVars() || HasDirtySDVars()); } int GetNumDirtyVars() const { return IGetNumDirtyVars(fVarsList); } int GetDirtyVars(SimpleVarsList *vars) const { return IGetDirtyVars(fVarsList, (VarsList*)vars); } // build a list of vars that are dirty bool HasDirtyVars() const { return IHasDirtyVars(fVarsList); } int GetNumDirtySDVars() const { return IGetNumDirtyVars(fSDVarsList); } int GetDirtySDVars(SDVarsList *vars) const { return IGetDirtyVars(fSDVarsList, (VarsList*)vars); }; // build a list of Sdvars that are dirty bool HasDirtySDVars() const { return IHasDirtyVars(fSDVarsList); } const plStateDescriptor* GetDescriptor() const { return fDescriptor; } void SetDescriptor(const char* sdName, int version); plNetMsgSDLState* PrepNetMsg(float timeConvert, UInt32 writeOptions) const; // create/prep a net msg with this data void SetAssocObject(const plUoid& u) { fAssocObject=u; } // optional plUoid* GetAssocObject() { return &fAssocObject; } // optional const plUoid* GetAssocObject() const { return &fAssocObject; } // optional // utils void FlagDifferentState(const plStateDataRecord& other); // mark items which differ from 'other' as dirty void FlagNewerState(const plStateDataRecord& other, bool respectAlwaysNew=false); // mark items which are newer than 'other' as dirty void FlagAlwaysNewState(); // mark 'alwaysNew' items as dirty void DumpToObjectDebugger(const char* msg, bool dirtyOnly=false, int level=0) const; void DumpToStream(hsStream* stream, const char* msg, bool dirtyOnly=false, int level=0) const; // IO bool Read(hsStream* s, float timeConvert, UInt32 readOptions=0); void Write(hsStream* s, float timeConvert, UInt32 writeOptions=0) const; static bool ReadStreamHeader(hsStream* s, char** name, int* version, plUoid* objUoid=nil); void WriteStreamHeader(hsStream* s, plUoid* objUoid=nil) const; }; // // Simple SDL parser // class plSDLMgr; class plSDLParser { private: bool IReadDescriptors() const; bool ILoadSDLFile(const char* fileName) const; bool IParseVarDesc(const char* fileName, hsStream* stream, char token[], plStateDescriptor*& curDesc, plVarDescriptor*& curVar) const; bool IParseStateDesc(const char* fileName, hsStream* stream, char token[], plStateDescriptor*& curDesc) const; void DebugMsg(char* fmt, ...) const; void DebugMsgV(char* fmt, va_list args) const; public: bool Parse() const; // reads sdl folder, creates descriptor list }; // // Holds, loads and unloads all state descriptors from sdl files. // Singleton. // class plNetApp; class plSDLMgr { friend class plSDLParser; private: std::string fSDLDir; plSDL::DescriptorList fDescriptors; plNetApp* fNetApp; UInt32 fBehaviorFlags; void IDeleteDescriptors(plSDL::DescriptorList* dl); public: plSDLMgr(); ~plSDLMgr(); static plSDLMgr* GetInstance(); plStateDescriptor* FindDescriptor(const char* name, int version, const plSDL::DescriptorList * dl=nil) const; // version or kLatestVersion const plSDL::DescriptorList * GetDescriptors( void ) const { return &fDescriptors;} void SetSDLDir(const char* s) { fSDLDir=s; } const char* GetSDLDir() const { return fSDLDir.c_str(); } void SetNetApp(plNetApp* a) { fNetApp=a; } plNetApp* GetNetApp() const { return fNetApp; } bool Init( UInt32 behaviorFlags=0 ); // parse sdl folder void DeInit(); UInt32 GetBehaviorFlags() const { return fBehaviorFlags; } void SetBehaviorFlags(UInt32 v) { fBehaviorFlags=v; } bool AllowTimeStamping() const { return ! ( fBehaviorFlags&plSDL::kDisallowTimeStamping ); } // I/O - return # of bytes read/written int Write(hsStream* s, const plSDL::DescriptorList* dl=nil); // write descriptors to a stream int Read(hsStream* s, plSDL::DescriptorList* dl=nil); // read descriptors into provided list (use legacyList if nil) }; #endif // PL_SDL_inc