// Ryzom - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero 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 Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . /* ************************************** NOTE: THIS IS NOT A NORMAL HEADER FILE ************************************** This header file generates code for the following methods for a user-defined class void ::store(CPersistentDataRecord &pdr) const; void ::apply(CPersistentDataRecord &pdr); Note: Prototypes for these methods are also defined in the DECLARE_PERSISTENCE_METHODS macro in the persistent_data.h file. Note: This header may be included several times in the same cpp file in order to instantiate store and apply methods for different classes USAGE: #define PERSISTENT_CLASS #define PERSISTENT_DATA #include "persistent_data_template.h" #undef PERSISTENT_CLASS #undef PERSISTENT_DATA EXAMPLE USAGE: //////////////////////////////////////////////////////// #define PERSISTENT_CLASS CMyClass #define PERSISTENT_DATA\ PROP(sint16, Title, getTitle(), setTitle(val))\ PROP(uint64, VisualPropertyA, getVisualPropertyA(), setVisualPropertyA(val)) #include "perstent_data_template.h" #undef PERSISTENT_CLASS #undef PERSISTENT_DATA //////////////////////////////////////////////////////// #define PERSISTENT_CLASS CAnotherClass #define PERSISTENT_DATA\ PROP_VECT(ucstring,IsIgnoredBy,_IsIgnoredBy,_IsIgnoredBy[i],_IsIgnoredBy[i]=val)\ PROP_MAP(SP_TYPE,uint32,SkillPoints,_SpType,toString(key),val,setSp(fromString(key),val))\ STRUCT(EntityBase,CEntityBaseCharacter::store(pdr),CEntityBaseCharacter::apply(pdr))\ STRUCT_VECT(Pact,_Pact,_Pact[i].store(pdr),_Pact[i].apply(pdr))\ STRUCT_MAP(TAIAlias,TMissionHistory,MissionHistories,_MissionHistories,key,val.store(pdr),_MissionHistories[key].apply(pdr)) #include "persistent_data_template.h" #undef PERSISTENT_CLASS #undef PERSISTENT_DATA //////////////////////////////////////////////////////// MACROS FOR PERSISTENT_DATA DEFINITION PROP(type,varName) PROP2(name,type,get,set) PROP3(name,type,logic,get,set) PROP_SET(type,varName) PROP_VECT(type,varName) PROP_MAP(keyType,valType,varName) PROP_MAP2(name,keyType,valType,logic,getKey,getVal,set) STRUCT(varName) STRUCT2(name,write,read) STRUCT3(name,logic,write,read) STRUCT_VECT(varName) STRUCT_MAP(keyType,valType,varName) STRUCT_PTR_VECT(type,varName) STRUCT_MAP2(name,keyType,logic,getKey,valWrite,read) Where: name - the name stored in the persistent data record for this data element varName - the name of a container (map or vector) to be iterated over type - the data type of a variable keyType - the data type of the key for a persistent map valType - the data type of the value for a persistent map get - code that returns a value to be stored in the persistent data write - code to be executed for a structure at write time (equivalent to 'get' for a property) getKey - code that returns a value for a map key to be stored in the persistent data getVal - code that returns a value for a map value to be stored in the persistent data valWrite - code to be executed for a map value structure at write time (equivalent to 'getVal' for a property) set - code to store the value 'val' in persistent property at read time read - code to be executed for a structure at read time (equivalent to 'get') Context notes: write - 'pdr' contains the persistent data record currently being processed getKey - 'it' is the map iterator getVal - 'it' is the map iterator valWrite - 'pdr' contains the persistent data record currently being processed set - 'val' contains the value to apply read - 'pdr' contains the persistent data record currently being processed read - for maps: 'key' contains the map key read - for property maps: 'val' contains the map entry value PROP_VECT - 'i' is the vector index during all read and write operations STRUCT_VECT - 'i' is the vector index during all read and write operations */ #ifndef PERSISTENT_CLASS #error PERSISTENT_CLASS not defined #endif #ifndef PERSISTENT_DATA #error PERSISTENT_DATA not defined #endif ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #define MAP_LOGIC(keyType,valType,varName) for(std::map::const_iterator it=varName.begin();it!=varName.end();++it) #define SET_LOGIC(type,varName) for(std::set::const_iterator it=varName.begin();it!=varName.end();++it) #define LIST_LOGIC(type,varName) for(std::list::const_iterator it=varName.begin();it!=varName.end();++it) #define VECT_LOGIC(varName) ARRAY_LOGIC(varName.size()) #define ARRAY_LOGIC(size) for(uint32 i=0;ifirst,it->second,varName[key]=val) #define NPROP_MAP(name,keyType,valType,varName) _PROP_MAP(__Tok##name,#name,keyType,valType,MAP_LOGIC(keyType,valType,varName),it->first,it->second,varName[key]=val) #define PROP_MAP2(name,keyType,valType,getKey,getVal,set) _PROP_MAP(__Tok##name,#name,keyType,valType,MAP_LOGIC(keyType,valType,varName),getKey,getVal,set) #define PROP_ARRAY(type,varName,size) _PROP_MAP(__Tok##varName,#varName,uint32,type,ARRAY_LOGIC(size),i,varName[i],if (keystore(pdr),(varName.push_back(new type),varName.back())->apply(pdr)) #define STRUCT_SMRTPTR_VECT(type,varName) _STRUCT(__Tok##varName,#varName,VECT_LOGIC(varName),if (varName[i]!=NULL)varName[i]->store(pdr),(varName.push_back(NLMISC::CSmartPtr(new type)),varName.back())->apply(pdr)) #define STRUCT_INDEXED_VECT(varName) _STRUCT_MAP(__Tok##varName,#varName,uint32,VECT_LOGIC(varName),i,varName[i].store(pdr),if (key>=varName.size()) varName.resize(key); varName[key].apply(pdr)) #define STRUCT_MAP(keyType,valType,varName) _STRUCT_MAP(__Tok##varName,#varName,keyType,MAP_LOGIC(keyType,valType,varName),it->first,it->second.store(pdr),varName[key].apply(pdr)) #define LSTRUCT_MAP2(name,keyType,logic,getKey,valWrite,read) _STRUCT_MAP(__Tok##name,#name,keyType,logic,getKey,valWrite,read) #define STRUCT_ARRAY(varName,size) _STRUCT_MAP(__Tok##varName,#varName,uint32,ARRAY_LOGIC(size),i,varName[i].store(pdr),if (key avoid them return std::max(newTick, sint32(0)); } #endif // GameCycle property (saved as a UTC of the current game cycle, support server migration) #define PROP_GAME_CYCLE(varName)\ /* read and write, in UTC seconds. if result is negative, clamp to 0 */ \ PROP2(UTC_##varName, uint32, saveGameCycleToSecond(varName), varName=loadSecondToGameCycle(val)) // GameCycle property with Backward compatibility (saved as a UTC of the current game cycle, support server migration) #define PROP_GAME_CYCLE_COMP(varName)\ /* don't write (old variable name), but direct read if in pdr */ \ LPROP2(varName, NLMISC::TGameCycle, if(0), 0, varName=val) \ /* read and write, in UTC seconds. if result is negative, clamp to 0 */ \ PROP2(UTC_##varName, uint32, saveGameCycleToSecond(varName), varName=loadSecondToGameCycle(val)) // SideNotes: // if the server where we load the game cycle is different from the one from where we saved, and if he is younger, // then we can have situations where gamecycle<0. Since TGameCycle is actually a uint32, this will cause bugs. // Hence we don't allow <0 values. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////// // // NOTE: This method is declared in DECLARE_PERSISTENCE_METHODS macro in persistent_data.h // /////////////////////////////////////////////////////////////////////////////////////////// #ifndef PERSISTENT_NO_STORE #ifdef PERSISTENT_TOKEN_FAMILY #ifdef NL_OS_WINDOWS #pragma message( "Using token family "NL_MACRO_TO_STR(PERSISTENT_TOKEN_FAMILY)" for persistent data class " NL_MACRO_TO_STR(PERSISTENT_CLASS) ) #endif // NL_OS_WINDOWS // setup some macros for constructing the classname and object name that we need for // the 'persistent token family' constants class #define _EVAL(b,c) b##c #define _BUILD_TOKENS_CLASSNAME(c) _EVAL(c,__constants__class) #define _BUILD_TOKENS_OBJNAME(c) _EVAL(c,__constants__obj) // setup the class name and object name for the 'persistent token family' constants class and its instance #ifdef PERSISTENT_TOKEN_CLASS #define _TOKENS_CLASSNAME _BUILD_TOKENS_CLASSNAME(PERSISTENT_TOKEN_CLASS) #define _TOKENS_OBJNAME _BUILD_TOKENS_OBJNAME(PERSISTENT_TOKEN_CLASS) #else #define _TOKENS_CLASSNAME _BUILD_TOKENS_CLASSNAME(PERSISTENT_CLASS) #define _TOKENS_OBJNAME _BUILD_TOKENS_OBJNAME(PERSISTENT_CLASS) #endif // define the set of tokens required by this class // adding them to some pre-defined token family struct _TOKENS_CLASSNAME { // the ctor is used to setup the values of the static tokens _TOKENS_CLASSNAME() { #define _ADD_TOKEN(token) CPdrTokenRegistry::getInstance()->addToken(NL_MACRO_TO_STR(PERSISTENT_TOKEN_FAMILY),token) __Tok__MapKey= _ADD_TOKEN("__Key__"); __Tok__MapVal= _ADD_TOKEN("__Val__"); #define _PROP(token,name,type,logic,get,set) token= _ADD_TOKEN(name); #define _STRUCT(token,name,logic,write,read) token= _ADD_TOKEN(name); #define _PROP_MAP(token,name,keyType,valType,logic,getKey,getVal,set) token= _ADD_TOKEN(name); #define _STRUCT_MAP(token,name,keyType,logic,getKey,valWrite,read) token= _ADD_TOKEN(name); #define _FLAG(token,name,logic,code) token= _ADD_TOKEN(name); PERSISTENT_DATA #undef _PROP #undef _STRUCT #undef _PROP_MAP #undef _STRUCT_MAP #undef _FLAG #undef _ADD_TOKEN } // define the set of static variables to act as constants for stocking the set of tokens for this persistet data class uint16 __Tok__MapKey; uint16 __Tok__MapVal; #define _PROP(token,name,type,logic,get,set) uint16 token; #define _STRUCT(token,name,logic,write,read) uint16 token; #define _PROP_MAP(token,name,keyType,valType,logic,getKey,getVal,set) uint16 token; #define _STRUCT_MAP(token,name,keyType,logic,getKey,valWrite,read) uint16 token; #define _FLAG(token,name,logic,code) uint16 token; PERSISTENT_DATA #undef _PROP #undef _STRUCT #undef _PROP_MAP #undef _STRUCT_MAP #undef _FLAG }; static _TOKENS_CLASSNAME _TOKENS_OBJNAME; #undef _TOKENS_CLASSNAME #undef _TOKENS_OBJNAME #else #pragma message( " ") #pragma message( "NON-OPTIMISED: Persistent data class " NL_MACRO_TO_STR(PERSISTENT_CLASS) " not using a token family") #pragma message( " ") #endif // define _PERSISTENT_STORE_ARGS as either () or (",") for use in adding args to the store() function prototype #ifdef PERSISTENT_STORE_ARGS #define _PERSISTENT_STORE_ARGS ,PERSISTENT_STORE_ARGS #else #define _PERSISTENT_STORE_ARGS #endif // store() void PERSISTENT_CLASS::store(CPersistentDataRecord &pdr _PERSISTENT_STORE_ARGS) const { // PDR debug helper #ifdef NL_DEBUG //nldebug("PDR:store:"NL_MACRO_TO_STR(PERSISTENT_CLASS)"(enter)"); # define _PDR_TRACE(name) //nldebug("PDR:store:" NL_MACRO_TO_STR(PERSISTENT_CLASS) "." #name) #else # define _PDR_TRACE(name) #endif #ifdef PERSISTENT_TOKEN_FAMILY #ifdef NL_DEBUG // this class has been configured to use a specific token family. This means that it is not possible // to store the class to a pdr that is not using the same token family. BOMB_IF(pdr.getTokenFamily()!=NL_MACRO_TO_STR(PERSISTENT_TOKEN_FAMILY), NL_MACRO_TO_STR(PERSISTENT_CLASS)"::store() requires token family '"NL_MACRO_TO_STR(PERSISTENT_TOKEN_FAMILY)"'" " but pdr is using token family '"+pdr.getTokenFamily()+"'", return); #endif #ifdef PERSISTENT_TOKEN_CLASS #define _TOKEN(token) _BUILD_TOKENS_OBJNAME(PERSISTENT_TOKEN_CLASS).token #else #define _TOKEN(token) _BUILD_TOKENS_OBJNAME(PERSISTENT_CLASS).token #endif #else // define the set of tokens - this makes sure that the tokens exist in the map and that we only look them up the once static uint16 __Tok__MapKey= (uint16)~0u; pdr.addString("__Key__",__Tok__MapKey); static uint16 __Tok__MapVal= (uint16)~0u; pdr.addString("__Val__",__Tok__MapVal); #define _PROP(token,name,type,logic,get,set) static uint16 token= (uint16)~0u; pdr.addString(name,token); #define _STRUCT(token,name,logic,write,read) static uint16 token= (uint16)~0u; pdr.addString(name,token); #define _PROP_MAP(token,name,keyType,valType,logic,getKey,getVal,set) static uint16 token= (uint16)~0u; pdr.addString(name,token); #define _STRUCT_MAP(token,name,keyType,logic,getKey,valWrite,read) static uint16 token= (uint16)~0u; pdr.addString(name,token); #define _FLAG(token,name,logic,code) static uint16 token= (uint16)~0u; pdr.addString(name,token); PERSISTENT_DATA #undef _PROP #undef _STRUCT #undef _PROP_MAP #undef _STRUCT_MAP #undef _FLAG #define _TOKEN(token) token #endif // Add user-defined code #ifdef PERSISTENT_PRE_STORE PERSISTENT_PRE_STORE #endif // write the properties to the persistent data record #define _PROP(token,name,type,logic,get,set)\ _PDR_TRACE(name);\ logic { type val=get; pdr.push(_TOKEN(token),val); } #define _STRUCT(token,name,logic,write,read)\ _PDR_TRACE(name);\ logic { pdr.pushStructBegin(_TOKEN(token)); write; pdr.pushStructEnd(_TOKEN(token)); } #define _PROP_MAP(token,name,keyType,valType,logic,getKey,getVal,set)\ _PDR_TRACE(name);\ {\ pdr.pushStructBegin(_TOKEN(token));\ logic { keyType __k=getKey; valType __v=getVal; pdr.push(_TOKEN(__Tok__MapKey),__k); pdr.push(_TOKEN(__Tok__MapVal),__v); }\ pdr.pushStructEnd(_TOKEN(token)); \ } #define _STRUCT_MAP(token,name,keyType,logic,getKey,valWrite,read)\ _PDR_TRACE(name);\ { \ pdr.pushStructBegin(_TOKEN(token));\ logic { keyType __k=getKey; pdr.push(_TOKEN(__Tok__MapKey),__k); pdr.pushStructBegin(_TOKEN(__Tok__MapVal)); valWrite; pdr.pushStructEnd(_TOKEN(__Tok__MapVal)); }\ pdr.pushStructEnd(_TOKEN(token)); \ } #define _FLAG(token,name,logic,code)\ _PDR_TRACE(name);\ logic { pdr.push(_TOKEN(token)); } PERSISTENT_DATA #undef _PROP #undef _STRUCT #undef _PROP_MAP #undef _STRUCT_MAP #undef _FLAG #undef _PDR_TRACE #undef _TOKEN // Add user-defined code #ifdef PERSISTENT_POST_STORE PERSISTENT_POST_STORE #endif #ifdef NL_DEBUG //nldebug("PDR:store:"NL_MACRO_TO_STR(PERSISTENT_CLASS)"(leave)"); #endif } #endif // ndef PERSISTENT_NO_STORE // define _PERSISTENT_APPLY_ARGS as either () or (",") for use in adding args to the apply() function prototype #ifdef PERSISTENT_APPLY_ARGS #define _PERSISTENT_APPLY_ARGS ,PERSISTENT_APPLY_ARGS #else #define _PERSISTENT_APPLY_ARGS #endif #ifndef PERSISTENT_NO_APPLY // apply() void PERSISTENT_CLASS::apply(CPersistentDataRecord &pdr _PERSISTENT_APPLY_ARGS) { #define DECORATE(x) #x #define MY_H_AUTO(name) static NLMISC::CHTimer _timer(DECORATE(name)"_Apply"); NLMISC::CAutoTimer _auto(&_timer); MY_H_AUTO(PERSISTENT_CLASS) #undef MY_H_AUTO #undef DECORATE // PDR debug helper #ifdef NL_DEBUG //nldebug("PDR:apply:"NL_MACRO_TO_STR(PERSISTENT_CLASS)"(enter)"); # define _PDR_TRACE(name) //nldebug("PDR:apply:" NL_MACRO_TO_STR(PERSISTENT_CLASS) "." #name) #else # define _PDR_TRACE(name) #endif // define the set of tokens - this makes sure that the tokens exist in the map and that we only look them up the once static uint16 __Tok__MapKey= (uint16)~0u; pdr.addString("__Key__",__Tok__MapKey); static uint16 __Tok__MapVal= (uint16)~0u; pdr.addString("__Val__",__Tok__MapVal); #define _PROP(token,name,type,logic,get,set) static uint16 token= (uint16)~0u; pdr.addString(name,token); #define _STRUCT(token,name,logic,write,read) static uint16 token= (uint16)~0u; pdr.addString(name,token); #define _PROP_MAP(token,name,keyType,valType,logic,getKey,getVal,set) static uint16 token= (uint16)~0u; pdr.addString(name,token); #define _STRUCT_MAP(token,name,keyType,logic,getKey,valWrite,read) static uint16 token= (uint16)~0u; pdr.addString(name,token); #define _FLAG(token,name,logic,code) static uint16 token= (uint16)~0u; pdr.addString(name,token); PERSISTENT_DATA #undef _PROP #undef _STRUCT #undef _PROP_MAP #undef _STRUCT_MAP #undef _FLAG // Add user-defined code at the start of the method #ifdef PERSISTENT_PRE_APPLY PERSISTENT_PRE_APPLY #endif // apply the properties from the persistent data record #define _PROP(token,name,type,logic,get,set)\ _PDR_TRACE(name);\ if (nextToken==token) { H_AUTO(token); type val; pdr.pop(token,val); set; continue; } #define _STRUCT(token,name,logic,write,read)\ _PDR_TRACE(name);\ if (nextToken==token) { H_AUTO(token##_STRUCT); pdr.popStructBegin(token); read; pdr.popStructEnd(token); continue; } #define _PROP_MAP(token,name,keyType,valType,logic,getKey,getVal,set)\ _PDR_TRACE(name);\ if (nextToken==token) { H_AUTO(token##_Key); pdr.popStructBegin(token);\ while (!pdr.isEndOfStruct()) { H_AUTO(token##_Val); keyType key; pdr.pop(__Tok__MapKey,key); valType val; pdr.pop(__Tok__MapVal,val);set;}\ pdr.popStructEnd(token); continue; } #define _STRUCT_MAP(token,name,keyType,logic,getKey,valWrite,read)\ _PDR_TRACE(name);\ if (nextToken==token) { H_AUTO(token##_Key); pdr.popStructBegin(token);\ while (!pdr.isEndOfStruct()) { H_AUTO(token##_Val_STRUCT); keyType key; pdr.pop(__Tok__MapKey,key);pdr.popStructBegin(__Tok__MapVal);read;pdr.popStructEnd(__Tok__MapVal);}\ pdr.popStructEnd(token); continue; } #define _FLAG(token,name,logic,code)\ _PDR_TRACE(name);\ if (nextToken==token) { H_AUTO(token##_FLAG); pdr.pop(token); code; continue; } while (!pdr.isEndOfStruct()) { uint16 nextToken= pdr.peekNextToken(); // try to match the next token to one of the persistent data elements and 'continue' on success PERSISTENT_DATA // nlwarning("Skipping unrecognised token: %s",pdr.peekNextTokenName().c_str()); // if this is a structure then skip the whole thing std::vector stack; do { H_AUTO(SkipUnknownStuff); if (pdr.isStartOfStruct()) { stack.push_back(pdr.peekNextToken()); pdr.popStructBegin(stack.back()); } else if (pdr.isEndOfStruct()) { pdr.popStructEnd(stack.back()); if (!stack.empty()) stack.pop_back(); } else { pdr.popNextArg(pdr.peekNextToken()); } } while (!stack.empty() && !pdr.isEndOfData()); } #undef _PROP #undef _STRUCT #undef _PROP_MAP #undef _STRUCT_MAP #undef _FLAG #undef _PDR_TRACE // Add user-defined code #ifdef PERSISTENT_POST_APPLY PERSISTENT_POST_APPLY #endif #ifdef NL_DEBUG //nldebug("PDR:apply:"NL_MACRO_TO_STR(PERSISTENT_CLASS)"(leave)"); #endif } #endif // ndef PERSISTENT_NO_APPLY ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #undef PROP #undef PROP2 #undef LPROP #undef LPROP2 #undef PROP_SET #undef PROP_VECT #undef LPROP_VECT #undef LPROP_VECT2 #undef PROP_MAP #undef PROP_MAP2 #undef LPROP_MAP2 #undef STRUCT #undef STRUCT2 #undef LSTRUCT #undef LSTRUCT2 #undef STRUCT_VECT #undef LSTRUCT_VECT #undef STRUCT_PTR_VECT #undef STRUCT_MAP #undef LSTRUCT_MAP2 #undef MAP_LOGIC #undef SET_LOGIC #undef VECT_LOGIC #undef ARRAY_LOGIC #undef DEFAULT_LOGIC #undef _PERSISTENT_STORE_ARGS #undef _PERSISTENT_APPLY_ARGS ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #ifdef PERSISTENT_MACROS_AUTO_UNDEF #undef PERSISTENT_CLASS #undef PERSISTENT_DATA #ifdef PERSISTENT_TOKEN_CLASS #undef PERSISTENT_TOKEN_CLASS #endif #ifdef PERSISTENT_PRE_APPLY #undef PERSISTENT_PRE_APPLY #endif #ifdef PERSISTENT_POST_APPLY #undef PERSISTENT_POST_APPLY #endif #ifdef PERSISTENT_PRE_STORE #undef PERSISTENT_PRE_STORE #endif #ifdef PERSISTENT_POST_STORE #undef PERSISTENT_POST_STORE #endif #ifdef PERSISTENT_STORE_ARGS #undef PERSISTENT_STORE_ARGS #endif #ifdef PERSISTENT_APPLY_ARGS #undef PERSISTENT_APPLY_ARGS #endif #ifdef PERSISTENT_NO_STORE #undef PERSISTENT_NO_STORE #endif #ifdef PERSISTENT_NO_APPLY #undef PERSISTENT_NO_APPLY #endif #endif //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////