// 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 . #ifndef RY_PD_MESSAGES_H #define RY_PD_MESSAGES_H /* * NeL Includes */ #include #include #include #include #include #include #include #include /* * PD Lib Includes */ #include "pd_utils.h" #include "db_description_parser.h" #include "timestamp.h" namespace RY_PDS { #define MAX_MESSAGE_REMAP 32767 #define MESSAGE_REMAP_MASK 0x7fff #define MESSAGE_REMAP_ENTITYID_PRESENT 0x8000 #define MESSAGE_SETPARENT_ENTITYID_PRESENT 0x8000 /** * Object Circular Mapper * Allows to map an object value through little sized uint (uint8 or uint16), allowing to reallocate * mapping when all values are used. * This is used to map long values (CEntityIds for instance) that are expected to appear frequently * in a stream with short keys, without making sure all values fit in mapping table. * For instance, if using uint8 as mapping key, when all 256 values are used, previously used key * 0 is remapped to the new object value that appears in stream and so on (reallocations are done * circularly through key values). Mask value is for test purposes only, not to be changed! */ template > class CObjCircMapper { public: CObjCircMapper() { _Next = 0; _Max = 0; // prepare mapping table _FrontMap.resize(getMappingSize()+1); } /** * Serialise object from stream */ void serial(NLMISC::IStream& s, Object& o) { Key fakeFlags = 0; serial(s, o, fakeFlags); } /** * Serialise object from stream, with flags added to msbits * WARNING: flags should NEVER interfere with Mask! * WARNING: when stream is reading, lower bits of flags are unspecified! */ void serial(NLMISC::IStream& s, Object& o, Key& flags) { if (s.isReading()) { Key k; s.serial(flags); // remove flags from read key k = (flags & Mask); // if key is next value to be mapped if (k == _Next) { // serial in object value to map _Next = ((_Next+1)&Mask); s.serial(o); // and map it to key _FrontMap[k] = o; } else { // already seen key? just copy object value o = _FrontMap[k]; } } else { // search for object value in map typename TBackMap::iterator it = _BackMap.find(o); // not yet found or mapping key is just next key to alloc if (it == _BackMap.end() || (*it).second == _Next) { // if mapping key is next, we have to force reallocation // as serial in code can't know if value is new or not... Key k = _Next; _Next = ((_Next+1)&Mask); // if new key as already circle'd down, unmap previous association if (k < _Max) { #ifdef NL_DEBUG typename TBackMap::iterator it = _BackMap.find(_FrontMap[k]); nlassert(it != _BackMap.end() && (*it).second == k); _BackMap.erase(it); #else _BackMap.erase(_FrontMap[k]); #endif } else { // else just increase max seen key... _Max = ((uint)k)+1; } // do mapping _BackMap[o] = k; _FrontMap[k] = o; // serial mapping k |= (flags & (~Mask)); s.serial(k); s.serial(o); } else { // mapping found and correct, only serial key out Key k = ((*it).second | (flags & (~Mask))); s.serial(k); } } } private: /// Back Mapping, from object values to keys TBackMap _BackMap; /// Front Mapping, from keys to object values typedef typename std::vector TFrontMap; TFrontMap _FrontMap; /// Next Key to map Key _Next; /// Max mapped Key uint _Max; uint getMappingSize() const { return (((uint)1)<<(sizeof(Key)*8))-1; } }; typedef CObjCircMapper TEntityIdCircMapper; class CMsgObjectIndex { public: CMsgObjectIndex() : Raw(0) {} CMsgObjectIndex(uint8 table, uint32 row) { Raw = 0; set(table, row); } void set(uint8 table, uint32 row) { Table = table; Row = row; } union { uint64 Raw; struct { uint32 Row; uint8 Table; }; }; void serial(NLMISC::IStream& f) { f.serial(Table, Row); } bool operator < (const CMsgObjectIndex& a) const { return Raw < a.Raw; } }; typedef CObjCircMapper TObjectIndexCircMapper; /** * Database update message */ class CDbMessage { public: CDbMessage() : Selected(false), ContextDepth(0), _ObjectIdPresent(false) { } /// Type of message, 4bits -> 16 message types available enum THeaderType { UpdateValue = 0, SetParent = 1, AllocRow = 2, DeallocRow = 3, ReleaseRow = 4, EndRemapMessages = 4, LoadRow = 5, AddString = 6, UnmapString = 7, Log = 8, PushContext = 9, PopContext = 10, LogChat = 11, End }; /// \name setup message methods // @{ /// update value template void updateValue(TColumnIndex column, const T& value) { setHeader(UpdateValue); uint sz = 0; // update 20101119 by packpro if (sizeof(value) == 1) { sz = 0; memcpy(&(_Value0[0]), &value, sizeof(value)); } else if (sizeof(value) == 2) { sz = 1; memcpy(&(_Value1[0]), &value, sizeof(value)); } else if (sizeof(value) == 4) { sz = 2; memcpy(&(_Value2[0]), &value, sizeof(value)); } else if (sizeof(value) == 8) { sz = 3; memcpy(&(_Value3[0]), &value, sizeof(value)); } //if (sizeof(value) == 1) { sz = 0; _Value0[0] = *(uint8*)(&value); } //else if (sizeof(value) == 2) { sz = 1; _Value1[0] = *(uint16*)(&value); } //else if (sizeof(value) == 4) { sz = 2; _Value2[0] = *(uint32*)(&value); } //else if (sizeof(value) == 8) { sz = 3; _Value3[0] = *(uint64*)(&value); } _ColumnAndSize = (uint16)(column | (sz << 14)); } /// update value template void updateValue(TColumnIndex column, const T& value, const NLMISC::CEntityId& objectId) { setHeader(UpdateValue); uint sz; // update 20101119 by packpro if (sizeof(value) == 1) { sz = 0; memcpy(&(_Value0[0]), &value, sizeof(value)); } else if (sizeof(value) == 2) { sz = 1; memcpy(&(_Value1[0]), &value, sizeof(value)); } else if (sizeof(value) == 4) { sz = 2; memcpy(&(_Value2[0]), &value, sizeof(value)); } else if (sizeof(value) == 8) { sz = 3; memcpy(&(_Value3[0]), &value, sizeof(value)); } //if (sizeof(value) == 1) { sz = 0; _Value0[0] = *(uint8*)(&value); } //else if (sizeof(value) == 2) { sz = 1; _Value1[0] = *(uint16*)(&value); } //else if (sizeof(value) == 4) { sz = 2; _Value2[0] = *(uint32*)(&value); } //else if (sizeof(value) == 8) { sz = 3; _Value3[0] = *(uint64*)(&value); } _ColumnAndSize = (uint16)(column | (sz << 14)); //_MapTableRow |= MESSAGE_REMAP_ENTITYID_PRESENT; _ObjectIdPresent = true; _ObjectId = objectId; } /// set parent void setParent(TColumnIndex column, const CObjectIndex& parent) { setHeader(SetParent); _ColumnAndSize = (uint16)column; _Value3[0] = *(uint64*)(&parent); } /// set parent, only child object has an entityId as key void setParent(TColumnIndex column, const CObjectIndex& parent, const NLMISC::CEntityId& objectId) { setHeader(SetParent); _ColumnAndSize = (uint16)column; _Value3[0] = *(uint64*)(&parent); //_MapTableRow |= MESSAGE_REMAP_ENTITYID_PRESENT; _ObjectIdPresent = true; _ObjectId = objectId; } /// set parent, only parent object has an entityId as key void setParent(TColumnIndex column, const CObjectIndex& parent, const NLMISC::CEntityId& newParentId, const NLMISC::CEntityId& previousParentId) { setHeader(SetParent); _ColumnAndSize = (uint16)column; _Value3[0] = *(uint64*)(&parent); _ColumnAndSize |= MESSAGE_SETPARENT_ENTITYID_PRESENT; _NewParentId = newParentId; _PreviousParentId = previousParentId; } /// set parent, both child and parent objects have an entityId as key void setParent(TColumnIndex column, const CObjectIndex& parent, const NLMISC::CEntityId& objectId, const NLMISC::CEntityId& newParentId, const NLMISC::CEntityId& previousParentId) { setHeader(SetParent); _ColumnAndSize = (uint16)column; _Value3[0] = *(uint64*)(&parent); //_MapTableRow |= MESSAGE_REMAP_ENTITYID_PRESENT; _ObjectIdPresent = true; _ObjectId = objectId; _ColumnAndSize |= MESSAGE_SETPARENT_ENTITYID_PRESENT; _NewParentId = newParentId; _PreviousParentId = previousParentId; } /// Is Object EntityId present bool objectEntityIdPresent() const { //return (_MapTableRow & MESSAGE_REMAP_ENTITYID_PRESENT) != 0; return _ObjectIdPresent; } /// Are Parents EntityId present bool parentsEntityIdPresent() const { return (_ColumnAndSize & MESSAGE_SETPARENT_ENTITYID_PRESENT) != 0; } /// allocate row void allocRow(uint64 key) { setHeader(AllocRow); _Value3[0] = key; } /// deallocate row void deallocRow() { setHeader(DeallocRow); } /// allocate row void allocRow(uint64 key, const NLMISC::CEntityId& objectId) { setHeader(AllocRow); //_MapTableRow |= MESSAGE_REMAP_ENTITYID_PRESENT; _ObjectIdPresent = true; _ObjectId = objectId; _Value3[0] = key; } /// deallocate row void deallocRow(const NLMISC::CEntityId& objectId) { setHeader(DeallocRow); //_MapTableRow |= MESSAGE_REMAP_ENTITYID_PRESENT; _ObjectIdPresent = true; _ObjectId = objectId; } /// load row void loadRow(TTableIndex table, uint64 key) { setHeader(LoadRow); _ObjectIndex.set((uint8)table, 0); //_Table = (uint8)table; _Value3[0] = key; } /// Add string void addString(uint64 skey, const ucstring& str) { setHeader(AddString); _Value3[0] = skey; _String = str; } /// Add string void unmapString(uint64 skey) { setHeader(UnmapString); _Value3[0] = skey; } /// Release a row in memory void releaseRow() { setHeader(ReleaseRow); } /// Log message void log(uint logId, uint bufferByteSize) { setHeader(Log); _LogId = logId; _LogBuffer.resize(bufferByteSize); } /// Push Log Parameter template void pushParameter(uint byteOffset, const T& parameter) { nlassertex(byteOffset+sizeof(T) <= _LogBuffer.size(), ("Internal error! failed to push parameter at %d (size=%d), beyond buffer limit (%d)", byteOffset, sizeof(T), _LogBuffer.size())); memcpy(&(_LogBuffer[byteOffset]), ¶meter, sizeof(parameter)); } /// Push Log Parameter (string) void pushParameter(uint byteOffset, const std::string& parameter) { nlassertex(byteOffset+sizeof(uint16) <= _LogBuffer.size(), ("Internal error! failed to push parameter at %d (size=%d), beyond buffer limit (%d)", byteOffset, sizeof(uint16), _LogBuffer.size())); // get current string index uint16 bo = (uint16)_ExtLogBuffer.size(); _ExtLogBuffer.resize(bo+parameter.size()+1); memcpy(&(_ExtLogBuffer[bo]), parameter.c_str(), parameter.size()+1); memcpy(&(_LogBuffer[byteOffset]), &bo, sizeof(uint16)); } /// Push Log Context void pushContext() { setHeader(PushContext); } /// Pop Log Context void popContext() { setHeader(PopContext); } /// Log Chat sentence void logChat(const ucstring& sentence, const NLMISC::CEntityId& sender, const std::vector& receivers) { setHeader(LogChat); _String = sentence; *(NLMISC::CEntityId*)(&(_Value3[0])) = sender; uint bufferSize = (uint)receivers.size()*sizeof(NLMISC::CEntityId); if (bufferSize > 0) { _LogBuffer.resize(bufferSize); NLMISC::CEntityId* srcBuffer = (NLMISC::CEntityId*)(&(receivers[0])); NLMISC::CEntityId* dstBuffer = (NLMISC::CEntityId*)(&(_LogBuffer[0])); memcpy(dstBuffer, srcBuffer, bufferSize); } else { _LogBuffer.clear(); } } // @} /// Get message type THeaderType getType() const { return _Type; } /// Set Type of message void setType(THeaderType type) { _Type = type; } /// \name common part methods // @{ TTableIndex getTable() const { return (TTableIndex)_ObjectIndex.Table; } TRowIndex getRow() const { return (TRowIndex)_ObjectIndex.Row; } uint32 getStringId() const { return _StringId; } // @} /// \name Update database value specific methods // @{ TColumnIndex getColumn() const { return (TColumnIndex)(_ColumnAndSize&0x3fff); } const void* getData() const { return &_Value0[0]; } uint getDatasize() const { return 1 << (_ColumnAndSize>>14); } uint8 getValue8bits() const { return _Value0[0]; } uint16 getValue16bits() const { return _Value1[0]; } uint32 getValue32bits() const { return _Value2[0]; } uint64 getValue64bits() const { return _Value3[0]; } CObjectIndex getObjectIndex() const { return *(CObjectIndex*)(&(_Value3[0])); } const ucstring& getString() const { return _String; } bool asBool() const { return _Value0[0] != 0; } char asChar() const { return (char)_Value0[0]; } ucchar asUCChar() const { return (ucchar)_Value1[0]; } uint8 asUint8() const { return (uint8)_Value0[0]; } uint16 asUint16() const { return (uint16)_Value1[0]; } uint32 asUint32() const { return (uint32)_Value2[0]; } uint64 asUint64() const { return (uint64)_Value3[0]; } sint8 asSint8() const { return (sint8)_Value0[0]; } sint16 asSint16() const { return (sint16)_Value1[0]; } sint32 asSint32() const { return (sint32)_Value2[0]; } sint64 asSint64() const { return (sint64)_Value3[0]; } float asFloat() const { return *(float*)(&_Value2[0]); } double asDouble() const { return *(double*)(&_Value3[0]); } const NLMISC::CSheetId& asSheetId() const { return *(NLMISC::CSheetId*)(&_Value2[0]); } const NLMISC::CEntityId& asEntityId() const { return *(NLMISC::CEntityId*)(&_Value3[0]); } const NLMISC::CEntityId& getObjectId() const { return _ObjectId; } const NLMISC::CEntityId& getNewParentId() const { return _NewParentId; } const NLMISC::CEntityId& getPreviousParentId() const { return _PreviousParentId; } uint16 getLogId() const { return _LogId; } const std::vector& getLogBuffer() const { return _LogBuffer; } void setupTableAndRow(TTableIndex table, TRowIndex row) { _ObjectIndex.set((uint8)table, (uint32)row); } // @} /// \name Log analysis/display // @{ /// Dump Message content to string as a human readable message void getHRContent(const CDBDescriptionParser& description, std::string& result) const; /// Does message contains CEntityId? bool contains(const CDBDescriptionParser& description, const NLMISC::CEntityId& id); /// Does message contains string? bool contains(const CDBDescriptionParser& description, const std::string& str); /// Build Log string std::string buildLogString(const CDBDescriptionParser& description) const; /// Is Value modified bool valueModified(uint table, uint column) { return ((getType() == UpdateValue || getType() == SetParent) && getTable() == table && getColumn() == column); } /// Is message selected bool Selected; /// Message context depth uint16 ContextDepth; // @} /// \name Serializing // @{ /// Serial message void serial(NLMISC::IStream &f, TObjectIndexCircMapper& indexMapper, TEntityIdCircMapper& eidMapper); /// Get Message Header Size uint32 getMessageHeaderSize(); // @} private: /** * Type of message * Type is not serialised directly in message, but in containing folder */ THeaderType _Type; /** * Message Id * Refers to the 'entity' used/updated by the message */ union // 32 bits { uint32 _StringId; uint16 _LogId; }; /// \name Extra info // @{ uint16 _ColumnAndSize; CMsgObjectIndex _ObjectIndex; union // 64 bits { uint8 _Value0[8]; uint16 _Value1[4]; uint32 _Value2[2]; uint64 _Value3[1]; }; bool _ObjectIdPresent; NLMISC::CEntityId _ObjectId; NLMISC::CEntityId _NewParentId; NLMISC::CEntityId _PreviousParentId; ucstring _String; std::vector _LogBuffer; std::vector _ExtLogBuffer; // @} void setHeader(THeaderType type) { _Type = type; } }; /** * A Folder a Db Messages, all of the same kind. * Based on the assumption that update value messages are the main kind of messages * and that the follow in series... * Thus, it should save one byte per message... */ class CDbMessageFolder { public: CDbMessageFolder() { _Type = 0xff; _NumMessages = 0; } /** * Constructor */ CDbMessageFolder(uint8 type) { _Type = type; _NumMessages = 0; } /** * Get Folder Type */ uint8 getType() const { return _Type; } /** * Get Number of messages in folder */ uint32 getNumMessages() const { return _NumMessages; } /** * Folder is full */ bool full() const { return _NumMessages == MAX_MESSAGE_REMAP; } /** * Add a message to folder */ void addMessage(const CDbMessage& msg) { nlassert(_NumMessages < MAX_MESSAGE_REMAP); nlassert(msg.getType() == _Type); ++_NumMessages; } /** * Serialise folder */ void serial(NLMISC::IStream& f) { f.serial(_Type, _NumMessages); nlassert(_Type < CDbMessage::End); } private: /// Type of messages in folder uint8 _Type; /// Number of messages in folder uint16 _NumMessages; }; /** * A Queue of messages */ class CDbMessageQueue { public: /** * Constructor */ CDbMessageQueue() { clear(); } /** * Clear */ void clear() { _Messages.clear(); _Folders.clear(); } /** * Get Next Message to be written */ CDbMessage& nextMessage() { _Messages.resize(_Messages.size()+1); return _Messages.back(); } /** * Get Current Message to be written */ CDbMessage& currentMessage() { nlassert(!_Messages.empty()); return _Messages.back(); } /** * Get Number of Messages in queue */ uint32 getNumMessages() const { return (uint32)_Messages.size(); } /** * Get Message */ CDbMessage& getMessage(uint32 message) { nlassert(message < _Messages.size()); return _Messages[message]; } /** * Serialise message queue */ void serial(NLMISC::IStream& f) { H_AUTO(PDLIB_MsgQueue_serial); // build folders first if writing to stream if (!f.isReading()) { buildFolders(); } uint32 numFolders = (uint32)_Folders.size(); uint32 numMessages = (uint32)_Messages.size(); f.serial(numFolders); f.serial(numMessages); if (f.isReading()) { _Folders.resize(numFolders); _Messages.resize(numMessages); } //f.serialCont(_BackRemap); TEntityIdCircMapper EIdMapper; TObjectIndexCircMapper IndexMapper; // for each folder, write message stored in it uint i, message = 0; for (i=0; i<_Folders.size(); ++i) { CDbMessageFolder& folder = _Folders[i]; f.serial(folder); uint j; for (j=0; j _Messages; /// List of folders std::vector _Folders; /** * Build message folders */ void buildFolders() { _Folders.clear(); uint i; for (i=0; i<_Messages.size(); ++i) { if (_Folders.empty() || _Folders.back().full() || _Messages[i].getType() != _Folders.back().getType()) _Folders.push_back(CDbMessageFolder(_Messages[i].getType())); _Folders.back().addMessage(_Messages[i]); } } }; /** * A Split Queue * Handle multiple queues, so one update may be splitted into multiple messages */ class CDbMessageSplitQueue { public: /** * Constructor */ CDbMessageSplitQueue() { } /** * Clearup */ void clear() { _Queues.clear(); } /** * Get Next Message to be written, no mapping to be done */ CDbMessage& nextMessage() { if (empty()) forceNextQueue(); return _Queues.back().nextMessage(); } /** * Get Next Remappable Message to be written */ CDbMessage& nextMessage(uint8 table, uint32 row) { if (empty()) forceNextQueue(); // here, queue allows to map message CDbMessage& msg = _Queues.back().nextMessage(); msg.setupTableAndRow(table, row); // and return it return msg; } /** * Get Current Message */ CDbMessage& currentMessage() { return _Queues.back().currentMessage(); } /** * Force MsgQueue to fill next queue */ void forceNextQueue() { if (_Queues.empty() || _Queues.back().getNumMessages() > 0) { _Queues.push_back(CDbMessageQueue()); } } /** * Is Queue Empty? */ bool empty() const { return _Queues.empty(); } /** * Number of message in queue */ uint32 getNumMessagesEnqueued() const { std::list::const_iterator it; uint32 totalMessages = 0; for (it=_Queues.begin(); it!=_Queues.end(); ++it) totalMessages += (*it).getNumMessages(); return totalMessages; } /** * begin() */ std::list::iterator begin() { return _Queues.begin(); } /** * end() */ std::list::iterator end() { return _Queues.end(); } /** * size() */ uint size() const { return (uint)_Queues.size(); } /** * get() */ CDbMessageQueue& get(uint i) { std::list::iterator it = _Queues.begin(); while (i-- > 0) ++it; return (*it); } private: /// Used Queues std::list _Queues; }; class CUpdateLog { public: CUpdateLog() : UpdateId(0xffffffff), _OwnUpdates(false), _Updates(NULL) { } ~CUpdateLog(); /// UpdateId sent by client for this update uint32 UpdateId; /// Start date for this update CTimestamp StartStamp; /// Start date for this update CTimestamp EndStamp; /// Serial log void serial(NLMISC::IStream& f); /// Display UpdateLog content (using a database description) void display(const CDBDescriptionParser& description, NLMISC::CLog& log, bool onlySelected = false); /** * Check log timestamp boundaries */ bool checkTimestampBoundaries(const CTimestamp& begin, const CTimestamp& end); /** * Is Empty */ bool isEmpty(); /** * Select contexts and messages containing a given entityId * return true if there were at least one message selected */ bool selectMessages(const CDBDescriptionParser& description, const NLMISC::CEntityId& id); /** * Select contexts and messages containing a given string * return true if there were at least one message selected */ bool selectMessages(const CDBDescriptionParser& description, const std::string& str); /** * Select contexts and messages containing modification of a value for a given entityId * return true if there were at least one message selected */ bool selectMessages(const CDBDescriptionParser& description, const NLMISC::CEntityId& id, const std::string& valuePath); /** * Select contexts and messages containing a list of entityIds (limited at most to 32 entityIds) * return true if there were at least one message selected */ bool selectMessages(const CDBDescriptionParser& description, const std::vector& ids); class CLogProcessor { public: /// process log, return true if some messages were selected virtual bool processLog(CUpdateLog& log, const CDBDescriptionParser& description) = 0; }; /** * Apply process on log files */ static void processLogs(const std::string& path, const CTimestamp& begin, const CTimestamp& end, NLMISC::CLog& log, CLogProcessor* processor, float* progress = NULL); /** * Display log for a given entity id, between 2 dates */ static void displayLogs(const CDBDescriptionParser& description, const NLMISC::CEntityId& id, const CTimestamp& begin, const CTimestamp& end, const std::string& path, NLMISC::CLog& log, float* progress = NULL); /** * Display log between 2 dates */ static void displayLogs(const std::string& path, const CTimestamp& begin, const CTimestamp& end, NLMISC::CLog& log, float* progress = NULL); /** * Display log for a given entity id, between 2 dates */ static void displayLogs(const std::string& path, const NLMISC::CEntityId& id, const CTimestamp& begin, const CTimestamp& end, NLMISC::CLog& log, float* progress = NULL); /** * Display log for a given entity id and a specified value to be modified, between 2 dates */ static void displayLogs(const std::string& path, const NLMISC::CEntityId& id, const std::string& valuePath, const CTimestamp& begin, const CTimestamp& end, NLMISC::CLog& log, float* progress = NULL); /** * Display log for a list of given entity id, between 2 dates */ static void displayLogs(const std::string& path, const std::vector& ids, const CTimestamp& begin, const CTimestamp& end, NLMISC::CLog& log, float* progress = NULL); /** * Display log for a list of given entity id, between 2 dates */ static void displayLogs(const std::string& path, const std::string& str, const CTimestamp& begin, const CTimestamp& end, NLMISC::CLog& log, float* progress = NULL); /** * Elect matching description */ static std::string electDescription(const std::string& logFile); /** * Set updates */ void setUpdates(CDbMessageQueue* updates); /** * Create updates */ void createUpdates(); /** * Get Updates */ CDbMessageQueue* getUpdates() { return _Updates; } private: bool _OwnUpdates; /// Updates contained in message CDbMessageQueue* _Updates; /// Release Updates (and delete if owned) void releaseUpdates() { if (_OwnUpdates && _Updates != NULL) delete _Updates; _Updates = NULL; _OwnUpdates = false; } }; // // CDbMessage inline methods // inline void CDbMessage::serial(NLMISC::IStream &f, TObjectIndexCircMapper& indexMapper, TEntityIdCircMapper& eidMapper) { switch (_Type) { case UpdateValue: { uint8 flags = (objectEntityIdPresent() ? 0x80 : 0); indexMapper.serial(f, _ObjectIndex, flags); _ObjectIdPresent = ((flags & 0x80) != 0); f.serial(_ColumnAndSize); switch (_ColumnAndSize & 0xc000) { case 0x0000: f.serial(_Value0[0]); break; case 0x4000: f.serial(_Value1[0]); break; case 0x8000: f.serial(_Value2[0]); break; case 0xc000: f.serial(_Value3[0]); break; } // serial owner CEntityId if present if (objectEntityIdPresent()) eidMapper.serial(f, _ObjectId); } break; case SetParent: { uint8 flags = (objectEntityIdPresent() ? 0x80 : 0); indexMapper.serial(f, _ObjectIndex, flags); _ObjectIdPresent = ((flags & 0x80) != 0); f.serial(_ColumnAndSize); f.serial(_Value3[0]); // serial object CEntityId if present if (objectEntityIdPresent()) eidMapper.serial(f, _ObjectId); // serial parents CEntityId if present if ((_ColumnAndSize & MESSAGE_SETPARENT_ENTITYID_PRESENT) != 0) { eidMapper.serial(f, _NewParentId); eidMapper.serial(f, _PreviousParentId); } } break; case AllocRow: { uint8 flags = (objectEntityIdPresent() ? 0x80 : 0); indexMapper.serial(f, _ObjectIndex, flags); _ObjectIdPresent = ((flags & 0x80) != 0); f.serial(_Value3[0]); // serial owner CEntityId if present if (objectEntityIdPresent()) eidMapper.serial(f, _ObjectId); } break; case DeallocRow: { uint8 flags = (objectEntityIdPresent() ? 0x80 : 0); indexMapper.serial(f, _ObjectIndex, flags); _ObjectIdPresent = ((flags & 0x80) != 0); // serial owner CEntityId if present if (objectEntityIdPresent()) eidMapper.serial(f, _ObjectId); } break; case LoadRow: f.serial(_ObjectIndex.Table); f.serial(_Value3[0]); break; case AddString: f.serial(_Value3[0]); f.serial(_String); break; case UnmapString: f.serial(_Value3[0]); break; case ReleaseRow: indexMapper.serial(f, _ObjectIndex); break; case Log: { f.serial(_LogId); if (f.isReading()) { uint8 sz; f.serial(sz); _LogBuffer.resize(sz); if (sz > 0) f.serialBuffer(&(_LogBuffer[0]), sz); f.serial(sz); _ExtLogBuffer.resize(sz); if (sz > 0) f.serialBuffer(&(_ExtLogBuffer[0]), sz); } else { uint8 sz; nlassert(_LogBuffer.size() <= 255); sz = (uint8)_LogBuffer.size(); f.serial(sz); if (sz > 0) f.serialBuffer(&(_LogBuffer[0]), sz); nlassert(_ExtLogBuffer.size() <= 255); sz = (uint8)_ExtLogBuffer.size(); f.serial(sz); if (sz > 0) f.serialBuffer(&(_ExtLogBuffer[0]), sz); } } break; case PushContext: break; case PopContext: break; case LogChat: // serial chat sentence f.serial(_String); // serial sender f.serial(_Value3[0]); // serial receivers list (whole buffer as uint8*) f.serialCont(_LogBuffer); break; default: nlerror("CDbMessage::serial(): unable to serial message type '%d'", _Type); break; } } /* * Get Message Header Size */ inline uint32 CDbMessage::getMessageHeaderSize() { uint size = 0; switch (_Type) { case UpdateValue: case SetParent: size += sizeof(_ObjectIndex.Table)+sizeof(_ObjectIndex.Row); size += sizeof(_ColumnAndSize); break; case AllocRow: size += sizeof(_ObjectIndex.Table)+sizeof(_ObjectIndex.Row); break; case DeallocRow: size += sizeof(_ObjectIndex.Table)+sizeof(_ObjectIndex.Row); break; case LoadRow: size += sizeof(_ObjectIndex.Table); size += sizeof(_Value3[0]); break; case ReleaseRow: size += sizeof(_ObjectIndex.Table)+sizeof(_ObjectIndex.Row); break; case Log: size += sizeof(_LogId); size += 2; break; default: break; } return size; } /* * Serial log */ inline void CUpdateLog::serial(NLMISC::IStream& f) { f.serialCheck(NELID("ULOG")); uint version = f.serialVersion(1); f.serial(UpdateId); if (version >= 1) { f.serial(StartStamp); f.serial(EndStamp); } if (f.isReading()) { releaseUpdates(); _Updates = new RY_PDS::CDbMessageQueue(); _OwnUpdates = true; } f.serial(*_Updates); } }; // RY_PDS #endif //RY_PD_MESSAGES_H