// 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 . //----------------------------------------------------------------------------- // includes //----------------------------------------------------------------------------- #include "stdpch.h" #include "object.h" #include "nel/misc/debug.h" #include "nel/misc/singleton.h" #include "nel/misc/string_common.h" #include "game_share/utils.h" #include #include "nel/misc/bit_mem_stream.h" #include #include #include #include #include #include namespace R2 { class CSerializeContext { public: CSerializeContext():_Indent(0){} sint32 getIndent() const { return _Indent;} void add() { ++_Indent; } void sub() { --_Indent; } private: sint32 _Indent; }; class CTableIntegrityChecker { public: CTableIntegrityChecker(const CObjectTable &table) : _Table(table) { _Table.checkIntegrity(); } ~CTableIntegrityChecker() { _Table.checkIntegrity(); } private: const CObjectTable &_Table; }; #ifdef NL_DEBUG #define CHECK_TABLE_INTEGRITY CTableIntegrityChecker __cti(*this); #else #define CHECK_TABLE_INTEGRITY #endif //CObjectFactory *CObjectSerializer::Factory = NULL; CObjectFactory *CObjectSerializerClient::_ClientObjecFactory = NULL; static CObject *newTable(CObjectFactory *factory) { if (factory) { return factory->newBasic("Table"); } else { return new CObjectTable; } } //----------------------- private implementation stuffs ---------------------------------------- static void addTab(std::string& out, sint32 n) { for (sint32 i = 0 ; i < n; ++i) { out += " "; } } //----------------------- CObject ---------------------------------------- CObject::~CObject() { BOMB_IF(_Validation != 0x01020304, "Error (double delete)?", return); _Validation = 0; } CObject::CObject() { _Parent = 0; _Ghost = false; _Validation = 0x01020304; } void CObject::previsit(std::vector &sons) { sons.push_back(this); } void CObject::visit(IObjectVisitor &visitor) { //H_AUTO(R2_CObjectTable_visit) std::vector sons; // must work on a copy here, because the 'visit' method may change the current list of sons of this table // Example of a scenario where this happened : // - Create a bandit camp // - onPostCreate (1) is called (see in client : CInstance::onPostCreate) // - create ghost bandit as sons of current bandit camps, their 'onPostCreate' method is called // - returns to previous onPostCreate (1) -> the list of sons has changed // -> as a result, onPostCreated was called twice on instances that were just created, leading to a crash (the onPostCreate method registered // the new object in some manager, and this registration is only allowed once per object) previsit(sons); for(uint k = 0; k < sons.size(); ++k) { if (sons[k]) // may become NULL if son was deleted during the visit callback { sons[k]->visitInternal(visitor); } } } void CObject::inPlaceCopy(const CObject &src) { //H_AUTO(R2_CObject_inPlaceCopy) src.inPlaceCopyTo(*this); } bool CObject::getGhost() const { //H_AUTO(R2_CObject_getGhost) return _Ghost; } void CObject::setGhost(bool ghost) { //H_AUTO(R2_CObject_setGhost) _Ghost = ghost; } void CObject::copyMismatchMsg(const CObject &src) { //H_AUTO(R2_CObject_copyMismatchMsg) nlwarning("Can't copy object of type %s into object of type %s", src.getTypeAsString(), this->getTypeAsString()); } void CObject::inPlaceCopy(const CObjectString &src) { //H_AUTO(R2_CObject_inPlaceCopy) copyMismatchMsg(src); } void CObject::inPlaceCopy(const CObjectNumber &src) { //H_AUTO(R2_CObject_inPlaceCopy) copyMismatchMsg(src); } void CObject::inPlaceCopy(const CObjectTable &src) { //H_AUTO(R2_CObject_inPlaceCopy) copyMismatchMsg(src); } bool CObject::isNumber(const std::string & prop) const { //H_AUTO(R2_CObject_isNumber) if (!prop.empty()) { CObject* attr = getAttr(prop); if (!attr) return false; return attr->doIsNumber(); } return doIsNumber(); } bool CObject::isString(const std::string & prop) const { //H_AUTO(R2_CObject_isString) if (!prop.empty()) { CObject* attr = getAttr(prop); if (!attr) return false; return attr->doIsString(); } return doIsString(); } sint32 CObject::findIndex(const std::string &/* key */) const { //H_AUTO(R2_CObject_findIndex) BOMB("Try to use the method findIndex() on a object that is not an CObjectTable", return 0); return 0; } bool CObject::isTable(const std::string & prop) const { //H_AUTO(R2_CObject_isTable) if (!prop.empty()) { CObject* attr = getAttr(prop); if (!attr) return false; return attr->isTable(); } return doIsTable(); } bool CObject::isRefId(const std::string & prop /*=""*/) const { //H_AUTO(R2_CObject_isRefId) if (!prop.empty()) { CObject* attr = getAttr(prop); if (!attr) return false; return attr->isTable(); } return doIsRefId(); } bool CObject::doIsNumber() const { //H_AUTO(R2_CObject_doIsNumber) return false; } bool CObject::doIsString() const { //H_AUTO(R2_CObject_doIsString) return false; } bool CObject::doIsTable() const { //H_AUTO(R2_CObject_doIsTable) return false; } bool CObject::doIsRefId() const { //H_AUTO(R2_CObject_doIsRefId) return false; } double CObject::toNumber(const std::string & prop) const { //H_AUTO(R2_CObject_toNumber) if (!prop.empty()) { CObject* attr = getAttr(prop); if (!attr) { BOMB("Try to use the method toNumber() on a NULL Object", return 0); } return attr->doToNumber(); } return doToNumber(); } std::string CObject::toString(const std::string & prop) const { //H_AUTO(R2_CObject_toString) if (!prop.empty()) { CObject* attr = getAttr(prop); if (!attr) { BOMB(NLMISC::toString("Try to access to the property '%s' that does not exist.",prop.c_str()) , return ""); return ""; } return attr->doToString(); } return doToString(); } CObjectTable* CObject::toTable(const std::string & prop) const { //H_AUTO(R2_CObject_toTable) if (!prop.empty()) { CObject* attr = getAttr(prop); if (!attr) { BOMB("Try to use the method toTable() on a NULL Object", return 0); } return attr->doToTable(); } return doToTable(); } CObject* CObject::take(sint32 /* position */) { //H_AUTO(R2_CObject_take) BOMB("Try to use the take function on an object that is not a table", return 0); return 0; } bool CObject::canTake(sint32 /* position */) const { //H_AUTO(R2_CObject_canTake) return false; } double CObject::doToNumber() const { //H_AUTO(R2_CObject_doToNumber) BOMB("Try to convert an objet to number without being allowed", return 0); return 0; } std::string CObject::doToString() const { //H_AUTO(R2_CObject_doToString) BOMB("Try to convert an objet to string without being allowed", return ""); return ""; } CObjectTable* CObject::doToTable() const { //H_AUTO(R2_CObject_doToTable) BOMB("Try to convert an objet to string without being allowed", return 0); return 0; } CObject* CObject::getAttr(const std::string & /* name */) const { return 0;} std::string CObject::getKey(uint32 /* pos */) const{ BOMB("Try to call the function getKey() on an object that is not a table", return ""); return "";} CObject* CObject::getValue(uint32 /* pos */) const{ BOMB("Try to call the function getValue() on an object that is not a table", return 0); return 0;} uint32 CObject::getSize() const { BOMB("Try to call the function getSize() on an object that is not a table", return 0); return 0; } CObject* CObject::clone() const { BOMB("Try to call the function clone() on an object that is not a table", return 0); return 0;} void CObject::add(const std::string & key, CObject* value) { //H_AUTO(R2_CObject_add) insert(key, value, -1); } void CObject::add(CObject* value){ add("", value); } bool CObject::set(const std::string& /* key */, const std::string & /* value */) { BOMB("Try to set the value of an object with a string on an object that does not allowed it", return false); return false; } bool CObject::set(const std::string& /* key */, double /* value */){ BOMB("Try to set the value of an object with a double on an object that does not allowed it", return false); return false; } bool CObject::setObject(const std::string& /* key */, CObject* /* value */) { BOMB("Try to set the value of an object with an object that does not allowed it", return false); return false; } void CObject::serialize(std::string& out) const { //H_AUTO(R2_CObject_serialize) CSerializeContext context; doSerialize(out, context); } bool CObject::insert( const std::string& /* key */, CObject* /* value */, sint32 /* position */) { //H_AUTO(R2_CObject_insert) BOMB("Try to call the function insert() on an object that is not a table", return false); return false; } CObject* CObject::getParent() const { //H_AUTO(R2_CObject_getParent) return _Parent; } void CObject::setParent(CObject* parent) { //H_AUTO(R2_CObject_setParent) _Parent = parent; } void CObject::add(const std::string& key, const std::string & value) { //H_AUTO(R2_CObject_add) this->add(key, new CObjectString(value)); } void CObject::add(const std::string& key, double value) { //H_AUTO(R2_CObject_add) this->add(key, new CObjectNumber(value)); } CObject* CObject::findAttr(const std::string & first) const { //H_AUTO(R2_CObject_findAttr) return getAttr(first); } CObject* CObject::findAttr(const std::string & first, const std::string & second) const { //H_AUTO(R2_CObject_findAttr) CObject* ret = getAttr(first); if (ret) { ret = ret->getAttr(second); } return ret; } CObject* CObject::findAttr(const std::string & first, const std::string & second, const std::string & third) const { //H_AUTO(R2_CObject_findAttr) CObject* ret = getAttr(first); if (ret) { ret = ret->getAttr(second); } if (ret) { ret = ret->getAttr(third); } return ret; } CObject* CObject::findAttr(const std::vector& attrNames ) const { //H_AUTO(R2_CObject_findAttr) std::vector::const_iterator first(attrNames.begin()), last(attrNames.end()); CObject* ret = const_cast(this); for ( ; first != last && ret; ++first) { if (ret) { ret = ret->getAttr(*first); } } return ret; } sint32 CObject::findIndex(const CObject* /* child */) const { //H_AUTO(R2_CObject_findIndex) BOMB("Try to call the function findIndex() on an object that is not a table", return 0); return 0; } bool CObject::getShortestName(std::string &instanceId, std::string &attrName, sint32 &position) const { //H_AUTO(R2_CObject_getShortestName) if (isTable() && getAttr("InstanceId")) { instanceId = getAttr("InstanceId")->toString(); attrName = ""; position = -1; return true; } CObject *parent = getParent(); if (!parent) { nlassert(0); // TMP : want to see if may possibly happen return false; } if (parent->isTable() && parent->getAttr("InstanceId")) { sint32 index = parent->findIndex(this); nlassert(index != -1); instanceId = parent->getAttr("InstanceId")->toString(); attrName = parent->getKey(index); if (attrName == "") { position = index; } return true; } CObject *parent2 = parent->getParent(); if (!parent2) { nlassert(0); // TMP : want to see if may possibly happen return false; } if (parent2->isTable() && parent2->getAttr("InstanceId")) { sint32 index2 = parent2->findIndex(parent); nlassert(index2 != -1); sint32 index = parent->findIndex(this); nlassert(index != -1); if (parent2->getKey(index2) == "") { nlassert(0); // TMP : want to see if may possibly happen return false; } instanceId = parent2->getAttr("InstanceId")->toString(); attrName == parent2->getKey(index2); position = index; return true; } return false; } bool CObject::getNameInParent(std::string &instanceId, std::string &attrName, sint32 &position) const { //H_AUTO(R2_CObject_getNameInParent) CObject *currParent = this->getParent(); if (!currParent) return false; if (currParent->findAttr("InstanceId")) { // i'm a property in my parent instanceId = currParent->findAttr("InstanceId")->toString(); position = -1; attrName = currParent->getKey(currParent->findIndex(this)); return true; } sint32 tmpPosition = currParent->findIndex(this); CObject *nextParent = currParent->getParent(); if (!nextParent) return false; if (nextParent->findAttr("InstanceId")) { instanceId = nextParent->findAttr("InstanceId")->toString(); // i'm a property in my parent position = tmpPosition; attrName = nextParent->getKey(nextParent->findIndex(currParent)); return true; } return false; } //----------------------- CObjectString ---------------------------------------- CObjectString::CObjectString(const std::string & value) : CObject(), _Value(value){} CObjectRefId::~CObjectRefId() { //nlwarning("# Destroying CObjectString 0x%x", (int) this); } const char *CObjectString::getTypeAsString() const { //H_AUTO(R2_CObjectString_getTypeAsString) return "String"; } void CObjectString::visitInternal(IObjectVisitor &visitor) { //H_AUTO(R2_CObjectString_visit) visitor.visit(*this); } void CObjectString::doSerialize(std::string& out, CSerializeContext& /* context */) const { //H_AUTO(R2_CObjectString_doSerialize) nlassert(!getGhost()); std::string copy; std::string::size_type first(0), last(_Value.size()); for (; first != last ; ++first) { char c = _Value[first]; if (c != ']' && c != '[') { copy += c; } else { copy += NLMISC::toString("]]..\"%c\"..[[", c); } } out += "[[" + copy + "]]"; } bool CObjectString::set(const std::string& key, const std::string & value) { //H_AUTO(R2_CObjectString_set) BOMB_IF( key != "", "Try to set the a sub value of an object that does not allowed it", return false); _Value = value; return true; } bool CObjectString::setObject(const std::string& key, CObject* value) { //H_AUTO(R2_CObjectString_setObject) BOMB_IF( key != "" || ! (value->isString() || value->isNumber()) , "Try to set the a sub value of an object that does not allowed it", return false); bool canSet = set(key, value->toString()); if (canSet) { setGhost(value->getGhost()); } return canSet; } void CObjectString::inPlaceCopyTo(CObject &dest) const { //H_AUTO(R2_CObjectString_inPlaceCopyTo) dest.inPlaceCopy(*this); } void CObjectString::inPlaceCopy(const CObjectString &src) { //H_AUTO(R2_CObjectString_inPlaceCopy) _Value = src._Value; setGhost(src.getGhost()); } std::string CObjectString::doToString() const { return _Value;} CObject* CObjectString::clone() const { //H_AUTO(R2_CObjectString_clone) CObjectString *result = new CObjectString(_Value); result->setGhost(getGhost()); return result; } bool CObjectString::doIsString() const { return true;} bool CObjectString::equal(const CObject* other) const { //H_AUTO(R2_CObjectString_equal) if (!other || !other->isString()) return false; std::string otherValue = other->toString(); return _Value == otherValue; } //----------------------- CObjectRefId ---------------------------------------- CObjectRefId::CObjectRefId(const std::string & value) : CObjectString(value) { } const char *CObjectRefId::getTypeAsString() const { //H_AUTO(R2_CObjectRefId_getTypeAsString) return "RefId"; } CObject* CObjectRefId::clone() const { //H_AUTO(R2_CObjectRefId_clone) return new CObjectRefId(*this); } void CObjectRefId::visitInternal(IObjectVisitor &visitor) { //H_AUTO(R2_CObjectRefId_visit) visitor.visit(*this); } bool CObjectRefId::equal(const CObject* other) const { //H_AUTO(R2_CObjectRefId_equal) if (!other || !other->isRefId()) return false; std::string otherValue = other->toString(); if (getValue() == otherValue ) return true; return false; } bool CObjectRefId::doIsRefId() const { //H_AUTO(R2_CObjectRefId_doIsRefId) return true; } void CObjectRefId::doSerialize(std::string& out, CSerializeContext& /* context */) const { //H_AUTO(R2_CObjectRefId_doSerialize) nlassert(!getGhost()); //out << "r2.RefId([[" << getValue() << "]])"; out += "r2.RefId([[" + getValue() + "]])"; } //----------------------- CObjectNumber ---------------------------------------- CObjectNumber::CObjectNumber(double value) : CObject(), _Value(value){} const char *CObjectNumber::getTypeAsString() const { return "Number"; } void CObjectNumber::visitInternal(IObjectVisitor &visitor) { //H_AUTO(R2_CObjectNumber_visit) visitor.visit(*this); } void CObjectNumber::inPlaceCopyTo(CObject &dest) const { //H_AUTO(R2_CObjectNumber_inPlaceCopyTo) dest.inPlaceCopy(*this); } void CObjectNumber::inPlaceCopy(const CObjectNumber &src) { //H_AUTO(R2_CObjectNumber_inPlaceCopy) _Value = src._Value; setGhost(src.getGhost()); } std::string CObjectNumber::doToString() const { return NLMISC::toString("%d", _Value);} void CObjectNumber::doSerialize(std::string& out, CSerializeContext& /* context */) const { //H_AUTO(R2_CObjectNumber_doSerialize) nlassert(!getGhost()); //out.precision(15); std::string value = NLMISC::toString(double(sint64(_Value * 1000.0 + (_Value>=0.0?0.5:-0.5)))/1000.0); // search for first not 0 value from the end std::string::size_type pos = value.find_last_not_of('0'); if (pos != std::string::npos) { // don't remove character at pos if it's another digit if (value[pos] != '.') ++pos; value.erase(pos); } out += value; } bool CObjectNumber::set(const std::string& key, double value) { //H_AUTO(R2_CObjectNumber_set) BOMB_IF(key != "", "Try to set an element of a table on an object that is not a table", return false); _Value = value; return true; } bool CObjectNumber::set(const std::string& key, const std::string & value) { //H_AUTO(R2_CObjectNumber_set) //XXX BOMB_IF(key != "", "Try to set an element of a table on an object that is not a table", return false); // std::stringstream ss ; // ss << value; // ss >> _Value; NLMISC::fromString(value, _Value); return true; } double CObjectNumber::doToNumber() const { return _Value; } CObject* CObjectNumber::clone() const { //H_AUTO(R2_CObjectNumber_clone) CObjectNumber *result = new CObjectNumber(_Value); result->setGhost(getGhost()); return result; } bool CObjectNumber::doIsNumber() const { return true;} bool CObjectNumber::setObject(const std::string& /* key */, CObject* value) { //H_AUTO(R2_CObjectNumber_setObject) BOMB_IF(!value->isNumber(), NLMISC::toString("Try to set an element of a type '%s' with a value of type '%s' on an object that is not a number", this->getTypeAsString(), value->getTypeAsString()), return false); _Value = value->toNumber(); setGhost(value->getGhost()); return true; } bool CObjectNumber::equal(const CObject* other) const { //H_AUTO(R2_CObjectNumber_equal) if (!other || !other->isNumber()) return false; double otherValue = other->toNumber(); if (_Value == otherValue ) return true; /* fabs + epsilon trick */ return false; } //----------------------- CObjectTable ---------------------------------------- CObjectTable::CObjectTable() : CObject(){} void CObjectTable::checkIntegrity() const { //H_AUTO(R2_CObjectTable_checkIntegrity) static volatile bool testWanted = true; if (!testWanted) return; if (_Ghost) { for(uint k = 0; k < getSize(); ++k) { CObject *subObj = getValue(k); if (subObj) { if (!subObj->getGhost()) { // dump whole hierarchy const CObject *topParent = this; while (topParent->getParent()) topParent = topParent->getParent(); topParent->dump(); nlwarning("Check Integrity failed."); return; } subObj->checkIntegrity(); } } } } void CObjectTable::setGhost(bool ghost) { //H_AUTO(R2_CObjectTable_setGhost) CHECK_TABLE_INTEGRITY _Ghost = ghost; CObject* parent = this->getParent(); if (!ghost && parent) { sint32 index = parent->findIndex(this); if (index != -1) { std::string key = parent->getKey( static_cast(index)); if (key == "Ghosts" ) { return; } } } for(uint k = 0; k < getSize(); ++k) { CObject *subObj = getValue(k); if (subObj) { subObj->setGhost(ghost); } } } void CObjectTable::previsit(std::vector &sons) { sons.push_back(this); sons.reserve(sons.size() + getSize()); for(uint k = 0; k < getSize(); ++k) { CObject *subObj = getValue(k); if (subObj) { subObj->previsit(sons); } } } void CObjectTable::visitInternal(IObjectVisitor &visitor) { visitor.visit(*this); } void CObjectTable::inPlaceCopyTo(CObject &dest) const { //H_AUTO(R2_CObjectTable_inPlaceCopyTo) CHECK_TABLE_INTEGRITY dest.inPlaceCopy(*this); } void CObjectTable::inPlaceCopy(const CObjectTable &src) { //H_AUTO(R2_CObjectTable_inPlaceCopy) CHECK_TABLE_INTEGRITY for(uint k = 0; k < src.getSize(); ++k) { if (!src.getKey(k).empty()) { CObject *dest = getAttr(src.getKey(k)); if (!dest) { nlwarning("No dest object %s found when copying in place", src.getKey(k).c_str()); } else { src.getValue(k)->inPlaceCopyTo(*dest); } } else { nlwarning("In place copy of objects with a number as key not supported"); } } } CObjectTable::~CObjectTable() { { CHECK_TABLE_INTEGRITY } TContainer::iterator first(_Value.begin()), last(_Value.end()); for ( ;first != last; ++first ) { delete first->second; first->second = 0; } _Value.clear(); } const char *CObjectTable::getTypeAsString() const { //H_AUTO(R2_CObjectTable_getTypeAsString) return "Table"; } //complexity less than void CObjectTable::sort() { //H_AUTO(R2_CObjectTable_sort) CHECK_TABLE_INTEGRITY std::vector < std::pair > data; std::vector keyVector; CObject* keys = getAttr("Keys"); if (!keys || !keys->isTable()) return; uint32 firstKey = 0; uint32 lastKey = keys->getSize(); for (; firstKey != lastKey ; ++firstKey) { CObject* keyObject = keys->getValue(firstKey); if (! keyObject->isString()) return; std::string key = keyObject->toString(); uint32 firstValue = 0; uint32 lastValue = (uint32)_Value.size(); for (; firstValue != lastValue; ++firstValue) { if ( key == _Value[firstValue].first) { data.push_back( _Value[firstValue]); _Value[firstValue].second = 0; } } } { uint32 firstValue = 0; uint32 lastValue = (uint32)_Value.size(); for (; firstValue != lastValue; ++firstValue) { if ( _Value[firstValue].first != "Keys" && _Value[firstValue].second != 0) { data.push_back( _Value[firstValue]); } } } _Value.swap(data); delete keys; } CObject* CObjectTable::clone() const { //H_AUTO(R2_CObjectTable_clone) CHECK_TABLE_INTEGRITY CObject* ret = new CObjectTable(); TContainer::const_iterator first(_Value.begin()), last(_Value.end()); for ( ;first != last; ++first ) { BOMB_IF(!first->second, "Try to clone a table with an NULL component", return 0) nlassert(first->second->getGhost() == this->getGhost()); CObject* clone = first->second->clone(); if (clone) { clone->setParent(0); } ret->add(first->first, clone); } ret->setGhost(getGhost()); #ifdef NL_DEBUG ret->checkIntegrity(); #endif return ret; } namespace { struct CValueIndex { CValueIndex(unsigned int index, const std::string& name, bool isTable, bool isDefaultFeature) :Index(index), Name(name), IsTable(isTable), IsDefaultFeature(isDefaultFeature){} bool operator < (const CValueIndex& rh) const { // container after values if ( !IsTable && rh.IsTable ) { return true; } if ( IsTable && !rh.IsTable ) { return false; } if ( !IsTable) { if ( Name == "InstanceId" && (rh.Name != "InstanceId" ) ) { return true;} if ( Name != "InstanceId" && (rh.Name == "InstanceId" ) ) { return false;} if ( Name == "Class" && (rh.Name != "Class" ) ) { return true;} if ( Name != "Class" && (rh.Name == "Class" ) ) { return false;} if ( Name == "Version" && (rh.Name != "Version" ) ) { return true;} if ( Name != "Version" && (rh.Name == "Version" ) ) { return false;} } if (IsTable && rh.IsTable) { if (IsDefaultFeature) { return true; } if (rh.IsDefaultFeature) { return false; } return Name < rh.Name; } return Name < rh.Name; } uint32 Index; std::string Name; bool IsTable; bool IsDefaultFeature; }; } void CObjectTable::doSerialize(std::string& out, CSerializeContext& context) const { //H_AUTO(R2_CObjectTable_doSerialize) std::vector indexs; uint32 i = 0; uint32 size = (uint32)_Value.size(); indexs.reserve(size); for (; i < size ; ++i) { bool isDefault= false; if (_Value[i].second->isTable() && _Value[i].second->isString("Class")) { std::string cl = _Value[i].second->toString("Class"); if (cl == "DefaultFeature") { isDefault = true; } else if ( cl == "Act") { if ( _Value[i].second->isString("Name") && _Value[i].second->isString("LocationId") && _Value[i].second->toString("Name") == "Permanent" && _Value[i].second->toString("LocationId") == "") { isDefault = true; } } } indexs.push_back( CValueIndex(i, _Value[i].first, _Value[i].second->isTable(), isDefault) ); } std::stable_sort(indexs.begin(), indexs.end()); CHECK_TABLE_INTEGRITY if (getGhost()) { nlwarning("Try to serialize a ghost Component"); return; } nlassert(!getGhost()); sint32 indent = context.getIndent(); out += "{"; uint32 j = 0; context.add(); for (; j < size ; ++j) { uint32 i = indexs[j].Index; static const std::string ghostStr = "Ghost_"; static const std::string::size_type ghostStrSize = ghostStr.size(); std::string key = _Value[i].first; if (!_Value[i].second->getGhost() && !(key.size() > ghostStrSize && key.substr(0, ghostStrSize) == ghostStr)) { out += "\n"; addTab(out, context.getIndent()); if (!_Value[i].first.empty()) { out += _Value[i].first + " = "; } _Value[i].second->doSerialize(out, context); // if (j != size -1) { out += ", "; } } } context.sub(); out += "\n"; addTab(out, indent); out += "}"; } CObject* CObjectTable::getAttr(const std::string & name) const { //H_AUTO(R2_CObjectTable_getAttr) CHECK_TABLE_INTEGRITY //search by position //XXX if (name.size() >= 1 && '0' <= name[0] && name[0] <='9') { uint32 first2 = 0; uint32 end2 = (uint32)name.size(); for ( ; first2 != end2 && '0' <= name[first2] && name[first2] <= '9'; ++first2) {} if (first2 == end2) { uint32 position; NLMISC::fromString(name, position); if (position < _Value.size()) { return _Value[position].second; } } } //search by name TContainer::const_iterator first(_Value.begin()); TContainer::const_iterator last(_Value.end()); for (; first != last ;++first) { if (first->first == name) { return first->second; } } return 0; } std::string CObjectTable::getKey(uint32 pos) const { //H_AUTO(R2_CObjectTable_getKey) CHECK_TABLE_INTEGRITY if (pos >= _Value.size()) { nlwarning(" bad index %d", pos); return ""; } return _Value[pos].first; } CObject* CObjectTable::getValue(uint32 pos) const { //H_AUTO(R2_CObjectTable_getValue) if (pos >= _Value.size()) { nlwarning(" bad index %d", pos); return NULL; } return _Value[pos].second; } bool CObjectTable::set(const std::string& key, const std::string & value) { //H_AUTO(R2_CObjectTable_set) CHECK_TABLE_INTEGRITY CObject *attr = getAttr(key); if (!attr) { CObject *str = new CObjectString(value); str->setGhost(this->getGhost()); this->add(key, str); //nlwarning("Can't find attribute '%s' in object of class '%s'", key.c_str(), toString("Class").c_str()); return true; } return attr->set("", value); } bool CObjectTable::set(const std::string& key, double value) { //H_AUTO(R2_CObjectTable_set) CHECK_TABLE_INTEGRITY CObject *attr = getAttr(key); if (!attr) { //nlwarning("Can't find attribute '%s' in object of class '%s'", key.c_str(), toString("Class").c_str()); CObject *nbr = new CObjectNumber(value); nbr->setGhost(this->getGhost()); this->add(key, nbr); return true; } return attr->set("", value); } bool CObjectTable::setObject(const std::string& key, CObject* value) { //H_AUTO(R2_CObjectTable_setObject) CHECK_TABLE_INTEGRITY value->setGhost(this->getGhost()); if (key == "") { clear(); uint32 first = 0; CObject* table = value; //check uint32 last = table->getSize(); for ( ; first != last ; ++first) { std::string key1 = table->getKey(first); CObject* value1 = table->getValue(first); add(key1, value1->clone()); } return true; //getAttrId //remove } else { CObject* attr= getAttr(key); if (!attr) { this->add(key, value->clone()); return true; } if (attr){ return attr->setObject("", value); } } return false; } void CObjectTable::clear() { //H_AUTO(R2_CObjectTable_clear) CHECK_TABLE_INTEGRITY TContainer::iterator first(_Value.begin()); TContainer::iterator last(_Value.end()); for (; first != last ;++first) { delete first->second; } _Value.clear(); } uint32 CObjectTable::getSize() const { //H_AUTO(R2_CObjectTable_getSize) return (uint32)_Value.size(); } bool CObjectTable::doIsTable() const { return true;} CObjectTable* CObjectTable::doToTable() const { return const_cast(this);} sint32 CObjectTable::findIndex(const CObject* child) const { //H_AUTO(R2_CObjectTable_findIndex) CHECK_TABLE_INTEGRITY uint32 first = 0, last = (uint32)_Value.size(); for (; first != last && _Value[first].second != child ; ++first){} if (first == last) return -1; return first; } sint32 CObjectTable::findIndex(const std::string &key) const { //H_AUTO(R2_CObjectTable_findIndex) CHECK_TABLE_INTEGRITY uint32 first = 0, last = (uint32)_Value.size(); for (; first != last && _Value[first].first != key ; ++first){} if (first == last) return -1; return first; } CObject* CObjectTable::take(sint32 position) { //H_AUTO(R2_CObjectTable_take) CHECK_TABLE_INTEGRITY BOMB_IF(!( -1 <= position && position < static_cast(_Value.size())), "Try to take an element that does not exist", return 0); if (0 <= position && position < static_cast(_Value.size())) { CObject* child = _Value[ static_cast(position) ].second; _Value.erase(_Value.begin() + static_cast(position)); child->setParent(0); return child; } else if (position == -1) { CObject* parent = getParent(); if (!parent) { return this; } uint32 pos = getParent()->findIndex(this); BOMB_IF(pos >= getParent()->getSize(), "Try to take an element that does not exist", return 0); CObject* child = getParent()->take(pos); return child; } return 0; } bool CObjectTable::canTake(sint32 position) const { //H_AUTO(R2_CObjectTable_canTake) CHECK_TABLE_INTEGRITY if(!( -1 <= position && position < static_cast(_Value.size()))) { return false; } if (position == -1) { CObject* parent = getParent(); if (parent) //try to take the root of a tree { return true; } uint32 pos = parent->findIndex(this); BOMB_IF(pos >= getParent()->getSize(), "Try to take an element that does not exist", return false); return getParent()->canTake(pos); } return true; } bool CObjectTable::insert(const std::string& key, CObject* value, sint32 position) { //H_AUTO(R2_CObjectTable_insert) CHECK_TABLE_INTEGRITY uint32 count = (uint32)_Value.size(); BOMB_IF(!( -1 <= position && position <= static_cast(count)), "Try to take an element that does not exist", return false); BOMB_IF(!value, "Try to insert a Null value", return false); BOMB_IF(value->getParent() != 0, "Try to insert an element that not at the root of the tree.", return false); value->setParent(this); // inherit the 'ghost' flag if (this->getGhost()) { value->setGhost(true); } if (0<= position && position < static_cast(count)) { _Value.insert(_Value.begin() + position, std::pair(key, value)); } else { _Value.push_back(std::pair(key, value)); } return true; } bool CObjectTable::equal(const CObject* other) const { //H_AUTO(R2_CObjectTable_equal) CHECK_TABLE_INTEGRITY if (!other || !other->isTable()) return false; #ifdef NL_DEBUG other->checkIntegrity(); #endif uint32 size; size = getSize(); if (size != other->getSize()) return false; uint i = 0; for ( i=0; i!= size ; ++i ) { std::string key = other->getKey(i); CObject* value = other->getValue(i); if (key != this->getKey(i)) return false; if ( !value->equal(this->getValue(i))) return false; } return true; } //----------------------- CObjectTable ---------------------------------------- CTypedObject::CTypedObject(const std::string & type): CObjectTable() { if (!type.empty()) { add("Class", type); } } bool CTypedObject::isOk() const { return true;} //------------------------------- //---------------------- std::string CNameGiver::getNewName(const std::string & type, sint32 id) { //H_AUTO(R2_CNameGiver_getNewName) //XX To change // std::stringstream ss; // std::string ret = type; // if (id == -1) // { // id = getNewId(type); // } // ss << ret << "_" << id; // return ss.str(); std::string ret = type; if (id == -1) { id = getNewId(type); } return ret + "_" + NLMISC::toString(id); } sint32 CNameGiver::getNewId(const std::string & type) { //H_AUTO(R2_CNameGiver_getNewId) std::map< std::string, sint32>::const_iterator found = _Value.find(type); if (found == _Value.end()) { _Value[type] = 0; } return ++_Value[type]; } CNameGiver::CNameGiver() { } void CNameGiver::setMaxId(const std::string& eid,sint32 id) { //H_AUTO(R2_CNameGiver_setMaxId) _Value[eid]=id; } sint32 CNameGiver::getMaxId(const std::string& eid) { //H_AUTO(R2_CNameGiver_getMaxId) return _Value[eid]; } void CNameGiver::clear() { //H_AUTO(R2_CNameGiver_clear) _Value.clear(); } //---------------------------------------------- void CObjectFactory::clear() { //H_AUTO(R2_CObjectFactory_clear) _NameGiver->clear(); } CObject* CObjectFactory::newBasic(const std::string & type) { //H_AUTO(R2_CObjectFactory_newBasic) if (type == "RefId") { return new CObjectRefId(""); } else if (type == "String") { return new CObjectString(""); } else if (type == "Number") { return new CObjectNumber(0); } else if (type == "Table") { return new CObjectTable(); } return 0; } CObject* CObjectFactory::newAvanced(const std::string & type) { //H_AUTO(R2_CObjectFactory_newAvanced) CObjectGenerator* ret = getGenerator(type); if (ret) { return ret->instanciate(this); } return 0; } void CObjectFactory::registerGenerator(CObject* objectClass) { //H_AUTO(R2_CObjectFactory_registerGenerator) nlassert(objectClass->isString("Name")); std::string classType = objectClass->toString("Name"); bool exist=_Map.insert(std::make_pair(classType,new CObjectGenerator(objectClass, this))).second; if (!exist) { nlwarning("Generator with name %s already exists", classType.c_str()); } } CObjectFactory::CObjectFactory(const std::string & prefix) { //H_AUTO(R2_CObjectFactory_CObjectFactory) _NameGiver = new CNameGiver(); _Prefix = prefix; } void CObjectFactory::setPrefix(const std::string & prefix) { //H_AUTO(R2_CObjectFactory_setPrefix) _Prefix = prefix; } CObjectFactory::~CObjectFactory() { delete _NameGiver; //delete map std::map::iterator it = _Map.begin(); while( it != _Map.end() ) delete (*it++).second; _Map.clear(); } void CObjectFactory::setMaxId(const std::string& eid,sint32 id) { //H_AUTO(R2_CObjectFactory_setMaxId) _NameGiver->setMaxId(eid,id); } sint32 CObjectFactory::getMaxId(const std::string& eid) const { //H_AUTO(R2_CObjectFactory_getMaxId) return _NameGiver->getMaxId(eid); } CObject* CObjectFactory::newComponent(const std::string & type) { //H_AUTO(R2_CObjectFactory_newComponent) CObject* ret = 0; ret = newBasic(type); if (ret) return ret; ret = newAvanced(type); if (!ret) { nlwarning("Component not found : %s", type.c_str()); } return ret; } std::string CObjectFactory::getNewName(const std::string& type) const { //H_AUTO(R2_CObjectFactory_getNewName) if (type.empty()) { return _NameGiver->getNewName(_Prefix); } return _NameGiver->getNewName(type); } //------------------------- CClass::CClass(const std::string & classType):CObjectTable() { CObjectTable* prop = new CObjectTable(); // never accessed by client add("name", classType); add("prop", prop); _ClassType = classType; } void CClass::addAttribute(const std::string & name, const std::string & type) { //H_AUTO(R2_CClass_addAttribute) findAttr("Prop")->add( "", new CClassAttribute(name, type)); } void CClass::addAttribute(const std::string & name, const std::string & type, const std::string & value) { //H_AUTO(R2_CClass_addAttribute) findAttr("Prop")->add( "", new CClassAttribute(name, type, value)); } //------------- CObjectGenerator::~CObjectGenerator() { delete _ObjectClass; TDefaultValues::iterator first(_DefaultValues.begin()), last(_DefaultValues.end()); for (;first != last; ++first) { CObject* data = first->second; delete data; } } CObjectGenerator * CObjectFactory::getGenerator(const std::string & type) { //H_AUTO(R2_CObjectFactory_getGenerator) std::map::const_iterator found(_Map.find(type)); if (found != _Map.end()) { CObjectGenerator* ret = found->second; return ret; } return 0; } void CObjectGenerator::createDefaultValues(CObjectFactory* factory) { //H_AUTO(R2_CObjectGenerator_createDefaultValues) if (!_ObjectClass) return; CObject* prop = _ObjectClass->getAttr("Prop"); nlassert(prop); uint32 first = 0; uint32 last = prop->getSize(); for ( ; first != last ; ++first ) { CObject* found = prop->getValue(first); if (found && found->isString("DefaultValue") && found->isString("Name") ) { std::string type = found->toString("Type"); std::string name = found->toString("Name"); CObject* instance = factory->newComponent(type); if (!instance) { nlwarning(" Can't create component of type %s", type.c_str()); return; } std::string value = found->toString("DefaultValue"); instance->set("", value); _DefaultValues.insert( std::make_pair(name, instance)); } } } std::string CObjectGenerator::getBaseClass() const { //H_AUTO(R2_CObjectGenerator_getBaseClass) if (!_ObjectClass) return ""; if ( _ObjectClass->isString("BaseClass")) { return _ObjectClass->toString("BaseClass"); } return ""; } CObject* CObjectGenerator::getDefaultValue(const std::string & propName) const { //H_AUTO(R2_CObjectGenerator_getDefaultValue) TDefaultValues::const_iterator found ( _DefaultValues.find(propName) ); if (found != _DefaultValues.end()) { return found->second; } return 0; } CObject* CObjectGenerator::instanciate(CObjectFactory* factory) const { //H_AUTO(R2_CObjectGenerator_instanciate) CObject* toRet = newTable(factory); CObject* objectClassType = _ObjectClass->getAttr("Name"); nlassert( objectClassType && objectClassType->isString()); std::string classType = objectClassType->toString(); toRet->add("Class", classType); CObject* prop = _ObjectClass->getAttr("Prop"); nlassert(prop); uint32 first = 0; uint32 last = prop->getSize(); for ( ; first != last ; ++first ) { CObject* found = prop->getValue(first); if (found && found->isString("Type") ) { CObject*defaultInBase = found->getAttr("DefaultInBase"); if (!defaultInBase || (defaultInBase->isNumber() && defaultInBase->toNumber() != 1)) { std::string type = found->toString("Type"); /* :XXX: Default Value = NIL */ CObject* instance = factory->newComponent(type); if (!instance) { nlwarning(" Can't create component of type %s", type.c_str()); } else { nlassert(instance); std::string name = found->toString("Name"); toRet->add(name, instance); if (found->isString("DefaultValue")) { std::string value = found->toString("DefaultValue"); instance->set("", value); } if (name == "InstanceId") { std::string value = factory->getNewName(); instance->set("", value); } else if (name == "Id") { std::string value = factory->getNewName(classType); instance->set("", value); } } } } else { if (!found) { nlwarning("Field 'Type' not found for a property in class %s", classType.c_str()); } else { nlwarning("Field 'Type' shoud has type 'String' (class = %s)", classType.c_str()); } } } return toRet; } void CObjectString::dump(const std::string prefix, uint depth) const { //H_AUTO(R2_CObjectString_dump) std::string result(depth * 4, ' '); result += NLMISC::toString("%sString, ptr = Ox%p, value = %s, ghost = %s", prefix.c_str(), this, _Value.c_str(), _Ghost ? "true" : "false"); nlwarning(result.c_str()); } void CObjectNumber::dump(const std::string prefix, uint depth) const { //H_AUTO(R2_CObjectNumber_dump) std::string result(depth * 4, ' '); result += NLMISC::toString("%sNumber, ptr = 0x%p, value = %f, ghost = %s", prefix.c_str(), this, _Value, _Ghost ? "true" : "false"); nlwarning(result.c_str()); } void CObjectTable::dump(const std::string prefix, uint depth) const { //H_AUTO(R2_CObjectTable_dump) std::string result(depth * 4, ' '); result += NLMISC::toString("%sTable, ptr = 0x%p, , ghost = %s", prefix.c_str(), this, _Ghost ? "true" : "false"); nlwarning(result.c_str()); for(uint k = 0; k < _Value.size(); ++k) { std::string prefix = NLMISC::toString("Index = %d, key = %s ", (int) k, _Value[k].first.c_str()); _Value[k].second->dump(prefix, depth + 1); } } enum { ObjectNull, ObjectString, ObjectNumber, ObjectTable, ObjectNumberZero, ObjectNumberSInt32, ObjectNumberUInt32, ObjectNumberSInt16, ObjectNumberUInt16, ObjectNumberSInt8, ObjectNumberUInt8, ObjectNumberFloat, ObjectStringEmpty, ObjectString8, ObjectString16, ObjectTablePosition, ObjectTableNpc, ObjectTableNpcCustom, ObjectTableWayPoint, ObjectTableRegionVertex, ObjectTableRegion, ObjectTableRoad, ObjectTableNpcGrpFeature, ObjectTableBehavior, ObjectTableActivitySequence, ObjectTableActivityStep, ObjectTableChatSequence, ObjectTableChatStep,ObjectTableChatAction, ObjectTableLogicEntityAction, ObjectTableActionStep, ObjectTableActionType, ObjectTableEventType, ObjectTableLogicEntityReaction, ObjectTableTextManagerEntry, ObjectTableConditionStep, ObjectTableConditionType, ObjectTableRtAct, ObjectTableRtNpcGrp, ObjectTableRtNpc, ObjectTableRtPosition, RtAiState, ObjectTableRtAiState, ObjectTableRtNpcEventHandler, ObjectTableRtNpcEventHandlerAction, ObjectRefIdEmpty, ObjectRefId8, ObjectRefId16, ObjectRefId, ObjectNumberDouble, ObjectHeaderTag // If we "sort" this list we bust remove old session save }; const double sint8Min = -128.; const double sint8Max = 127.; const double uint8Max = 255.; const double sint16Min = -32768.; const double sint16Max = 32767.; const double uint16Max = 65535.; const double sint32Min = -2147483648.; const double sint32Max = 2147483647.; const double uint32Max = 4294967295.; uint32 CObject::instanceIdToUint32(const std::string& instanceId) { //H_AUTO(R2_CObject_instanceIdToUint32) if (instanceId.empty()) return 0; uint32 size = (uint32)instanceId.size(); if ( instanceId.substr(0, 6) != "Client") { nlwarning("R2Share: Wrong InstanceId(%s)", instanceId.c_str()); return 0; } std::string::size_type clientIdIt= instanceId.find("_", 6); if (clientIdIt == std::string::npos) { nlwarning("R2Share: Wrong InstanceId(%s)", instanceId.c_str()); return 0; } std::string clientIdStr = instanceId.substr(6, clientIdIt-6); std::string componentIdStr = instanceId.substr(clientIdIt+1, size - clientIdIt); char* ko=NULL; uint32 clientId = static_cast(strtol(clientIdStr.c_str(), &ko, 10)); if (*ko != '\0') { nlwarning("R2Share: Wrong InstanceId(%s)", instanceId.c_str()); return 0; } uint32 componentId = static_cast(strtol(componentIdStr.c_str(), &ko, 10)); if (*ko != '\0') { nlwarning("R2Share: Wrong InstanceId(%s)", instanceId.c_str()); return 0; } return (0xff000000 & clientId << 24) | (componentId & 0x00ffffff); } std::string CObject::uint32ToInstanceId(uint32 id) { //H_AUTO(R2_CObject_uint32ToInstanceId) if (id == 0) return ""; uint32 clientId = (id>> 24) & 0x000000ff; uint32 componentId = (id & 0x00ffffff); return NLMISC::toString("Client%d_%d", clientId, componentId); } static void writeNumber( NLMISC::IStream& stream, double theValue) { double value = theValue; double absValue = fabs(value); uint8 type; // It's 0 if (absValue <= std::numeric_limits::epsilon()) { type = ObjectNumberZero; stream.serial(type); return; } double integral; double fractional = modf(absValue, &integral); // It is an integral type (no fractional part) if ( fractional <= std::numeric_limits::epsilon() ) { bool pos = 0.0 <= value; // positif if (pos) { if (integral <= uint8Max) { uint8 uint8value = static_cast(value); type = ObjectNumberUInt8; stream.serial(type); stream.serial( uint8value); return; } if (integral <= uint16Max) { uint16 uint16value = static_cast(value); type = ObjectNumberUInt16; stream.serial(type); stream.serial(uint16value); return; } if (integral <= uint32Max) { uint32 uint32value = static_cast(value); type = ObjectNumberUInt32; stream.serial(type); stream.serial(uint32value); return; } } //negatif else { if ( sint8Min <= integral && integral <= sint8Max) { sint8 sint8value = static_cast(value); type = ObjectNumberSInt8; stream.serial(type); stream.serial( sint8value); return; } if ( sint16Min <= integral && integral <= sint16Max) { sint16 sint16value = static_cast(value); type = ObjectNumberSInt16; stream.serial(type); stream.serial( sint16value); return; } if ( sint32Min <= integral && integral <= sint32Max) { sint32 sint32value = static_cast(value); type = ObjectNumberSInt32; stream.serial(type); stream.serial( sint32value); return; } } } //Default case // Float are evil: you loose too much precision type = ObjectNumberDouble; double fValue = value; stream.serial(type); stream.serial(fValue); } static void serialStringInstanceId( NLMISC::IStream& stream, CObject*& data) { if (!stream.isReading()) { uint32 instanceId = CObject::instanceIdToUint32(data->toString()); stream.serial(instanceId); } else { uint32 instanceId; stream.serial(instanceId); std::string strInstanceId = CObject::uint32ToInstanceId(instanceId); data = new CObjectString(strInstanceId); } } void CObjectSerializer::serialStringInstanceId( NLMISC::IStream& stream, std::string& data) { //H_AUTO(R2_CObjectSerializer_serialStringInstanceId) if (!stream.isReading()) { uint32 instanceId = CObject::instanceIdToUint32(data); stream.serial(instanceId); } else { uint32 instanceId; stream.serial(instanceId); data= CObject::uint32ToInstanceId(instanceId); } } void CObjectSerializer::swap(CObjectSerializer& other) { //H_AUTO(R2_CObjectSerializer_swap) std::swap(this->_Data, other._Data); std::swap(this->_Compressed, other._Compressed); std::swap(this->_MustUncompress, other._MustUncompress); std::swap(this->_CompressedBuffer, other._CompressedBuffer); std::swap(this->_CompressedLen, other._CompressedLen); std::swap(this->_UncompressedLen, other._UncompressedLen); std::swap(this->Level, other.Level); std::swap(this->Log, other.Log); } static void serialNumberFixedPoint( NLMISC::IStream& stream, CObject*& data, CObjectSerializer* serializer ) { if (serializer->getVersion() == 0) { nlwarning("Using oldScenario Version (must only be use when loading) old scenarios scenario session"); if (!stream.isReading()) { sint32 v = static_cast(data->toNumber()* 64); stream.serial(v); } else { sint32 v; stream.serial(v); data = new CObjectNumber( 1.0 * v / 64.0); } } else { if (!stream.isReading()) { double val = data->toNumber(); sint32 v = static_cast( val * 1000.0 + (val >= 0.0? 0.5: -0.5)); stream.serial(v); // nldebug("serial > %f %d ", val, v); } else { sint32 v; stream.serial(v); double val = double(v) / 1000.0; data = new CObjectNumber( val ); // nldebug("serial < %f %d", val, v); } } } class CClassSerializer; class CObjectSerializerImpl : public NLMISC::CSingleton { public: ~CObjectSerializerImpl(); CObjectSerializerImpl(); // release singleton static void releaseInstance(); void registerSerializer(CClassSerializer* serializer); void serialImpl(NLMISC::IStream& stream, CObject*& data, CObjectSerializer* serializer, bool init = false); private: std::map _ClassSerializersById; std::map _ClassSerializersByName; }; class CClassSerializer { public: virtual void serialClass(NLMISC::IStream& stream, CObject*& data, CObjectSerializer* serializer) { if (!stream.isReading()) { //serial other prop; std::vector optionalPropFoundIndex; std::vector otherPropFoundIndex; std::vector valuePropFoundIndex; /* make difference between properties */ { uint32 first=0; uint last=data->getSize(); for (; first != last; ++first) { std::string key = data->getKey(first); if (key == "Class") { } else if ( key.empty() ) { valuePropFoundIndex.push_back(first); } else if (std::find(_NeededProp.begin(), _NeededProp.end(), key) == _NeededProp.end()) { std::vector::const_iterator optionalFound = std::find(_OptionalProp.begin(), _OptionalProp.end(), key); if (optionalFound != _OptionalProp.end()) { optionalPropFoundIndex.push_back(first); } else { otherPropFoundIndex.push_back(first); } } } } uint8 id = getId(); stream.serial(id); uint8 next = 0; if (!_NeededProp.empty()) next |= 1; if (!optionalPropFoundIndex.empty()) next |= 2; if (!valuePropFoundIndex.empty()) next |= 4; if (!otherPropFoundIndex.empty()) next |= 8; stream.serial(next); /* Serial needed property */ if (!_NeededProp.empty()) { //no need to put a number we already known how meany object must be std::vector::const_iterator first(_NeededProp.begin()), last(_NeededProp.end()); uint initLength = stream.getPos(); serializer->Level +=2; for ( ; first != last ; ++first) { //*first => Property key () CObject* value = data->getAttr(*first); if( !value ) { nlwarning( "About to serialize a NULL value at key %s, className %s!", first->c_str(), _ClassName.c_str() ); } if (serializer->Log) { nldebug("R2NET: (%u) Field '%s'", serializer->Level, first->c_str());} onSerial(stream, *first, value, serializer); } serializer->Level -=2; uint endLength = stream.getPos(); if (serializer->Log) { nldebug("R2NET: (%u) Needed Properties sent %u bytes", serializer->Level +1, endLength - initLength);} } // serial optional properties if (!optionalPropFoundIndex.empty()) { uint initLength = stream.getPos(); serializer->Level +=2; uint32 first = 0, last = (uint32)optionalPropFoundIndex.size(); uint8 optionalSize = static_cast(last); stream.serial(optionalSize); for (; first != last; ++first) { std::string key = data->getKey(optionalPropFoundIndex[first]); uint32 uiKey = 0, lastLocal = (uint32)_OptionalProp.size(); for ( ; uiKey != lastLocal && _OptionalProp[uiKey]!=key ; ++uiKey){} uint8 shortKey = static_cast(uiKey); stream.serial(shortKey); CObject*value = data->getValue(optionalPropFoundIndex[first]); if (serializer->Log) { nldebug("R2NET: (%u) Field '%s'", serializer->Level, key.c_str());} onSerial(stream, key, value, serializer); } serializer->Level -=2; uint endLength = stream.getPos(); if (serializer->Log) { nldebug("R2NET: (%u) Optional Properties sent %u bytes", serializer->Level +1, endLength - initLength);} } // serial value properties if (!valuePropFoundIndex.empty()) { uint initLength = stream.getPos(); serializer->Level +=2; uint32 first = 0, last = (uint32)valuePropFoundIndex.size(); uint16 last16 = static_cast(last); stream.serial(last16); for (; first != last; ++first) { CObject* value = data->getValue(valuePropFoundIndex[first]); if (serializer->Log) { nldebug("R2NET: (%u) Field [%u]", serializer->Level, first);} CObjectSerializerImpl::getInstance().serialImpl(stream, value, serializer); } serializer->Level -=2; uint endLength = stream.getPos(); if (serializer->Log) { nldebug("R2NET: (%u) Values Properties sent %u bytes", serializer->Level +1, endLength - initLength);} } // serial other properties if (!otherPropFoundIndex.empty()) { uint initLength = stream.getPos(); serializer->Level +=2; uint32 first = 0, last = (uint32)otherPropFoundIndex.size(); uint16 last16 = static_cast(last); stream.serial(last16); for (; first != last; ++first) { uint32 keyIndex = otherPropFoundIndex[first]; std::string key = data->getKey(keyIndex); CObject* value = data->getValue(keyIndex); stream.serial(key); if (serializer->Log) { nldebug("R2NET: (%u) Field '%s'", serializer->Level, key.c_str());} CObjectSerializerImpl::getInstance().serialImpl(stream, value, serializer); } serializer->Level -=2; uint endLength = stream.getPos(); if (serializer->Log) { nldebug("R2NET: (%u) Other Properties sent %u bytes", serializer->Level +1, endLength - initLength);} } } else { data = newTable(serializer->Factory); data->add("Class", new CObjectString(_ClassName)); uint8 next; stream.serial(next); // Needed pop if( (next & 1) ) { std::vector::const_iterator first(_NeededProp.begin()), last(_NeededProp.end()); for ( ; first != last ; ++first) { //*first => Property key () CObject* value = 0; std::string key = *first; onSerial(stream, key, value, serializer); if (!value) { nlwarning( "Error the needed param '%s' of Class '%s' is a NULL value.", _ClassName.c_str(), first->c_str() ); } else { data->add(key, value); } } } // Optional value if ( (next & 2) ) { uint8 optionalSize; stream.serial(optionalSize); uint first(0), last(optionalSize); for ( ; first != last ; ++first) { CObject* value; uint8 keyIndex; stream.serial(keyIndex); std::string key = _OptionalProp[keyIndex]; onSerial(stream, key, value, serializer); if (!value) { nlwarning("R2Share: Stream error"); } data->add(key, value); } } // Table value if ( (next & 4) ) { uint16 arrayValuesSize; stream.serial(arrayValuesSize); uint first(0), last(arrayValuesSize); for ( ; first != last ; ++first) { CObject* value; onSerial(stream, "", value, serializer); if (!value) { nlwarning("R2Share: Stream error"); } data->add("", value); } } // Other Value if ( (next & 8) ) { uint16 otherPropSize; stream.serial(otherPropSize); uint first(0), last(otherPropSize); for ( ; first != last ; ++first) { std::string key; stream.serial(key); CObject* value=0; onSerial(stream, key, value, serializer); if (!value) { nlwarning("R2Share: Stream error"); } else { data->add(key, value); } } } } } virtual void onSerial(NLMISC::IStream& stream, const std::string& key, CObject*& data, CObjectSerializer* serializer) { if (key == "InstanceId") { serialStringInstanceId(stream, data); return; } CObjectSerializerImpl::getInstance().serialImpl(stream, data, serializer); } virtual ~CClassSerializer(){} uint8 getId() const { return _Type; } std::string getClassName() const { return _ClassName; } protected: std::vector _NeededProp; std::vector _OptionalProp; std::string _ClassName; uint8 _Type; }; //------------------ class CNpcSerializer : public CClassSerializer { public: CNpcSerializer(); virtual void onSerial(NLMISC::IStream& stream, const std::string& key, CObject*& data, CObjectSerializer* serializer); }; CNpcSerializer::CNpcSerializer() { static const char* neededProp[] = { "InstanceId", "Base", "Position", "Angle", "Behavior" }; static const char* optionalProp[] = { "GabaritHeight", "GabaritTorsoWidth","GabaritArmsWidth", "GabaritLegsWidth", "GabaritBreastSize", "HairType", "HairColor", "Tattoo", "EyesColor", "MorphTarget1", "MorphTarget2", "MorphTarget3", "MorphTarget4", "MorphTarget5", "MorphTarget6", "MorphTarget7", "MorphTarget8", "Sex", "JacketModel", "TrouserModel", "FeetModel", "HandsModel", "ArmModel", "WeaponRightHand", "WeaponLeftHand", "JacketColor", "ArmColor", "HandsColor", "TrouserColor", "FeetColor", "Function","Level", "Profile", "Speed", "Aggro", "PlayerAttackable", "BotAttackable" }; _ClassName = "Npc"; _Type = ObjectTableNpc; const uint32 last = sizeof(neededProp) / sizeof(neededProp[0]) ; std::vector tmp(&neededProp[0], &neededProp[last]); _NeededProp.swap(tmp); std::vector tmp2(&optionalProp[0], &optionalProp[ sizeof(optionalProp) / sizeof(optionalProp[0]) ]); _OptionalProp.swap(tmp2); } void CNpcSerializer::onSerial(NLMISC::IStream& stream, const std::string& key, CObject*& data, CObjectSerializer* serializer) { //H_AUTO(R2_CNpcSerializer_onSerial) if (key == "Angle") { serialNumberFixedPoint(stream, data, serializer); return; } this->CClassSerializer::onSerial(stream, key, data, serializer); } //------------------ class CNpcCustomSerializer : public CClassSerializer { public: CNpcCustomSerializer(); virtual void onSerial(NLMISC::IStream& stream, const std::string& key, CObject*& data, CObjectSerializer* serializer); }; CNpcCustomSerializer::CNpcCustomSerializer() { static const char* neededProp[] = { "InstanceId", "Base", "Position", "Angle", "Behavior", "GabaritHeight", "GabaritTorsoWidth","GabaritArmsWidth", "GabaritLegsWidth", "GabaritBreastSize", "HairType", "HairColor", "Tattoo", "EyesColor", "MorphTarget1", "MorphTarget2", "MorphTarget3", "MorphTarget4", "MorphTarget5", "MorphTarget6", "MorphTarget7", "MorphTarget8", "JacketModel", "TrouserModel", "FeetModel", "HandsModel", "ArmModel", "JacketColor", "ArmColor", "HandsColor", "TrouserColor", "FeetColor" }; static const char* optionalProp[] = { "Name", "Profile", "Speed", "Aggro", "PlayerAttackable", "BotAttackable", "Function", "Level", "WeaponRightHand", "WeaponLeftHand", "Sex" }; _ClassName = "NpcCustom"; _Type = ObjectTableNpcCustom; const uint32 last = sizeof(neededProp) / sizeof(neededProp[0]) ; std::vector tmp(&neededProp[0], &neededProp[last]); _NeededProp.swap(tmp); std::vector tmp2(&optionalProp[0], &optionalProp[ sizeof(optionalProp) / sizeof(optionalProp[0]) ]); _OptionalProp.swap(tmp2); } // TODO -> Lot of property to bitstream void CNpcCustomSerializer::onSerial(NLMISC::IStream& stream, const std::string& key, CObject*& data, CObjectSerializer* serializer) { //H_AUTO(R2_CNpcCustomSerializer_onSerial) if (key == "Angle") { serialNumberFixedPoint(stream, data, serializer); return; } this->CClassSerializer::onSerial(stream, key, data, serializer); } //-------------------------------- #define R2_CLASS_SERIALIZER_NEEDED(Name, NeededProp ) \ class C##Name##Serializer : public CClassSerializer \ {\ public:\ C##Name##Serializer()\ { \ _ClassName = #Name;\ _Type = ObjectTable##Name;\ const uint32 last = sizeof(NeededProp) / sizeof(NeededProp[0]) ;\ std::vector tmp(&NeededProp[0], &NeededProp[last]);\ _NeededProp.swap(tmp);\ }\ }; static const char* ActivitySequenceNeededProp[] = {"InstanceId","Name","Repeating","Components"}; R2_CLASS_SERIALIZER_NEEDED(ActivitySequence, ActivitySequenceNeededProp); static const char* ActivityStepNeededProp[] = {"InstanceId","Type","TimeLimitValue","EventsIds","Chat", "ActivityZoneId", "Name", "TimeLimit", "Activity"}; R2_CLASS_SERIALIZER_NEEDED(ActivityStep, ActivityStepNeededProp); static const char* ChatSequenceNeededProp[] = {"InstanceId","Name", "Components"}; R2_CLASS_SERIALIZER_NEEDED(ChatSequence, ChatSequenceNeededProp); static const char* ChatStepNeededProp[] = {"InstanceId","Time", "Actions", "Name"}; R2_CLASS_SERIALIZER_NEEDED(ChatStep, ChatStepNeededProp); static const char* ChatActionNeededProp[] = {"InstanceId","Emote", "Who", "Facing","Says" }; R2_CLASS_SERIALIZER_NEEDED(ChatAction, ChatActionNeededProp); static const char* LogicEntityActionNeededProp[] = {"InstanceId","Conditions", "Actions", "Event", "Name"}; R2_CLASS_SERIALIZER_NEEDED(LogicEntityAction, LogicEntityActionNeededProp); static const char* ActionStepNeededProp[] = {"InstanceId","Entity"}; R2_CLASS_SERIALIZER_NEEDED(ActionStep, ActionStepNeededProp); static const char* ActionTypeNeededProp[] = {"InstanceId","Type", "Value"}; R2_CLASS_SERIALIZER_NEEDED(ActionType, ActionTypeNeededProp); static const char* EventTypeNeededProp[] = {"InstanceId","Type", "Value"}; R2_CLASS_SERIALIZER_NEEDED(EventType, EventTypeNeededProp); static const char* LogicEntityReactionNeededProp[] = {"InstanceId","LogicEntityAction", "ActionStep", "Name"}; R2_CLASS_SERIALIZER_NEEDED(LogicEntityReaction, LogicEntityReactionNeededProp); static const char* TextManagerEntryNeededProp[] = {"InstanceId","Count", "Text"}; R2_CLASS_SERIALIZER_NEEDED(TextManagerEntry, TextManagerEntryNeededProp); static const char* ConditionStepNeededProp[] = {"InstanceId","Entity", "Condition"}; R2_CLASS_SERIALIZER_NEEDED(ConditionStep, ConditionStepNeededProp); static const char* ConditionTypeNeededProp[] = {"InstanceId","Type", "Value"}; R2_CLASS_SERIALIZER_NEEDED(ConditionType, ConditionTypeNeededProp); //-------------------------------- class CPositionSerializer : public CClassSerializer { public: CPositionSerializer(); virtual void onSerial(NLMISC::IStream& stream, const std::string& key, CObject*& data, CObjectSerializer* serializer); }; CPositionSerializer::CPositionSerializer() { static const char* neededProp[] = {"InstanceId", "x", "y", "z"}; _ClassName = "Position"; _Type = ObjectTablePosition; std::vector tmp(&(neededProp[0]), &(neededProp[4])); _NeededProp.swap(tmp); } void CPositionSerializer::onSerial(NLMISC::IStream& stream, const std::string& key, CObject*& data, CObjectSerializer* serializer) { //H_AUTO(R2_CPositionSerializer_onSerial) if (key == "x" || key == "y" || key == "z") { serialNumberFixedPoint(stream, data, serializer); return; } this->CClassSerializer::onSerial(stream, key, data, serializer); } //-------------------------------- class CWayPointSerializer : public CClassSerializer { public: CWayPointSerializer() { static const char* neededProp[] = {"InstanceId", "Position"}; _ClassName = "WayPoint"; _Type = ObjectTableWayPoint; std::vector tmp(&(neededProp[0]), &(neededProp[2])); _NeededProp.swap(tmp); } }; class CRegionVertexSerializer : public CClassSerializer { public: CRegionVertexSerializer() { static const char* neededProp[] = {"InstanceId", "Position"}; _ClassName = "RegionVertex"; _Type = ObjectTableRegionVertex; std::vector tmp(&(neededProp[0]), &(neededProp[2])); _NeededProp.swap(tmp); } }; class CRegionSerializer : public CClassSerializer { public: CRegionSerializer() { static const char* neededProp[] = {"InstanceId", "Name", "Points"}; _ClassName = "Region"; _Type = ObjectTableRegion; std::vector tmp(&(neededProp[0]), &(neededProp[2])); _NeededProp.swap(tmp); } }; class CRoadSerializer : public CClassSerializer { public: CRoadSerializer() { static const char* neededProp[] = {"InstanceId", "Name", "Points"}; _ClassName = "Road"; _Type = ObjectTableRoad; std::vector tmp(&(neededProp[0]), &(neededProp[2])); _NeededProp.swap(tmp); } }; //------------------------------ class CNpcGrpFeatureSerializer : public CClassSerializer { public: CNpcGrpFeatureSerializer() { _ClassName = "NpcGrpFeature"; _Type = ObjectTableNpcGrpFeature; static const char* neededProp[] = {"InstanceId", "Name", "Components", "ActivitiesId"}; static uint32 nbNeededProp = sizeof(neededProp) / sizeof(neededProp[0]); std::vector tmp(&(neededProp[0]), &(neededProp[nbNeededProp])); _NeededProp.swap(tmp); } }; //-------------------------------------------- class CBehaviorSerializer : public CClassSerializer { public: CBehaviorSerializer() { _ClassName = "Behavior"; _Type = ObjectTableBehavior; static const char* neededProp[] = {"InstanceId", "Type", "ZoneId"}; static uint32 nbNeededProp = sizeof(neededProp) / sizeof(neededProp[0]); std::vector tmp(&(neededProp[0]), &(neededProp[nbNeededProp])); _NeededProp.swap(tmp); } }; //---------------------------------------------- class CRtSerializer : public CClassSerializer { public: void onSerial(NLMISC::IStream& stream, const std::string& key, CObject*& data, CObjectSerializer* serializer) { if (key == "Id") { serialRtId(stream, data); return; } this->CClassSerializer::onSerial(stream, key, data, serializer); } void serialRtId( NLMISC::IStream& stream, CObject*& data) { if (!stream.isReading()) { uint32 id = 0; std::string str = data->toString(); std::string toFind = _ClassName; toFind += "_"; std::string::size_type pos = str.find(toFind); if (pos != std::string::npos) { NLMISC::fromString(str.substr(toFind.size()), id); } stream.serial(id); } else { uint32 id; stream.serial(id); std::string strInstanceId = NLMISC::toString("%s_%d", _ClassName.c_str(), id); data = new CObjectString(strInstanceId); } } }; //---------------------------------------------- class CRtActSerializer : public CRtSerializer { public: CRtActSerializer() { _ClassName = "RtAct"; _Type = ObjectTableRtAct; static const char* neededProp[] = {"Id", "NpcGrps", "FaunaGrps", "AiStates", "Npcs", "Events", "Actions"}; static uint32 nbNeededProp = sizeof(neededProp) / sizeof(neededProp[0]); std::vector tmp(&(neededProp[0]), &(neededProp[nbNeededProp])); _NeededProp.swap(tmp); } }; class CRtNpcGrpSerializer : public CRtSerializer { public: CRtNpcGrpSerializer() { _ClassName = "RtNpcGrp"; _Type = ObjectTableRtNpcGrp; static const char* neededProp[] = {"Id", "Name", "Children", "AutoSpawn", "BotChat_parameters", "BotEquipment", "BotSheetClient", "BotVerticalPos", "Count", "GrpKeywords", "GrpParameters"}; static uint32 nbNeededProp = sizeof(neededProp) / sizeof(neededProp[0]); std::vector tmp(&(neededProp[0]), &(neededProp[nbNeededProp])); _NeededProp.swap(tmp); } }; class CRtNpcSerializer : public CRtSerializer { public: CRtNpcSerializer() { _ClassName = "RtNpc"; _Type = ObjectTableRtNpc; static const char* neededProp[] = {"Id", "Name", "Children", "ChatParameters", "Equipment", "IsStuck", "Keywords", "Keywords", "Sheet", "SheetClient", "BotVerticalPos", "Angle", "Pt"}; static uint32 nbNeededProp = sizeof(neededProp) / sizeof(neededProp[0]); std::vector tmp(&(neededProp[0]), &(neededProp[nbNeededProp])); _NeededProp.swap(tmp); } }; class CRtPositionSerializer : public CRtSerializer { public: CRtPositionSerializer() { _ClassName = "RtPosition"; _Type = ObjectTableRtPosition; static const char* neededProp[] = {"x", "y", "z"}; static uint32 nbNeededProp = sizeof(neededProp) / sizeof(neededProp[0]); std::vector tmp(&(neededProp[0]), &(neededProp[nbNeededProp])); _NeededProp.swap(tmp); } }; class CRtAiStateSerializer : public CRtSerializer { public: CRtAiStateSerializer() { _ClassName = "RtAiState"; _Type = ObjectTableRtAiState; static const char* neededProp[] = {"Id", "Name", "Children", "AiActivity", "AiMovement", "AiProfileParams", "Keywords","VerticalPos", "Pts" }; static uint32 nbNeededProp = sizeof(neededProp) / sizeof(neededProp[0]); std::vector tmp(&(neededProp[0]), &(neededProp[nbNeededProp])); _NeededProp.swap(tmp); } }; class CRtNpcEventHandlerSerializer : public CRtSerializer { public: CRtNpcEventHandlerSerializer() { _ClassName = "RtNpcEventHandler"; _Type = ObjectTableRtNpcEventHandler; static const char* neededProp[] = {"Id", "Name", "Event", "StatesByName", "GroupsByName", "ActionsId"}; static uint32 nbNeededProp = sizeof(neededProp) / sizeof(neededProp[0]); std::vector tmp(&(neededProp[0]), &(neededProp[nbNeededProp])); _NeededProp.swap(tmp); } }; class CRtNpcEventHandlerActionSerializer : public CRtSerializer { public: CRtNpcEventHandlerActionSerializer() { _ClassName = "RtNpcEventHandlerAction"; _Type = ObjectTableRtNpcEventHandlerAction; static const char* neededProp[] = {"Id", "Action", "Name", "Parameters", "Children", "Weight"}; static uint32 nbNeededProp = sizeof(neededProp) / sizeof(neededProp[0]); std::vector tmp(&(neededProp[0]), &(neededProp[nbNeededProp])); _NeededProp.swap(tmp); } }; //------------------------------------------- /* class CXXXSerializer : public CClassSerializer { public: CNpcGrpFeatureSerializer() { _ClassName = "XXX"; _Type = ObjectTableXXX; static const char* neededProp[] = {"InstanceId", "Type", "ZoneId"}; static uint32 nbNeededProp = sizeof(neededProp) / sizeof(neededProp[0]); std::vector tmp(&(neededProp[0]), &(neededProp[nbNeededProp])); _NeededProp.swap(tmp); } }; */ //------------------------------ //-------------------------------- CObjectSerializerImpl::~CObjectSerializerImpl() { std::map::iterator first(_ClassSerializersById.begin()), last(_ClassSerializersById.end()); for ( ; first != last ; ++first) { delete first->second; } _ClassSerializersById.clear(); } CObjectSerializerImpl::CObjectSerializerImpl() { registerSerializer( new CNpcSerializer() ); registerSerializer( new CNpcCustomSerializer() ); registerSerializer( new CNpcGrpFeatureSerializer()); registerSerializer( new CPositionSerializer()); registerSerializer( new CWayPointSerializer()); registerSerializer( new CRegionVertexSerializer()); registerSerializer( new CRegionSerializer()); registerSerializer( new CRoadSerializer()); // registerSerializer( new CNpcGrpFeatureSerializer()); registerSerializer( new CBehaviorSerializer()); registerSerializer( new CRtActSerializer()); registerSerializer( new CRtNpcGrpSerializer()); registerSerializer( new CRtNpcSerializer()); registerSerializer( new CRtAiStateSerializer()); registerSerializer( new CRtNpcEventHandlerSerializer()); registerSerializer( new CRtNpcEventHandlerActionSerializer()); registerSerializer( new CActivitySequenceSerializer()); registerSerializer( new CActivityStepSerializer()); registerSerializer( new CChatSequenceSerializer()); registerSerializer( new CChatStepSerializer()); registerSerializer( new CChatActionSerializer()); registerSerializer( new CLogicEntityActionSerializer()); registerSerializer( new CActionStepSerializer()); registerSerializer( new CActionTypeSerializer()); registerSerializer( new CEventTypeSerializer()); registerSerializer( new CLogicEntityReactionSerializer()); registerSerializer( new CTextManagerEntrySerializer()); registerSerializer( new CConditionTypeSerializer()); registerSerializer( new CConditionStepSerializer()); } void CObjectSerializerImpl::releaseInstance() { if( Instance ) { delete Instance; Instance = NULL; } } void CObjectSerializerImpl::registerSerializer(CClassSerializer* serializer) { bool insert = _ClassSerializersById.insert(std::make_pair(serializer->getId(), serializer)).second; if (!insert) { nlinfo("R2Share: Prototype register two time"); } insert = _ClassSerializersByName.insert(std::make_pair(serializer->getClassName(), serializer)).second; if (!insert) { nlinfo("R2Share: Prototype register two time"); } } void CObjectSerializerImpl::serialImpl(NLMISC::IStream& stream, CObject*& data, CObjectSerializer* serializer, bool init) { //H_AUTO(R2_CObjectSerializerImpl_serialImpl) uint8 type; if (!stream.isReading()) { if (init) { type = ObjectHeaderTag; uint32 version = 1; stream.serial(type); stream.serial(version); serializer->setVersion(version); } if (data == 0) { type = ObjectNull; uint initLength = stream.getPos(); stream.serial(type); uint endLength = stream.getPos(); if (serializer->Log) { nldebug("R2NET: (%u) Null sent %u bytes", serializer->Level, endLength - initLength);} } else if (data->isNumber()) { uint initLength = stream.getPos(); writeNumber(stream, data->toNumber()); uint endLength = stream.getPos(); if (serializer->Log) { nldebug("R2NET: (%u) Number sent %u bytes", serializer->Level, endLength - initLength); } } else if(data->isString() || data->isRefId()) { uint initLength = stream.getPos(); std::string value = data->toString(); uint32 size = (uint32)value.size(); if (size == 0) { /*if (data->isRefId()) { nlwarning("Serializing an object of type 'empty RefId'"); }*/ type = data->isRefId() ? ObjectRefIdEmpty : ObjectStringEmpty; // NB : 'isString()' would be true in both test because CObjectRefId derives from CObjectString stream.serial(type); } else if (size < static_cast(uint8Max) ) { /*if (data->isRefId()) { nlwarning("Serializing an object of type 'RefId8'"); }*/ type = data->isRefId() ? ObjectRefId8 : ObjectString8; // NB : 'isString()' would be true in both test because CObjectRefId derives from CObjectString uint8 size8 = static_cast(size); stream.serial(type); stream.serial(size8); stream.serialBuffer((uint8*)(&(value[0])), size8); } else if (size < static_cast(uint16Max) ) { /*if (data->isRefId()) { nlwarning("Serializing an object of type 'RefId16'"); }*/ type = data->isRefId() ? ObjectRefId16 : ObjectString16; // NB : 'isString()' would be true in both test because CObjectRefId derives from CObjectString uint16 size16 = static_cast(size); stream.serial(type); stream.serial(size16); stream.serialBuffer((uint8*)(&(value[0])), size16); } else { //default very big string type = data->isRefId() ? ObjectRefId : ObjectString; // NB : 'isString()' would be true in both test because CObjectRefId derives from CObjectString stream.serial(type); stream.serial(value); } uint endLength = stream.getPos(); if (serializer->Log) { nldebug("R2NET: (%u) String Send %u bytes", serializer->Level, endLength - initLength); } return; } else if (data->isTable()) { uint initLength = stream.getPos(); std::string className; if ( data->isString("Class")) { className = data->toString("Class"); } if (!className.empty()) { std::map::const_iterator found(_ClassSerializersByName.find(className)); if (found != _ClassSerializersByName.end() ) { found->second->serialClass(stream, data, serializer); uint endLength = stream.getPos(); if (serializer->Log) { nldebug("R2NET: (%u) Class '%s' sent %u bytes", serializer->Level, className.c_str(), endLength - initLength); } return; } } type = ObjectTable; stream.serial(type); uint32 size = data->getSize(); stream.serial(size); for (uint first = 0; first != size; ++first) { std::string key = data->getKey(first); CObject* value = data->getValue(first); stream.serial(key); ++ (serializer->Level); if (serializer->Log) { nldebug("R2NET: (%u) Field '%s'", serializer->Level, key.c_str());} serialImpl(stream, value, serializer); -- (serializer->Level); } uint endLength = stream.getPos(); if (serializer->Log) { if (className.empty()) { nldebug("R2NET: (%u) Table sent %u bytes",serializer->Level, endLength - initLength); } else { nldebug("R2NET: (%u) Generic Class(%s) sent %u bytes", serializer->Level, className.c_str(), endLength - initLength); } } // nlstop; } } else { uint8 type = ObjectNull; stream.serial(type); // NB nico : use factory here instead of plain 'new' because client may use derived classes internally // NB 2 : if server is created locally, not a problem because derived object fonctinnality switch(type) { case ObjectHeaderTag: { uint32 version = 0; stream.serial(version); serializer->setVersion(version); this->serialImpl(stream, data, serializer); return; } case ObjectNull: { data = 0; return; } case ObjectNumber: { double value; stream.serial(value); data = serializer->Factory ? serializer->Factory->newBasic("Number") : new CObjectNumber(0); ((CObjectNumber *) data)->set("", value); return; } case ObjectNumberZero: { data = serializer->Factory ? serializer->Factory->newBasic("Number") : new CObjectNumber(0); ((CObjectNumber *) data)->set("", 0.0); return; } case ObjectNumberSInt32: { sint32 value; stream.serial(value); data = serializer->Factory ? serializer->Factory->newBasic("Number") : new CObjectNumber(0); ((CObjectNumber *) data)->set("", value); return; } case ObjectNumberUInt32: { uint32 value; stream.serial(value); data = serializer->Factory ? serializer->Factory->newBasic("Number") : new CObjectNumber(0); ((CObjectNumber *) data)->set("", value); return; } case ObjectNumberSInt16: { sint16 value; stream.serial(value); data = serializer->Factory ? serializer->Factory->newBasic("Number") : new CObjectNumber(0); ((CObjectNumber *) data)->set("", value); return; } case ObjectNumberUInt16: { uint16 value; stream.serial(value); data = serializer->Factory ? serializer->Factory->newBasic("Number") : new CObjectNumber(0); ((CObjectNumber *) data)->set("", value); return; } case ObjectNumberSInt8: { sint8 value; stream.serial(value); data = serializer->Factory ? serializer->Factory->newBasic("Number") : new CObjectNumber(0); ((CObjectNumber *) data)->set("", value); return; } case ObjectNumberUInt8: { uint8 value; stream.serial(value); data = serializer->Factory ? serializer->Factory->newBasic("Number") : new CObjectNumber(0); ((CObjectNumber *) data)->set("", value); return; } // Do not remove this or it would be impossible to load old session case ObjectNumberFloat: { float value; stream.serial(value); data = serializer->Factory ? serializer->Factory->newBasic("Number") : new CObjectNumber(0); ((CObjectNumber *) data)->set("", value); return; } case ObjectNumberDouble: { double value; stream.serial(value); data = serializer->Factory ? serializer->Factory->newBasic("Number") : new CObjectNumber(0); ((CObjectNumber *) data)->set("", value); return; } case ObjectStringEmpty: { data = serializer->Factory ? serializer->Factory->newBasic("String") : new CObjectString(""); return; } case ObjectString8: { std::string value; uint8 size; stream.serial(size); value.resize(size); stream.serialBuffer((uint8*)(&(value[0])), size); data = serializer->Factory ? serializer->Factory->newBasic("String") : new CObjectString(""); ((CObjectString *) data)->set("", value); return; } case ObjectString16: { std::string value; uint16 size; stream.serial(size); value.resize(size); stream.serialBuffer((uint8*)(&(value[0])), size); data = serializer->Factory ? serializer->Factory->newBasic("String") : new CObjectString(""); ((CObjectString *) data)->set("", value); break; } case ObjectString: { std::string value; stream.serial(value); data = serializer->Factory ? serializer->Factory->newBasic("String") : new CObjectString(""); ((CObjectString *) data)->set("", value); break; } case ObjectRefId: { std::string value; stream.serial(value); data = serializer->Factory ? serializer->Factory->newBasic("RefId") : new CObjectRefId(""); ((CObjectRefId *) data)->set("", value); break; } case ObjectRefIdEmpty: { data = serializer->Factory ? serializer->Factory->newBasic("RefId") : new CObjectRefId(""); return; } case ObjectRefId8: { std::string value; uint8 size; stream.serial(size); value.resize(size); stream.serialBuffer((uint8*)(&(value[0])), size); data = serializer->Factory ? serializer->Factory->newBasic("RefId") : new CObjectRefId(""); ((CObjectRefId *) data)->set("", value); return; } case ObjectRefId16: { std::string value; uint16 size; stream.serial(size); value.resize(size); stream.serialBuffer((uint8*)(&(value[0])), size); data = serializer->Factory ? serializer->Factory->newBasic("RefId") : new CObjectRefId(""); ((CObjectRefId *) data)->set("", value); } break; case ObjectTable: { uint32 size; stream.serial(size); data = newTable(serializer->Factory); uint32 first; for (first = 0 ; first != size; ++first) { std::string key; stream.serial(key); CObject* value=0; serialImpl(stream,value, serializer); data->add(key, value); } } break; default: std::map::const_iterator found(_ClassSerializersById.find(type)); if (found != _ClassSerializersById.end()) { found->second->serialClass(stream, data, serializer); return; } BOMB("ClassSerializer not found: can not read data", return); } } } void CObjectSerializer::releaseInstance() { //H_AUTO(R2_CObjectSerializer_releaseInstance) CObjectSerializerImpl::releaseInstance(); } void CObjectSerializer::serial(NLMISC::IStream& stream) { //H_AUTO(R2_CObjectSerializer_serial) stream.serial(_Compressed); if ( stream.isReading() ) { _MustUncompress = _Compressed; } if (!_Compressed) { CObjectSerializerImpl::getInstance().serialImpl(stream, _Data, this, true); } else { stream.serial(_CompressedLen); stream.serial(_UncompressedLen); if ( stream.isReading() ) { _CompressedBuffer = new uint8[_CompressedLen]; } stream.serialBuffer(_CompressedBuffer, _CompressedLen); } } CObject* CObjectSerializer::getData() const { //H_AUTO(R2_CObjectSerializer_getData) if (_Compressed && _MustUncompress) { uncompress(); }; if (_Data) return _Data->clone(); return 0; } CObjectSerializer::CObjectSerializer(CObjectFactory *factory, CObject* data) : Factory(factory), Level(0), Log(false) { Log = false; _CompressedBuffer = 0; _CompressedLen = 0; _UncompressedLen = 0; _Compressed = false; _MustUncompress = false; _Version = 0; if (data) { _Data = data->clone(); } else { _Data = 0; } } CObjectSerializer::~CObjectSerializer() { if (_CompressedBuffer) { delete [] _CompressedBuffer; _CompressedBuffer = 0;} delete _Data; } CObjectSerializer::CObjectSerializer(const CObjectSerializer& /* lh */) { //H_AUTO(R2_CObjectSerializer_CObjectSerializer) nlassert(0); } CObjectSerializer& CObjectSerializer::operator=(const CObjectSerializer& /* rh */) { nlassert(0); return *this; } void CObjectSerializer::setData(CObject* data) { //H_AUTO(R2_CObjectSerializer_setData) nlassert(!_Compressed); if (data) { _Data = data->clone(); } else { _Data = 0; } } void CObjectSerializer::compress() { //H_AUTO(R2_CObjectSerializer_compress) NLMISC::CMemStream buffer; if (buffer.isReading()) buffer.invert(); uint32 init = buffer.length(); CObjectSerializerImpl::getInstance().serialImpl(buffer, _Data, this, true); uint32 length = buffer.length() - init; uLongf destLen = length + length / 1000 + 12; Bytef *dest = new Bytef[destLen]; int ok = ::compress(dest, &destLen, (Bytef *)buffer.buffer(), buffer.length()); if (ok != Z_OK) { delete [] dest; nlwarning("Error while compressing data stream"); return; } // Compress data only if shortest if ( length < destLen) { delete [] dest; return; } _Compressed = true; _CompressedBuffer = (uint8*) dest; _CompressedLen = destLen; _UncompressedLen = length; nlinfo("Compress Data from %u to %u",_UncompressedLen, _CompressedLen ); } void CObjectSerializer::uncompress() const { //H_AUTO(R2_CObjectSerializer_uncompress) const_cast(this)->uncompressImpl(); } void CObjectSerializer::uncompressImpl() { //H_AUTO(R2_CObjectSerializer_uncompressImpl) if (_Compressed && _MustUncompress) { _MustUncompress = false; if ( _Data ) { delete _Data; _Data = 0; } Bytef* data = new Bytef[_UncompressedLen]; uLongf dataLen = _UncompressedLen; sint32 state = ::uncompress (data, &dataLen , reinterpret_cast(_CompressedBuffer), _CompressedLen); if (state != Z_OK) { delete[] data; nlwarning("Error while uncompressing data stream."); return; } if (_UncompressedLen != dataLen) { nlwarning("Error error in data stream."); } _UncompressedLen = dataLen; NLMISC::CMemStream buffer; if (buffer.isReading()) buffer.invert(); buffer.serialBuffer((uint8*)data, _UncompressedLen); buffer.invert(); buffer.seek(0, NLMISC::IStream::begin); CObjectSerializerImpl::getInstance().serialImpl(buffer, _Data, this, true); delete[] data; } } }