// 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 "utils.h"
#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.empty())
{
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).empty())
{
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").empty())
{
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.empty())
{
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);
bool ko;
uint32 clientId;
ko = NLMISC::fromString(clientIdStr, clientId);
if (!ko)
{
nlwarning("R2Share: Wrong InstanceId(%s)", instanceId.c_str());
return 0;
}
uint32 componentId;
ko = NLMISC::fromString(componentIdStr, componentId);
if (!ko)
{
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;
}
}
}