// 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 .
/////////////////////////////////////////////////////////////////
// WARNING : this is a generated file, don't change it !
/////////////////////////////////////////////////////////////////
#include "stdpch.h"
#include "database_mapping.h"
namespace RSMGR
{
void CKnownUserPtr::linkPtr()
{
nlassert(_NextPtr == NULL);
nlassert(_PrevPtr == NULL);
if (_Ptr != NULL)
{
_NextPtr = _Ptr->getFirstPtr();
if (_NextPtr != NULL)
{
_PrevPtr = _NextPtr->_PrevPtr;
_PrevPtr->_NextPtr = this;
_NextPtr->_PrevPtr = this;
}
else
{
_NextPtr = this;
_PrevPtr = this;
_Ptr->setFirstPtr(this);
}
}
}
void CKnownUserPtr::unlinkPtr()
{
if (_NextPtr == NULL)
{
nlassert(_PrevPtr == NULL);
return;
}
if (_Ptr != NULL)
{
if (_NextPtr == this)
{
nlassert(_PrevPtr == this);
// last pointer !
_Ptr->setFirstPtr(NULL);
}
else
{
if (_Ptr->getFirstPtr() == this)
{
// the first ptr is the current one, we need to switch to next one
_Ptr->setFirstPtr(_NextPtr);
}
}
}
if (_NextPtr != this)
{
nlassert(_PrevPtr != this);
_NextPtr->_PrevPtr = _PrevPtr;
_PrevPtr->_NextPtr = _NextPtr;
}
_NextPtr = NULL;
_PrevPtr = NULL;
}
CKnownUser::TObjectCache CKnownUser::_ObjectCache;
CKnownUser::TReleasedObject CKnownUser::_ReleasedObject;
// Destructor, delete any children
CKnownUser::~CKnownUser()
{
// release childs reference
if (_PtrList != NULL)
{
nlwarning("ERROR : someone try to delete this object, but there are still ptr on it !");
CKnownUserPtr *ptr = _PtrList;
do
{
nlwarning(" Pointer created from '%s', line %u", ptr->_FileName, ptr->_LineNum);
ptr = _PtrList->getNextPtr();
} while(ptr != _PtrList);
nlstop;
}
// remove object from cache map
if (_RelationId != NOPE::INVALID_OBJECT_ID
&& _ObjectState != NOPE::os_removed
&& _ObjectState != NOPE::os_transient)
{
nldebug("NOPE: clearing CKnownUser @%p from cache with id %u", this, static_cast(_RelationId));
nlverify(_ObjectCache.erase(_RelationId) == 1);
}
else if (_ObjectState != NOPE::os_transient)
{
nlassert(_ObjectCache.find(_RelationId) == _ObjectCache.end());
}
if (_ObjectState == NOPE::os_released)
{
removeFromReleased();
}
else
{
TReleasedObject::iterator it(_ReleasedObject.find(_ReleaseDate));
if (it != _ReleasedObject.end())
{
nlassert(it->second.find(this) == it->second.end());
}
}
}
void CKnownUser::removeFromReleased()
{
TReleasedObject::iterator it(_ReleasedObject.find(_ReleaseDate));
nlassert(it != _ReleasedObject.end());
TObjectSet &os = it->second;
nlverify(os.erase(this) == 1);
// nb : _ReleasedObject time entry are removed by the cache update
}
bool CKnownUser::create(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_transient);
std::string qs;
qs = "INSERT INTO known_users (";
qs += "owner, targer_user, targer_character, relation_type, comments";
qs += ") VALUES (";
qs += "'"+MSW::escapeString(NLMISC::toString(_OwnerId), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_TargetUser), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_TargetCharacter), connection)+"'";
qs += ", ";
qs += "'"+_Relation.toString()+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_Comments), connection)+"'";
qs += ")";
if (connection.query(qs))
{
uint32 _id_ = connection.getLastGeneratedId();
setObjectId(_id_);
setPersistentState(NOPE::os_clean);
// update the parent class instance in cache if any
if (_OwnerId != 0)
{
// need to update the parent class child list if it is in the cache
CRingUser *parent = CRingUser::loadFromCache(_OwnerId, false);
if (parent && parent->_KnownUsers != NULL)
{
nlassert(std::find(parent->_KnownUsers->begin(), parent->_KnownUsers->end(), CKnownUserPtr(this, __FILE__, __LINE__)) == parent->_KnownUsers->end());
parent->_KnownUsers->push_back(CKnownUserPtr(this, __FILE__, __LINE__));
}
}
if (_TargetUser != 0)
{
// need to update the parent class child list if it is in the cache
CCharacter *parent = CCharacter::loadFromCache(_TargetUser, false);
if (parent && parent->_KnownBy != NULL)
{
nlassert(std::find(parent->_KnownBy->begin(), parent->_KnownBy->end(), CKnownUserPtr(this, __FILE__, __LINE__)) == parent->_KnownBy->end());
parent->_KnownBy->push_back(CKnownUserPtr(this, __FILE__, __LINE__));
}
}
return true;
}
return false;
}
bool CKnownUser::update(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_dirty || getPersistentState() == NOPE::os_clean);
if (getPersistentState() == NOPE::os_clean)
// the object is clean, just ignore the save
return true;
std::string qs;
qs = "UPDATE known_users SET ";
qs += "owner = '"+MSW::escapeString(NLMISC::toString(_OwnerId), connection)+"'";
qs += ", ";
qs += "targer_user = '"+MSW::escapeString(NLMISC::toString(_TargetUser), connection)+"'";
qs += ", ";
qs += "targer_character = '"+MSW::escapeString(NLMISC::toString(_TargetCharacter), connection)+"'";
qs += ", ";
qs += "relation_type = '"+_Relation.toString()+"'";
qs += ", ";
qs += "comments = '"+MSW::escapeString(NLMISC::toString(_Comments), connection)+"'";
qs += " WHERE Id = '"+NLMISC::toString(_RelationId)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
setPersistentState(NOPE::os_clean);
return true;
}
}
return false;
}
bool CKnownUser::remove(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_dirty || getPersistentState() == NOPE::os_clean);
std::string qs;
qs = "DELETE FROM known_users ";
qs += " WHERE Id = '"+NLMISC::toString(_RelationId)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
// change the persistant state to 'removed'.
setPersistentState(NOPE::os_removed);
// need to remove ref from parent class container (if any)
{
CRingUserPtr parent(CRingUser::loadFromCache(_OwnerId, true), __FILE__, __LINE__);
if (parent != NULL && parent->_KnownUsers != NULL)
{
std::vector < CKnownUserPtr >::iterator it = std::find(parent->_KnownUsers->begin(), parent->_KnownUsers->end(), this);
if (it != parent->_KnownUsers->end())
{
parent->_KnownUsers->erase(it);
}
}
}
{
CCharacterPtr parent(CCharacter::loadFromCache(_TargetUser, true), __FILE__, __LINE__);
if (parent != NULL && parent->_KnownBy != NULL)
{
std::vector < CKnownUserPtr >::iterator it = std::find(parent->_KnownBy->begin(), parent->_KnownBy->end(), this);
if (it != parent->_KnownBy->end())
{
parent->_KnownBy->erase(it);
}
}
}
// need to remove ref from parent (if any)
return true;
}
}
return false;
}
bool CKnownUser::removeById(MSW::CConnection &connection, uint32 id)
{
CKnownUser *object = loadFromCache(id, true);
if (object != NULL)
{
return object->remove(connection);
}
// not in cache, run a SQL query
std::string qs;
qs = "DELETE FROM known_users ";
qs += " WHERE Id = '"+NLMISC::toString(id)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
// ok, the row is removed
return true;
}
}
return false;
}
// Try to load the specified object from the memory cache, return NULL if the object is not in the cache
CKnownUser *CKnownUser::loadFromCache(uint32 objectId, bool unrelease)
{
// look in the cache
TObjectCache::iterator it(_ObjectCache.find(objectId));
if (it == _ObjectCache.end())
{
// not found, return null
return NULL;
}
else
{
CKnownUser *object = it->second;
if (object->_ObjectState == NOPE::os_released)
{
if (unrelease)
{
// we need to remove this object from the released object set.
object->removeFromReleased();
object->_ObjectState = NOPE::os_clean;
}
}
return it->second;
}
}
// Receive and execute command from the cache manager.
uint32 CKnownUser::cacheCmd(NOPE::TCacheCmd cmd)
{
if (cmd == NOPE::cc_update)
{
updateCache();
}
else if (cmd == NOPE::cc_clear)
{
clearCache();
}
else if (cmd == NOPE::cc_dump)
{
dump();
}
else if (cmd == NOPE::cc_instance_count)
{
return (uint32)_ObjectCache.size();
}
// default return value
return 0;
}
void CKnownUser::dump()
{
nlinfo(" Cache info for class CKnownUser :");
nlinfo(" There are %u object instances in cache", _ObjectCache.size());
// count the number of object in the released object set
uint32 nbReleased = 0;
TReleasedObject::iterator first(_ReleasedObject.begin()), last(_ReleasedObject.end());
for (; first != last; ++first)
{
nbReleased += (uint32)first->second.size();
}
nlinfo(" There are %u object instances in cache not referenced (waiting deletion or re-use))", nbReleased);
}
void CKnownUser::updateCache()
{
if (_ReleasedObject.empty())
return;
// 30 s hold in cache
const time_t MAX_CACHE_OLD_TIME = 30;
time_t now = NLMISC::CTime::getSecondsSince1970();
// look for object set older than MAX_CACHE_OLD_TIME and delete them
while (!_ReleasedObject.empty() && _ReleasedObject.begin()->first < now-MAX_CACHE_OLD_TIME)
{
TObjectSet &delSet = _ReleasedObject.begin()->second;
// unload this objects
while (!delSet.empty())
{
CKnownUser *object = *delSet.begin();
delete object;
}
_ReleasedObject.erase(_ReleasedObject.begin());
}
}
void CKnownUser::clearCache()
{
// remove any unreferenced object from the cache
while (!_ReleasedObject.empty())
{
TObjectSet &delSet = _ReleasedObject.begin()->second;
// unload this objects
while (!delSet.empty())
{
CKnownUser *object = *delSet.begin();
delete object;
}
_ReleasedObject.erase(_ReleasedObject.begin());
}
}
void CKnownUser::registerUpdatable()
{
static bool registered = false;
if (!registered)
{
NOPE::CPersistentCache::getInstance().registerCache(&CKnownUser::cacheCmd);
registered = true;
}
}
// set the pointer on the first pointer of the pointer list (set to null when there is no more pointer)
void CKnownUser::setFirstPtr(CKnownUserPtr *ptr)
{
_PtrList = ptr;
if (ptr == NULL)
{
// this is the last pointer !
if (_ObjectState == NOPE::os_transient
|| _ObjectState == NOPE::os_removed)
{
// not a persistent object, or removed object, just delet it
delete this;
}
else if (_ObjectState != NOPE::os_removed)
{
setPersistentState(NOPE::os_released);
}
}
}
// Set the persistent state of the object and do some house keeping
void CKnownUser::setPersistentState(NOPE::TObjectState state)
{
nlassert(NOPE::AllowedTransition[_ObjectState][state] == true);
if(_ObjectState == NOPE::os_released && state == NOPE::os_removed)
{
// a release object gets removed (e.g. by remove by id)
// delete the object
delete this;
// no more to do
return;
}
if (_ObjectState == NOPE::os_transient && state != NOPE::os_transient)
{
nldebug("NOPE: inserting CKnownUser @%p in cache with id %u", this, static_cast(_RelationId));
nlverify(_ObjectCache.insert(std::make_pair(_RelationId, this)).second);
}
if (_ObjectState != NOPE::os_transient)
nlassert(_ObjectCache.find(_RelationId) != _ObjectCache.end());
_ObjectState = state;
if (state == NOPE::os_released)
{
_ReleaseDate = NLMISC::CTime::getSecondsSince1970();
nlverify(_ReleasedObject[_ReleaseDate].insert(this).second);
}
else if (state == NOPE::os_removed)
{
nldebug("NOPE: erasing CKnownUser @%p in cache with id %u", this, static_cast(_RelationId));
nlverify(_ObjectCache.erase(_RelationId) == 1);
}
}
CKnownUserPtr CKnownUser::load(MSW::CConnection &connection, uint32 id, const char *filename, uint32 lineNum)
{
CKnownUser *inCache = loadFromCache(id, true);
if (inCache != NULL)
{
return CKnownUserPtr(inCache, filename, lineNum);
}
std::string qs;
qs = "SELECT ";
qs += "Id, owner, targer_user, targer_character, relation_type, comments";
qs += " FROM known_users";
qs += " WHERE Id = '"+NLMISC::toString(id)+"'";
CKnownUserPtr ret;
if (!connection.query(qs))
{
return ret;
}
MSW::CStoreResult *result = connection.storeResult().release();
nlassert(result->getNumRows() <= 1);
if (result->getNumRows() == 1)
{
ret.assign(new CKnownUser, filename, lineNum);
// ok, we have an object
result->fetchRow();
result->getField(0, ret->_RelationId);
result->getField(1, ret->_OwnerId);
result->getField(2, ret->_TargetUser);
result->getField(3, ret->_TargetCharacter);
{
std::string s;
result->getField(4, s);
ret->_Relation = TKnownUserRelation(s);
}
result->getField(5, ret->_Comments);
ret->setPersistentState(NOPE::os_clean);
}
delete result;
return ret;
}
bool CKnownUser::loadChildrenOfCRingUser(MSW::CConnection &connection, uint32 parentId, std::vector < CKnownUserPtr > & container, const char *filename, uint32 lineNum)
{
std::string qs;
qs = "SELECT ";
qs += "Id, owner, targer_user, targer_character, relation_type, comments";
qs += " FROM known_users";
qs += " WHERE owner = '"+NLMISC::toString(parentId)+"'";
if (!connection.query(qs))
{
return false;
}
std::unique_ptr result(connection.storeResult());
for (uint i=0; igetNumRows(); ++i)
{
CKnownUser *ret = new CKnownUser();
// ok, we have an object
result->fetchRow();
result->getField(0, ret->_RelationId);
result->getField(1, ret->_OwnerId);
result->getField(2, ret->_TargetUser);
result->getField(3, ret->_TargetCharacter);
{
std::string s;
result->getField(4, s);
ret->_Relation = TKnownUserRelation(s);
}
result->getField(5, ret->_Comments);
CKnownUser *inCache = loadFromCache(ret->_RelationId, true);
if (inCache != NULL)
{
container.push_back(CKnownUserPtr(inCache, filename, lineNum));
// no more needed
delete ret;
}
else
{
ret->setPersistentState(NOPE::os_clean);
container.push_back(CKnownUserPtr(ret, filename, lineNum));
}
}
return true;
}
bool CKnownUser::loadChildrenOfCCharacter(MSW::CConnection &connection, uint32 parentId, std::vector < CKnownUserPtr > & container, const char *filename, uint32 lineNum)
{
std::string qs;
qs = "SELECT ";
qs += "Id, owner, targer_user, targer_character, relation_type, comments";
qs += " FROM known_users";
qs += " WHERE targer_user = '"+NLMISC::toString(parentId)+"'";
if (!connection.query(qs))
{
return false;
}
std::unique_ptr result(connection.storeResult());
for (uint i=0; igetNumRows(); ++i)
{
CKnownUser *ret = new CKnownUser();
// ok, we have an object
result->fetchRow();
result->getField(0, ret->_RelationId);
result->getField(1, ret->_OwnerId);
result->getField(2, ret->_TargetUser);
result->getField(3, ret->_TargetCharacter);
{
std::string s;
result->getField(4, s);
ret->_Relation = TKnownUserRelation(s);
}
result->getField(5, ret->_Comments);
CKnownUser *inCache = loadFromCache(ret->_RelationId, true);
if (inCache != NULL)
{
container.push_back(CKnownUserPtr(inCache, filename, lineNum));
// no more needed
delete ret;
}
else
{
ret->setPersistentState(NOPE::os_clean);
container.push_back(CKnownUserPtr(ret, filename, lineNum));
}
}
return true;
}
void CSessionParticipantPtr::linkPtr()
{
nlassert(_NextPtr == NULL);
nlassert(_PrevPtr == NULL);
if (_Ptr != NULL)
{
_NextPtr = _Ptr->getFirstPtr();
if (_NextPtr != NULL)
{
_PrevPtr = _NextPtr->_PrevPtr;
_PrevPtr->_NextPtr = this;
_NextPtr->_PrevPtr = this;
}
else
{
_NextPtr = this;
_PrevPtr = this;
_Ptr->setFirstPtr(this);
}
}
}
void CSessionParticipantPtr::unlinkPtr()
{
if (_NextPtr == NULL)
{
nlassert(_PrevPtr == NULL);
return;
}
if (_Ptr != NULL)
{
if (_NextPtr == this)
{
nlassert(_PrevPtr == this);
// last pointer !
_Ptr->setFirstPtr(NULL);
}
else
{
if (_Ptr->getFirstPtr() == this)
{
// the first ptr is the current one, we need to switch to next one
_Ptr->setFirstPtr(_NextPtr);
}
}
}
if (_NextPtr != this)
{
nlassert(_PrevPtr != this);
_NextPtr->_PrevPtr = _PrevPtr;
_PrevPtr->_NextPtr = _NextPtr;
}
_NextPtr = NULL;
_PrevPtr = NULL;
}
CSessionParticipant::TObjectCache CSessionParticipant::_ObjectCache;
CSessionParticipant::TReleasedObject CSessionParticipant::_ReleasedObject;
// Destructor, delete any children
CSessionParticipant::~CSessionParticipant()
{
// release childs reference
if (_PtrList != NULL)
{
nlwarning("ERROR : someone try to delete this object, but there are still ptr on it !");
CSessionParticipantPtr *ptr = _PtrList;
do
{
nlwarning(" Pointer created from '%s', line %u", ptr->_FileName, ptr->_LineNum);
ptr = _PtrList->getNextPtr();
} while(ptr != _PtrList);
nlstop;
}
// remove object from cache map
if (_Id != NOPE::INVALID_OBJECT_ID
&& _ObjectState != NOPE::os_removed
&& _ObjectState != NOPE::os_transient)
{
nldebug("NOPE: clearing CSessionParticipant @%p from cache with id %u", this, static_cast(_Id));
nlverify(_ObjectCache.erase(_Id) == 1);
}
else if (_ObjectState != NOPE::os_transient)
{
nlassert(_ObjectCache.find(_Id) == _ObjectCache.end());
}
if (_ObjectState == NOPE::os_released)
{
removeFromReleased();
}
else
{
TReleasedObject::iterator it(_ReleasedObject.find(_ReleaseDate));
if (it != _ReleasedObject.end())
{
nlassert(it->second.find(this) == it->second.end());
}
}
}
void CSessionParticipant::removeFromReleased()
{
TReleasedObject::iterator it(_ReleasedObject.find(_ReleaseDate));
nlassert(it != _ReleasedObject.end());
TObjectSet &os = it->second;
nlverify(os.erase(this) == 1);
// nb : _ReleasedObject time entry are removed by the cache update
}
bool CSessionParticipant::create(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_transient);
std::string qs;
qs = "INSERT INTO session_participant (";
qs += "session_id, char_id, status, kicked";
qs += ") VALUES (";
qs += "'"+MSW::escapeString(NLMISC::toString(_SessionId), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_CharId), connection)+"'";
qs += ", ";
qs += "'"+_Status.toString()+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_Kicked), connection)+"'";
qs += ")";
if (connection.query(qs))
{
uint32 _id_ = connection.getLastGeneratedId();
setObjectId(_id_);
setPersistentState(NOPE::os_clean);
// update the parent class instance in cache if any
if (_CharId != 0)
{
// need to update the parent class child list if it is in the cache
CCharacter *parent = CCharacter::loadFromCache(_CharId, false);
if (parent && parent->_SessionParticipants != NULL)
{
nlassert(std::find(parent->_SessionParticipants->begin(), parent->_SessionParticipants->end(), CSessionParticipantPtr(this, __FILE__, __LINE__)) == parent->_SessionParticipants->end());
parent->_SessionParticipants->push_back(CSessionParticipantPtr(this, __FILE__, __LINE__));
}
}
if (_SessionId != 0)
{
// need to update the parent class child list if it is in the cache
CSession *parent = CSession::loadFromCache(_SessionId, false);
if (parent && parent->_SessionParticipants != NULL)
{
nlassert(std::find(parent->_SessionParticipants->begin(), parent->_SessionParticipants->end(), CSessionParticipantPtr(this, __FILE__, __LINE__)) == parent->_SessionParticipants->end());
parent->_SessionParticipants->push_back(CSessionParticipantPtr(this, __FILE__, __LINE__));
}
}
return true;
}
return false;
}
bool CSessionParticipant::update(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_dirty || getPersistentState() == NOPE::os_clean);
if (getPersistentState() == NOPE::os_clean)
// the object is clean, just ignore the save
return true;
std::string qs;
qs = "UPDATE session_participant SET ";
qs += "session_id = '"+MSW::escapeString(NLMISC::toString(_SessionId), connection)+"'";
qs += ", ";
qs += "char_id = '"+MSW::escapeString(NLMISC::toString(_CharId), connection)+"'";
qs += ", ";
qs += "status = '"+_Status.toString()+"'";
qs += ", ";
qs += "kicked = '"+MSW::escapeString(NLMISC::toString(_Kicked), connection)+"'";
qs += " WHERE Id = '"+NLMISC::toString(_Id)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
setPersistentState(NOPE::os_clean);
return true;
}
}
return false;
}
bool CSessionParticipant::remove(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_dirty || getPersistentState() == NOPE::os_clean);
std::string qs;
qs = "DELETE FROM session_participant ";
qs += " WHERE Id = '"+NLMISC::toString(_Id)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
// change the persistant state to 'removed'.
setPersistentState(NOPE::os_removed);
// need to remove ref from parent class container (if any)
{
CCharacterPtr parent(CCharacter::loadFromCache(_CharId, true), __FILE__, __LINE__);
if (parent != NULL && parent->_SessionParticipants != NULL)
{
std::vector < CSessionParticipantPtr >::iterator it = std::find(parent->_SessionParticipants->begin(), parent->_SessionParticipants->end(), this);
if (it != parent->_SessionParticipants->end())
{
parent->_SessionParticipants->erase(it);
}
}
}
{
CSessionPtr parent(CSession::loadFromCache(_SessionId, true), __FILE__, __LINE__);
if (parent != NULL && parent->_SessionParticipants != NULL)
{
std::vector < CSessionParticipantPtr >::iterator it = std::find(parent->_SessionParticipants->begin(), parent->_SessionParticipants->end(), this);
if (it != parent->_SessionParticipants->end())
{
parent->_SessionParticipants->erase(it);
}
}
}
// need to remove ref from parent (if any)
return true;
}
}
return false;
}
bool CSessionParticipant::removeById(MSW::CConnection &connection, uint32 id)
{
CSessionParticipant *object = loadFromCache(id, true);
if (object != NULL)
{
return object->remove(connection);
}
// not in cache, run a SQL query
std::string qs;
qs = "DELETE FROM session_participant ";
qs += " WHERE Id = '"+NLMISC::toString(id)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
// ok, the row is removed
return true;
}
}
return false;
}
// Try to load the specified object from the memory cache, return NULL if the object is not in the cache
CSessionParticipant *CSessionParticipant::loadFromCache(uint32 objectId, bool unrelease)
{
// look in the cache
TObjectCache::iterator it(_ObjectCache.find(objectId));
if (it == _ObjectCache.end())
{
// not found, return null
return NULL;
}
else
{
CSessionParticipant *object = it->second;
if (object->_ObjectState == NOPE::os_released)
{
if (unrelease)
{
// we need to remove this object from the released object set.
object->removeFromReleased();
object->_ObjectState = NOPE::os_clean;
}
}
return it->second;
}
}
// Receive and execute command from the cache manager.
uint32 CSessionParticipant::cacheCmd(NOPE::TCacheCmd cmd)
{
if (cmd == NOPE::cc_update)
{
updateCache();
}
else if (cmd == NOPE::cc_clear)
{
clearCache();
}
else if (cmd == NOPE::cc_dump)
{
dump();
}
else if (cmd == NOPE::cc_instance_count)
{
return (uint32)_ObjectCache.size();
}
// default return value
return 0;
}
void CSessionParticipant::dump()
{
nlinfo(" Cache info for class CSessionParticipant :");
nlinfo(" There are %u object instances in cache", _ObjectCache.size());
// count the number of object in the released object set
uint32 nbReleased = 0;
TReleasedObject::iterator first(_ReleasedObject.begin()), last(_ReleasedObject.end());
for (; first != last; ++first)
{
nbReleased += (uint32)first->second.size();
}
nlinfo(" There are %u object instances in cache not referenced (waiting deletion or re-use))", nbReleased);
}
void CSessionParticipant::updateCache()
{
if (_ReleasedObject.empty())
return;
// 30 s hold in cache
const time_t MAX_CACHE_OLD_TIME = 30;
time_t now = NLMISC::CTime::getSecondsSince1970();
// look for object set older than MAX_CACHE_OLD_TIME and delete them
while (!_ReleasedObject.empty() && _ReleasedObject.begin()->first < now-MAX_CACHE_OLD_TIME)
{
TObjectSet &delSet = _ReleasedObject.begin()->second;
// unload this objects
while (!delSet.empty())
{
CSessionParticipant *object = *delSet.begin();
delete object;
}
_ReleasedObject.erase(_ReleasedObject.begin());
}
}
void CSessionParticipant::clearCache()
{
// remove any unreferenced object from the cache
while (!_ReleasedObject.empty())
{
TObjectSet &delSet = _ReleasedObject.begin()->second;
// unload this objects
while (!delSet.empty())
{
CSessionParticipant *object = *delSet.begin();
delete object;
}
_ReleasedObject.erase(_ReleasedObject.begin());
}
}
void CSessionParticipant::registerUpdatable()
{
static bool registered = false;
if (!registered)
{
NOPE::CPersistentCache::getInstance().registerCache(&CSessionParticipant::cacheCmd);
registered = true;
}
}
// set the pointer on the first pointer of the pointer list (set to null when there is no more pointer)
void CSessionParticipant::setFirstPtr(CSessionParticipantPtr *ptr)
{
_PtrList = ptr;
if (ptr == NULL)
{
// this is the last pointer !
if (_ObjectState == NOPE::os_transient
|| _ObjectState == NOPE::os_removed)
{
// not a persistent object, or removed object, just delet it
delete this;
}
else if (_ObjectState != NOPE::os_removed)
{
setPersistentState(NOPE::os_released);
}
}
}
// Set the persistent state of the object and do some house keeping
void CSessionParticipant::setPersistentState(NOPE::TObjectState state)
{
nlassert(NOPE::AllowedTransition[_ObjectState][state] == true);
if(_ObjectState == NOPE::os_released && state == NOPE::os_removed)
{
// a release object gets removed (e.g. by remove by id)
// delete the object
delete this;
// no more to do
return;
}
if (_ObjectState == NOPE::os_transient && state != NOPE::os_transient)
{
nldebug("NOPE: inserting CSessionParticipant @%p in cache with id %u", this, static_cast(_Id));
nlverify(_ObjectCache.insert(std::make_pair(_Id, this)).second);
}
if (_ObjectState != NOPE::os_transient)
nlassert(_ObjectCache.find(_Id) != _ObjectCache.end());
_ObjectState = state;
if (state == NOPE::os_released)
{
_ReleaseDate = NLMISC::CTime::getSecondsSince1970();
nlverify(_ReleasedObject[_ReleaseDate].insert(this).second);
}
else if (state == NOPE::os_removed)
{
nldebug("NOPE: erasing CSessionParticipant @%p in cache with id %u", this, static_cast(_Id));
nlverify(_ObjectCache.erase(_Id) == 1);
}
}
CSessionParticipantPtr CSessionParticipant::load(MSW::CConnection &connection, uint32 id, const char *filename, uint32 lineNum)
{
CSessionParticipant *inCache = loadFromCache(id, true);
if (inCache != NULL)
{
return CSessionParticipantPtr(inCache, filename, lineNum);
}
std::string qs;
qs = "SELECT ";
qs += "Id, session_id, char_id, status, kicked";
qs += " FROM session_participant";
qs += " WHERE Id = '"+NLMISC::toString(id)+"'";
CSessionParticipantPtr ret;
if (!connection.query(qs))
{
return ret;
}
MSW::CStoreResult *result = connection.storeResult().release();
nlassert(result->getNumRows() <= 1);
if (result->getNumRows() == 1)
{
ret.assign(new CSessionParticipant, filename, lineNum);
// ok, we have an object
result->fetchRow();
result->getField(0, ret->_Id);
result->getField(1, ret->_SessionId);
result->getField(2, ret->_CharId);
{
std::string s;
result->getField(3, s);
ret->_Status = TSessionPartStatus(s);
}
result->getField(4, ret->_Kicked);
ret->setPersistentState(NOPE::os_clean);
}
delete result;
return ret;
}
bool CSessionParticipant::loadChildrenOfCCharacter(MSW::CConnection &connection, uint32 parentId, std::vector < CSessionParticipantPtr > & container, const char *filename, uint32 lineNum)
{
std::string qs;
qs = "SELECT ";
qs += "Id, session_id, char_id, status, kicked";
qs += " FROM session_participant";
qs += " WHERE char_id = '"+NLMISC::toString(parentId)+"'";
if (!connection.query(qs))
{
return false;
}
std::unique_ptr result(connection.storeResult());
for (uint i=0; igetNumRows(); ++i)
{
CSessionParticipant *ret = new CSessionParticipant();
// ok, we have an object
result->fetchRow();
result->getField(0, ret->_Id);
result->getField(1, ret->_SessionId);
result->getField(2, ret->_CharId);
{
std::string s;
result->getField(3, s);
ret->_Status = TSessionPartStatus(s);
}
result->getField(4, ret->_Kicked);
CSessionParticipant *inCache = loadFromCache(ret->_Id, true);
if (inCache != NULL)
{
container.push_back(CSessionParticipantPtr(inCache, filename, lineNum));
// no more needed
delete ret;
}
else
{
ret->setPersistentState(NOPE::os_clean);
container.push_back(CSessionParticipantPtr(ret, filename, lineNum));
}
}
return true;
}
bool CSessionParticipant::loadChildrenOfCSession(MSW::CConnection &connection, uint32 parentId, std::vector < CSessionParticipantPtr > & container, const char *filename, uint32 lineNum)
{
std::string qs;
qs = "SELECT ";
qs += "Id, session_id, char_id, status, kicked";
qs += " FROM session_participant";
qs += " WHERE session_id = '"+NLMISC::toString(parentId)+"'";
if (!connection.query(qs))
{
return false;
}
std::unique_ptr result(connection.storeResult());
for (uint i=0; igetNumRows(); ++i)
{
CSessionParticipant *ret = new CSessionParticipant();
// ok, we have an object
result->fetchRow();
result->getField(0, ret->_Id);
result->getField(1, ret->_SessionId);
result->getField(2, ret->_CharId);
{
std::string s;
result->getField(3, s);
ret->_Status = TSessionPartStatus(s);
}
result->getField(4, ret->_Kicked);
CSessionParticipant *inCache = loadFromCache(ret->_Id, true);
if (inCache != NULL)
{
container.push_back(CSessionParticipantPtr(inCache, filename, lineNum));
// no more needed
delete ret;
}
else
{
ret->setPersistentState(NOPE::os_clean);
container.push_back(CSessionParticipantPtr(ret, filename, lineNum));
}
}
return true;
}
void CCharacterPtr::linkPtr()
{
nlassert(_NextPtr == NULL);
nlassert(_PrevPtr == NULL);
if (_Ptr != NULL)
{
_NextPtr = _Ptr->getFirstPtr();
if (_NextPtr != NULL)
{
_PrevPtr = _NextPtr->_PrevPtr;
_PrevPtr->_NextPtr = this;
_NextPtr->_PrevPtr = this;
}
else
{
_NextPtr = this;
_PrevPtr = this;
_Ptr->setFirstPtr(this);
}
}
}
void CCharacterPtr::unlinkPtr()
{
if (_NextPtr == NULL)
{
nlassert(_PrevPtr == NULL);
return;
}
if (_Ptr != NULL)
{
if (_NextPtr == this)
{
nlassert(_PrevPtr == this);
// last pointer !
_Ptr->setFirstPtr(NULL);
}
else
{
if (_Ptr->getFirstPtr() == this)
{
// the first ptr is the current one, we need to switch to next one
_Ptr->setFirstPtr(_NextPtr);
}
}
}
if (_NextPtr != this)
{
nlassert(_PrevPtr != this);
_NextPtr->_PrevPtr = _PrevPtr;
_PrevPtr->_NextPtr = _NextPtr;
}
_NextPtr = NULL;
_PrevPtr = NULL;
}
CCharacter::TObjectCache CCharacter::_ObjectCache;
CCharacter::TReleasedObject CCharacter::_ReleasedObject;
// Destructor, delete any children
CCharacter::~CCharacter()
{
// release childs reference
if (_Sessions != NULL)
delete _Sessions;
if (_SessionParticipants != NULL)
delete _SessionParticipants;
if (_KnownBy != NULL)
delete _KnownBy;
if (_PlayerRatings != NULL)
delete _PlayerRatings;
if (_PtrList != NULL)
{
nlwarning("ERROR : someone try to delete this object, but there are still ptr on it !");
CCharacterPtr *ptr = _PtrList;
do
{
nlwarning(" Pointer created from '%s', line %u", ptr->_FileName, ptr->_LineNum);
ptr = _PtrList->getNextPtr();
} while(ptr != _PtrList);
nlstop;
}
// remove object from cache map
if (_CharId != NOPE::INVALID_OBJECT_ID
&& _ObjectState != NOPE::os_removed
&& _ObjectState != NOPE::os_transient)
{
nldebug("NOPE: clearing CCharacter @%p from cache with id %u", this, static_cast(_CharId));
nlverify(_ObjectCache.erase(_CharId) == 1);
}
else if (_ObjectState != NOPE::os_transient)
{
nlassert(_ObjectCache.find(_CharId) == _ObjectCache.end());
}
if (_ObjectState == NOPE::os_released)
{
removeFromReleased();
}
else
{
TReleasedObject::iterator it(_ReleasedObject.find(_ReleaseDate));
if (it != _ReleasedObject.end())
{
nlassert(it->second.find(this) == it->second.end());
}
}
}
void CCharacter::removeFromReleased()
{
TReleasedObject::iterator it(_ReleasedObject.find(_ReleaseDate));
nlassert(it != _ReleasedObject.end());
TObjectSet &os = it->second;
nlverify(os.erase(this) == 1);
// nb : _ReleasedObject time entry are removed by the cache update
}
bool CCharacter::create(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_transient);
nlassert(_CharId != 0);
std::string qs;
qs = "INSERT INTO characters (";
qs += "char_id, char_name, user_id, guild_id, best_combat_level, home_mainland_session_id, ring_access, race, civilisation, cult, current_session, rrp_am, rrp_masterless, rrp_author, newcomer, creation_date, last_played_date";
qs += ") VALUES (";
qs += "'"+MSW::escapeString(NLMISC::toString(_CharId), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_CharName), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_UserId), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_GuildId), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_BestCombatLevel), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_HomeMainlandSessionId), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_RingAccess), connection)+"'";
qs += ", ";
qs += "'"+_Race.toString()+"'";
qs += ", ";
qs += "'"+_Civilisation.toString()+"'";
qs += ", ";
qs += "'"+_Cult.toString()+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_CurrentSession), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_RRPAM), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_RRPMasterless), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_RRPAuthor), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_Newcomer), connection)+"'";
qs += ", ";
qs += "'"+MSW::encodeDate(_CreationDate)+"'";
qs += ", ";
qs += "'"+MSW::encodeDate(_LastPlayedDate)+"'";
qs += ")";
if (connection.query(qs))
{
setPersistentState(NOPE::os_clean);
// update the parent class instance in cache if any
if (_UserId != 0)
{
// need to update the parent class child list if it is in the cache
CRingUser *parent = CRingUser::loadFromCache(_UserId, false);
if (parent && parent->_Characters != NULL)
{
nlverify(parent->_Characters->insert(std::make_pair(getObjectId(), CCharacterPtr(this, __FILE__, __LINE__))).second);
}
}
if (_GuildId != 0)
{
// need to update the parent class child list if it is in the cache
CGuild *parent = CGuild::loadFromCache(_GuildId, false);
if (parent && parent->_Characters != NULL)
{
nlassert(std::find(parent->_Characters->begin(), parent->_Characters->end(), CCharacterPtr(this, __FILE__, __LINE__)) == parent->_Characters->end());
parent->_Characters->push_back(CCharacterPtr(this, __FILE__, __LINE__));
}
}
return true;
}
return false;
}
bool CCharacter::update(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_dirty || getPersistentState() == NOPE::os_clean);
if (getPersistentState() == NOPE::os_clean)
// the object is clean, just ignore the save
return true;
std::string qs;
qs = "UPDATE characters SET ";
qs += "char_id = '"+MSW::escapeString(NLMISC::toString(_CharId), connection)+"'";
qs += ", ";
qs += "char_name = '"+MSW::escapeString(NLMISC::toString(_CharName), connection)+"'";
qs += ", ";
qs += "user_id = '"+MSW::escapeString(NLMISC::toString(_UserId), connection)+"'";
qs += ", ";
qs += "guild_id = '"+MSW::escapeString(NLMISC::toString(_GuildId), connection)+"'";
qs += ", ";
qs += "best_combat_level = '"+MSW::escapeString(NLMISC::toString(_BestCombatLevel), connection)+"'";
qs += ", ";
qs += "home_mainland_session_id = '"+MSW::escapeString(NLMISC::toString(_HomeMainlandSessionId), connection)+"'";
qs += ", ";
qs += "ring_access = '"+MSW::escapeString(NLMISC::toString(_RingAccess), connection)+"'";
qs += ", ";
qs += "race = '"+_Race.toString()+"'";
qs += ", ";
qs += "civilisation = '"+_Civilisation.toString()+"'";
qs += ", ";
qs += "cult = '"+_Cult.toString()+"'";
qs += ", ";
qs += "current_session = '"+MSW::escapeString(NLMISC::toString(_CurrentSession), connection)+"'";
qs += ", ";
qs += "rrp_am = '"+MSW::escapeString(NLMISC::toString(_RRPAM), connection)+"'";
qs += ", ";
qs += "rrp_masterless = '"+MSW::escapeString(NLMISC::toString(_RRPMasterless), connection)+"'";
qs += ", ";
qs += "rrp_author = '"+MSW::escapeString(NLMISC::toString(_RRPAuthor), connection)+"'";
qs += ", ";
qs += "newcomer = '"+MSW::escapeString(NLMISC::toString(_Newcomer), connection)+"'";
qs += ", ";
qs += "creation_date = '"+MSW::encodeDate(_CreationDate)+"'";
qs += ", ";
qs += "last_played_date = '"+MSW::encodeDate(_LastPlayedDate)+"'";
qs += " WHERE char_id = '"+NLMISC::toString(_CharId)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
setPersistentState(NOPE::os_clean);
return true;
}
}
return false;
}
bool CCharacter::remove(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_dirty || getPersistentState() == NOPE::os_clean);
std::string qs;
qs = "DELETE FROM characters ";
qs += " WHERE char_id = '"+NLMISC::toString(_CharId)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
{
// cascading deletion for vector child SessionParticipants
nlassert(loadSessionParticipants(connection, __FILE__, __LINE__));
const std::vector < CSessionParticipantPtr > & childs = getSessionParticipants();
while (!childs.empty())
{
getSessionParticipantsByIndex((uint32)childs.size()-1)->remove(connection);
}
}
{
// cascading deletion for vector child KnownBy
nlassert(loadKnownBy(connection, __FILE__, __LINE__));
const std::vector < CKnownUserPtr > & childs = getKnownBy();
while (!childs.empty())
{
getKnownByByIndex((uint32)childs.size()-1)->remove(connection);
}
}
{
// unreference (and update) for vector child PlayerRatings
nlassert(loadPlayerRatings(connection, __FILE__, __LINE__));
const std::vector < CPlayerRatingPtr > & childs = getPlayerRatings();
for (uint i=0; i < childs.size(); ++i)
{
getPlayerRatingsByIndex(i)->setAuthor(0);
getPlayerRatingsByIndex(i)->update(connection);
}
}
// change the persistant state to 'removed'.
setPersistentState(NOPE::os_removed);
// need to remove ref from parent class container (if any)
{
CRingUserPtr parent(CRingUser::loadFromCache(_UserId, true), __FILE__, __LINE__);
if (parent != NULL && parent->_Characters != NULL)
{
parent->_Characters->erase(getObjectId());
}
}
{
CGuildPtr parent(CGuild::loadFromCache(_GuildId, true), __FILE__, __LINE__);
if (parent != NULL && parent->_Characters != NULL)
{
std::vector < CCharacterPtr >::iterator it = std::find(parent->_Characters->begin(), parent->_Characters->end(), this);
if (it != parent->_Characters->end())
{
parent->_Characters->erase(it);
}
}
}
// need to remove ref from parent (if any)
return true;
}
}
return false;
}
bool CCharacter::removeById(MSW::CConnection &connection, uint32 id)
{
CCharacter *object = loadFromCache(id, true);
if (object != NULL)
{
return object->remove(connection);
}
// not in cache, run a SQL query
std::string qs;
qs = "DELETE FROM characters ";
qs += " WHERE char_id = '"+NLMISC::toString(id)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
// ok, the row is removed
return true;
}
}
return false;
}
// Try to load the specified object from the memory cache, return NULL if the object is not in the cache
CCharacter *CCharacter::loadFromCache(uint32 objectId, bool unrelease)
{
// look in the cache
TObjectCache::iterator it(_ObjectCache.find(objectId));
if (it == _ObjectCache.end())
{
// not found, return null
return NULL;
}
else
{
CCharacter *object = it->second;
if (object->_ObjectState == NOPE::os_released)
{
if (unrelease)
{
// we need to remove this object from the released object set.
object->removeFromReleased();
object->_ObjectState = NOPE::os_clean;
}
}
return it->second;
}
}
// Receive and execute command from the cache manager.
uint32 CCharacter::cacheCmd(NOPE::TCacheCmd cmd)
{
if (cmd == NOPE::cc_update)
{
updateCache();
}
else if (cmd == NOPE::cc_clear)
{
clearCache();
}
else if (cmd == NOPE::cc_dump)
{
dump();
}
else if (cmd == NOPE::cc_instance_count)
{
return (uint32)_ObjectCache.size();
}
// default return value
return 0;
}
void CCharacter::dump()
{
nlinfo(" Cache info for class CCharacter :");
nlinfo(" There are %u object instances in cache", _ObjectCache.size());
// count the number of object in the released object set
uint32 nbReleased = 0;
TReleasedObject::iterator first(_ReleasedObject.begin()), last(_ReleasedObject.end());
for (; first != last; ++first)
{
nbReleased += (uint32)first->second.size();
}
nlinfo(" There are %u object instances in cache not referenced (waiting deletion or re-use))", nbReleased);
}
void CCharacter::updateCache()
{
if (_ReleasedObject.empty())
return;
// 30 s hold in cache
const time_t MAX_CACHE_OLD_TIME = 30;
time_t now = NLMISC::CTime::getSecondsSince1970();
// look for object set older than MAX_CACHE_OLD_TIME and delete them
while (!_ReleasedObject.empty() && _ReleasedObject.begin()->first < now-MAX_CACHE_OLD_TIME)
{
TObjectSet &delSet = _ReleasedObject.begin()->second;
// unload this objects
while (!delSet.empty())
{
CCharacter *object = *delSet.begin();
delete object;
}
_ReleasedObject.erase(_ReleasedObject.begin());
}
}
void CCharacter::clearCache()
{
// remove any unreferenced object from the cache
while (!_ReleasedObject.empty())
{
TObjectSet &delSet = _ReleasedObject.begin()->second;
// unload this objects
while (!delSet.empty())
{
CCharacter *object = *delSet.begin();
delete object;
}
_ReleasedObject.erase(_ReleasedObject.begin());
}
}
void CCharacter::registerUpdatable()
{
static bool registered = false;
if (!registered)
{
NOPE::CPersistentCache::getInstance().registerCache(&CCharacter::cacheCmd);
registered = true;
}
}
// set the pointer on the first pointer of the pointer list (set to null when there is no more pointer)
void CCharacter::setFirstPtr(CCharacterPtr *ptr)
{
_PtrList = ptr;
if (ptr == NULL)
{
// this is the last pointer !
if (_ObjectState == NOPE::os_transient
|| _ObjectState == NOPE::os_removed)
{
// not a persistent object, or removed object, just delet it
delete this;
}
else if (_ObjectState != NOPE::os_removed)
{
setPersistentState(NOPE::os_released);
}
}
}
// Set the persistent state of the object and do some house keeping
void CCharacter::setPersistentState(NOPE::TObjectState state)
{
nlassert(NOPE::AllowedTransition[_ObjectState][state] == true);
if(_ObjectState == NOPE::os_released && state == NOPE::os_removed)
{
// a release object gets removed (e.g. by remove by id)
// delete the object
delete this;
// no more to do
return;
}
if (_ObjectState == NOPE::os_transient && state != NOPE::os_transient)
{
nldebug("NOPE: inserting CCharacter @%p in cache with id %u", this, static_cast(_CharId));
nlverify(_ObjectCache.insert(std::make_pair(_CharId, this)).second);
}
if (_ObjectState != NOPE::os_transient)
nlassert(_ObjectCache.find(_CharId) != _ObjectCache.end());
_ObjectState = state;
if (state == NOPE::os_released)
{
_ReleaseDate = NLMISC::CTime::getSecondsSince1970();
nlverify(_ReleasedObject[_ReleaseDate].insert(this).second);
}
else if (state == NOPE::os_removed)
{
nldebug("NOPE: erasing CCharacter @%p in cache with id %u", this, static_cast(_CharId));
nlverify(_ObjectCache.erase(_CharId) == 1);
}
}
CCharacterPtr CCharacter::load(MSW::CConnection &connection, uint32 id, const char *filename, uint32 lineNum)
{
CCharacter *inCache = loadFromCache(id, true);
if (inCache != NULL)
{
return CCharacterPtr(inCache, filename, lineNum);
}
std::string qs;
qs = "SELECT ";
qs += "char_id, char_name, user_id, guild_id, best_combat_level, home_mainland_session_id, ring_access, race, civilisation, cult, current_session, rrp_am, rrp_masterless, rrp_author, newcomer, creation_date, last_played_date";
qs += " FROM characters";
qs += " WHERE char_id = '"+NLMISC::toString(id)+"'";
CCharacterPtr ret;
if (!connection.query(qs))
{
return ret;
}
MSW::CStoreResult *result = connection.storeResult().release();
nlassert(result->getNumRows() <= 1);
if (result->getNumRows() == 1)
{
ret.assign(new CCharacter, filename, lineNum);
// ok, we have an object
result->fetchRow();
result->getField(0, ret->_CharId);
result->getField(1, ret->_CharName);
result->getField(2, ret->_UserId);
result->getField(3, ret->_GuildId);
result->getField(4, ret->_BestCombatLevel);
result->getField(5, ret->_HomeMainlandSessionId);
result->getField(6, ret->_RingAccess);
{
std::string s;
result->getField(7, s);
ret->_Race = CHARSYNC::TRace(s);
}
{
std::string s;
result->getField(8, s);
ret->_Civilisation = CHARSYNC::TCivilisation(s);
}
{
std::string s;
result->getField(9, s);
ret->_Cult = CHARSYNC::TCult(s);
}
result->getField(10, ret->_CurrentSession);
result->getField(11, ret->_RRPAM);
result->getField(12, ret->_RRPMasterless);
result->getField(13, ret->_RRPAuthor);
result->getField(14, ret->_Newcomer);
result->getDateField(15, ret->_CreationDate);
result->getDateField(16, ret->_LastPlayedDate);
ret->setPersistentState(NOPE::os_clean);
}
delete result;
return ret;
}
bool CCharacter::loadChildrenOfCRingUser(MSW::CConnection &connection, uint32 parentId, std::map < uint32, CCharacterPtr > & container, const char *filename, uint32 lineNum)
{
std::string qs;
qs = "SELECT ";
qs += "char_id, char_name, user_id, guild_id, best_combat_level, home_mainland_session_id, ring_access, race, civilisation, cult, current_session, rrp_am, rrp_masterless, rrp_author, newcomer, creation_date, last_played_date";
qs += " FROM characters";
qs += " WHERE user_id = '"+NLMISC::toString(parentId)+"'";
if (!connection.query(qs))
{
return false;
}
std::unique_ptr result(connection.storeResult());
for (uint i=0; igetNumRows(); ++i)
{
CCharacter *ret = new CCharacter();
// ok, we have an object
result->fetchRow();
result->getField(0, ret->_CharId);
result->getField(1, ret->_CharName);
result->getField(2, ret->_UserId);
result->getField(3, ret->_GuildId);
result->getField(4, ret->_BestCombatLevel);
result->getField(5, ret->_HomeMainlandSessionId);
result->getField(6, ret->_RingAccess);
{
std::string s;
result->getField(7, s);
ret->_Race = CHARSYNC::TRace(s);
}
{
std::string s;
result->getField(8, s);
ret->_Civilisation = CHARSYNC::TCivilisation(s);
}
{
std::string s;
result->getField(9, s);
ret->_Cult = CHARSYNC::TCult(s);
}
result->getField(10, ret->_CurrentSession);
result->getField(11, ret->_RRPAM);
result->getField(12, ret->_RRPMasterless);
result->getField(13, ret->_RRPAuthor);
result->getField(14, ret->_Newcomer);
result->getDateField(15, ret->_CreationDate);
result->getDateField(16, ret->_LastPlayedDate);
CCharacter *inCache = loadFromCache(ret->_CharId, true);
if (inCache != NULL)
{
container.insert(std::make_pair(inCache->getObjectId(), CCharacterPtr(inCache, filename, lineNum)));
// no more needed
delete ret;
}
else
{
ret->setPersistentState(NOPE::os_clean);
container.insert(std::make_pair(ret->getObjectId(), CCharacterPtr(ret, filename, lineNum)));
}
}
return true;
}
bool CCharacter::loadChildrenOfCGuild(MSW::CConnection &connection, uint32 parentId, std::vector < CCharacterPtr > & container, const char *filename, uint32 lineNum)
{
std::string qs;
qs = "SELECT ";
qs += "char_id, char_name, user_id, guild_id, best_combat_level, home_mainland_session_id, ring_access, race, civilisation, cult, current_session, rrp_am, rrp_masterless, rrp_author, newcomer, creation_date, last_played_date";
qs += " FROM characters";
qs += " WHERE guild_id = '"+NLMISC::toString(parentId)+"'";
if (!connection.query(qs))
{
return false;
}
std::unique_ptr result(connection.storeResult());
for (uint i=0; igetNumRows(); ++i)
{
CCharacter *ret = new CCharacter();
// ok, we have an object
result->fetchRow();
result->getField(0, ret->_CharId);
result->getField(1, ret->_CharName);
result->getField(2, ret->_UserId);
result->getField(3, ret->_GuildId);
result->getField(4, ret->_BestCombatLevel);
result->getField(5, ret->_HomeMainlandSessionId);
result->getField(6, ret->_RingAccess);
{
std::string s;
result->getField(7, s);
ret->_Race = CHARSYNC::TRace(s);
}
{
std::string s;
result->getField(8, s);
ret->_Civilisation = CHARSYNC::TCivilisation(s);
}
{
std::string s;
result->getField(9, s);
ret->_Cult = CHARSYNC::TCult(s);
}
result->getField(10, ret->_CurrentSession);
result->getField(11, ret->_RRPAM);
result->getField(12, ret->_RRPMasterless);
result->getField(13, ret->_RRPAuthor);
result->getField(14, ret->_Newcomer);
result->getDateField(15, ret->_CreationDate);
result->getDateField(16, ret->_LastPlayedDate);
CCharacter *inCache = loadFromCache(ret->_CharId, true);
if (inCache != NULL)
{
container.push_back(CCharacterPtr(inCache, filename, lineNum));
// no more needed
delete ret;
}
else
{
ret->setPersistentState(NOPE::os_clean);
container.push_back(CCharacterPtr(ret, filename, lineNum));
}
}
return true;
}
bool CCharacter::loadSessions(MSW::CConnection &connection, const char *filename, uint32 lineNum)
{
bool ret = true;
if (_Sessions != NULL)
{
// the children are already loaded, just return true
return true;
}
// allocate the container
_Sessions = new std::vector < CSessionPtr >;
// load the childs
ret &= CSession::loadChildrenOfCCharacter(connection, getObjectId(), *_Sessions, filename, lineNum);
return ret;
}
const std::vector &CCharacter::getSessions() const
{
nlassert(_Sessions != NULL);
return *_Sessions;
}
CSessionPtr &CCharacter::getSessionsByIndex(uint32 index) const
{
nlassert(_Sessions != NULL);
nlassert(index < _Sessions->size());
return const_cast< CSessionPtr & >(_Sessions->operator[](index));
}
CSessionPtr &CCharacter::getSessionsById(uint32 id) const
{
nlassert(_Sessions != NULL);
std::vector::const_iterator first(_Sessions->begin()), last(_Sessions->end());
for (; first != last; ++first)
{
const CSessionPtr &child = *first;
if (child->getObjectId() == id)
{
return const_cast< CSessionPtr & >(child);
}
}
// no object with this id, return a null pointer
static CSessionPtr nil;
return nil;
}
bool CCharacter::loadSessionParticipants(MSW::CConnection &connection, const char *filename, uint32 lineNum)
{
bool ret = true;
if (_SessionParticipants != NULL)
{
// the children are already loaded, just return true
return true;
}
// allocate the container
_SessionParticipants = new std::vector < CSessionParticipantPtr >;
// load the childs
ret &= CSessionParticipant::loadChildrenOfCCharacter(connection, getObjectId(), *_SessionParticipants, filename, lineNum);
return ret;
}
const std::vector &CCharacter::getSessionParticipants() const
{
nlassert(_SessionParticipants != NULL);
return *_SessionParticipants;
}
CSessionParticipantPtr &CCharacter::getSessionParticipantsByIndex(uint32 index) const
{
nlassert(_SessionParticipants != NULL);
nlassert(index < _SessionParticipants->size());
return const_cast< CSessionParticipantPtr & >(_SessionParticipants->operator[](index));
}
CSessionParticipantPtr &CCharacter::getSessionParticipantsById(uint32 id) const
{
nlassert(_SessionParticipants != NULL);
std::vector::const_iterator first(_SessionParticipants->begin()), last(_SessionParticipants->end());
for (; first != last; ++first)
{
const CSessionParticipantPtr &child = *first;
if (child->getObjectId() == id)
{
return const_cast< CSessionParticipantPtr & >(child);
}
}
// no object with this id, return a null pointer
static CSessionParticipantPtr nil;
return nil;
}
bool CCharacter::loadKnownBy(MSW::CConnection &connection, const char *filename, uint32 lineNum)
{
bool ret = true;
if (_KnownBy != NULL)
{
// the children are already loaded, just return true
return true;
}
// allocate the container
_KnownBy = new std::vector < CKnownUserPtr >;
// load the childs
ret &= CKnownUser::loadChildrenOfCCharacter(connection, getObjectId(), *_KnownBy, filename, lineNum);
return ret;
}
const std::vector &CCharacter::getKnownBy() const
{
nlassert(_KnownBy != NULL);
return *_KnownBy;
}
CKnownUserPtr &CCharacter::getKnownByByIndex(uint32 index) const
{
nlassert(_KnownBy != NULL);
nlassert(index < _KnownBy->size());
return const_cast< CKnownUserPtr & >(_KnownBy->operator[](index));
}
CKnownUserPtr &CCharacter::getKnownByById(uint32 id) const
{
nlassert(_KnownBy != NULL);
std::vector::const_iterator first(_KnownBy->begin()), last(_KnownBy->end());
for (; first != last; ++first)
{
const CKnownUserPtr &child = *first;
if (child->getObjectId() == id)
{
return const_cast< CKnownUserPtr & >(child);
}
}
// no object with this id, return a null pointer
static CKnownUserPtr nil;
return nil;
}
bool CCharacter::loadPlayerRatings(MSW::CConnection &connection, const char *filename, uint32 lineNum)
{
bool ret = true;
if (_PlayerRatings != NULL)
{
// the children are already loaded, just return true
return true;
}
// allocate the container
_PlayerRatings = new std::vector < CPlayerRatingPtr >;
// load the childs
ret &= CPlayerRating::loadChildrenOfCCharacter(connection, getObjectId(), *_PlayerRatings, filename, lineNum);
return ret;
}
const std::vector &CCharacter::getPlayerRatings() const
{
nlassert(_PlayerRatings != NULL);
return *_PlayerRatings;
}
CPlayerRatingPtr &CCharacter::getPlayerRatingsByIndex(uint32 index) const
{
nlassert(_PlayerRatings != NULL);
nlassert(index < _PlayerRatings->size());
return const_cast< CPlayerRatingPtr & >(_PlayerRatings->operator[](index));
}
CPlayerRatingPtr &CCharacter::getPlayerRatingsById(uint32 id) const
{
nlassert(_PlayerRatings != NULL);
std::vector::const_iterator first(_PlayerRatings->begin()), last(_PlayerRatings->end());
for (; first != last; ++first)
{
const CPlayerRatingPtr &child = *first;
if (child->getObjectId() == id)
{
return const_cast< CPlayerRatingPtr & >(child);
}
}
// no object with this id, return a null pointer
static CPlayerRatingPtr nil;
return nil;
}
void CRingUserPtr::linkPtr()
{
nlassert(_NextPtr == NULL);
nlassert(_PrevPtr == NULL);
if (_Ptr != NULL)
{
_NextPtr = _Ptr->getFirstPtr();
if (_NextPtr != NULL)
{
_PrevPtr = _NextPtr->_PrevPtr;
_PrevPtr->_NextPtr = this;
_NextPtr->_PrevPtr = this;
}
else
{
_NextPtr = this;
_PrevPtr = this;
_Ptr->setFirstPtr(this);
}
}
}
void CRingUserPtr::unlinkPtr()
{
if (_NextPtr == NULL)
{
nlassert(_PrevPtr == NULL);
return;
}
if (_Ptr != NULL)
{
if (_NextPtr == this)
{
nlassert(_PrevPtr == this);
// last pointer !
_Ptr->setFirstPtr(NULL);
}
else
{
if (_Ptr->getFirstPtr() == this)
{
// the first ptr is the current one, we need to switch to next one
_Ptr->setFirstPtr(_NextPtr);
}
}
}
if (_NextPtr != this)
{
nlassert(_PrevPtr != this);
_NextPtr->_PrevPtr = _PrevPtr;
_PrevPtr->_NextPtr = _NextPtr;
}
_NextPtr = NULL;
_PrevPtr = NULL;
}
CRingUser::TObjectCache CRingUser::_ObjectCache;
CRingUser::TReleasedObject CRingUser::_ReleasedObject;
// Destructor, delete any children
CRingUser::~CRingUser()
{
// release childs reference
if (_KnownUsers != NULL)
delete _KnownUsers;
if (_Characters != NULL)
delete _Characters;
if (_Folders != NULL)
delete _Folders;
if (_FolderAccess != NULL)
delete _FolderAccess;
if (_PtrList != NULL)
{
nlwarning("ERROR : someone try to delete this object, but there are still ptr on it !");
CRingUserPtr *ptr = _PtrList;
do
{
nlwarning(" Pointer created from '%s', line %u", ptr->_FileName, ptr->_LineNum);
ptr = _PtrList->getNextPtr();
} while(ptr != _PtrList);
nlstop;
}
// remove object from cache map
if (_UserId != NOPE::INVALID_OBJECT_ID
&& _ObjectState != NOPE::os_removed
&& _ObjectState != NOPE::os_transient)
{
nldebug("NOPE: clearing CRingUser @%p from cache with id %u", this, static_cast(_UserId));
nlverify(_ObjectCache.erase(_UserId) == 1);
}
else if (_ObjectState != NOPE::os_transient)
{
nlassert(_ObjectCache.find(_UserId) == _ObjectCache.end());
}
if (_ObjectState == NOPE::os_released)
{
removeFromReleased();
}
else
{
TReleasedObject::iterator it(_ReleasedObject.find(_ReleaseDate));
if (it != _ReleasedObject.end())
{
nlassert(it->second.find(this) == it->second.end());
}
}
}
void CRingUser::removeFromReleased()
{
TReleasedObject::iterator it(_ReleasedObject.find(_ReleaseDate));
nlassert(it != _ReleasedObject.end());
TObjectSet &os = it->second;
nlverify(os.erase(this) == 1);
// nb : _ReleasedObject time entry are removed by the cache update
}
bool CRingUser::create(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_transient);
nlassert(_UserId != 0);
std::string qs;
qs = "INSERT INTO ring_users (";
qs += "user_id, user_name, current_char, current_session, current_activity, current_status, public_level, account_type, content_access_level, description, lang, cookie, current_domain_id, add_privileges";
qs += ") VALUES (";
qs += "'"+MSW::escapeString(NLMISC::toString(_UserId), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_UserName), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_CurrentCharacter), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_CurrentSession), connection)+"'";
qs += ", ";
qs += "'"+_CurrentActivity.toString()+"'";
qs += ", ";
qs += "'"+_CurrentStatus.toString()+"'";
qs += ", ";
qs += "'"+_PublicLevel.toString()+"'";
qs += ", ";
qs += "'"+_AccountType.toString()+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_ContentAccessLevel), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_Description), connection)+"'";
qs += ", ";
qs += "'"+_Lang.toString()+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_Cookie), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_CurrentDomainId), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_AddedPrivileges), connection)+"'";
qs += ")";
if (connection.query(qs))
{
setPersistentState(NOPE::os_clean);
// update the parent class instance in cache if any
return true;
}
return false;
}
bool CRingUser::update(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_dirty || getPersistentState() == NOPE::os_clean);
if (getPersistentState() == NOPE::os_clean)
// the object is clean, just ignore the save
return true;
std::string qs;
qs = "UPDATE ring_users SET ";
qs += "user_id = '"+MSW::escapeString(NLMISC::toString(_UserId), connection)+"'";
qs += ", ";
qs += "user_name = '"+MSW::escapeString(NLMISC::toString(_UserName), connection)+"'";
qs += ", ";
qs += "current_char = '"+MSW::escapeString(NLMISC::toString(_CurrentCharacter), connection)+"'";
qs += ", ";
qs += "current_session = '"+MSW::escapeString(NLMISC::toString(_CurrentSession), connection)+"'";
qs += ", ";
qs += "current_activity = '"+_CurrentActivity.toString()+"'";
qs += ", ";
qs += "current_status = '"+_CurrentStatus.toString()+"'";
qs += ", ";
qs += "public_level = '"+_PublicLevel.toString()+"'";
qs += ", ";
qs += "account_type = '"+_AccountType.toString()+"'";
qs += ", ";
qs += "content_access_level = '"+MSW::escapeString(NLMISC::toString(_ContentAccessLevel), connection)+"'";
qs += ", ";
qs += "description = '"+MSW::escapeString(NLMISC::toString(_Description), connection)+"'";
qs += ", ";
qs += "lang = '"+_Lang.toString()+"'";
qs += ", ";
qs += "cookie = '"+MSW::escapeString(NLMISC::toString(_Cookie), connection)+"'";
qs += ", ";
qs += "current_domain_id = '"+MSW::escapeString(NLMISC::toString(_CurrentDomainId), connection)+"'";
qs += ", ";
qs += "add_privileges = '"+MSW::escapeString(NLMISC::toString(_AddedPrivileges), connection)+"'";
qs += " WHERE user_id = '"+NLMISC::toString(_UserId)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
setPersistentState(NOPE::os_clean);
return true;
}
}
return false;
}
bool CRingUser::remove(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_dirty || getPersistentState() == NOPE::os_clean);
std::string qs;
qs = "DELETE FROM ring_users ";
qs += " WHERE user_id = '"+NLMISC::toString(_UserId)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
{
// cascading deletion for vector child KnownUsers
nlassert(loadKnownUsers(connection, __FILE__, __LINE__));
const std::vector < CKnownUserPtr > & childs = getKnownUsers();
while (!childs.empty())
{
getKnownUsersByIndex((uint32)childs.size()-1)->remove(connection);
}
}
{
// cascading deletion for map child Characters
nlassert(loadCharacters(connection, __FILE__, __LINE__));
const std::map < uint32, CCharacterPtr > & childs = getCharacters();
while (!childs.empty())
{
getCharactersById(childs.begin()->first)->remove(connection);
}
}
{
// cascading deletion for single child GMStatus
nlassert(loadGMStatus(connection, __FILE__, __LINE__));
if (getGMStatus() != NULL)
getGMStatus()->remove(connection);
}
{
// unreference (and update) for vector child Folders
nlassert(loadFolders(connection, __FILE__, __LINE__));
const std::vector < CFolderPtr > & childs = getFolders();
for (uint i=0; i < childs.size(); ++i)
{
getFoldersByIndex(i)->setAuthor(0);
getFoldersByIndex(i)->update(connection);
}
}
{
// unreference (and update) for vector child FolderAccess
nlassert(loadFolderAccess(connection, __FILE__, __LINE__));
const std::vector < CFolderAccessPtr > & childs = getFolderAccess();
for (uint i=0; i < childs.size(); ++i)
{
getFolderAccessByIndex(i)->setUserId(0);
getFolderAccessByIndex(i)->update(connection);
}
}
// change the persistant state to 'removed'.
setPersistentState(NOPE::os_removed);
// need to remove ref from parent class container (if any)
// need to remove ref from parent (if any)
return true;
}
}
return false;
}
bool CRingUser::removeById(MSW::CConnection &connection, uint32 id)
{
CRingUser *object = loadFromCache(id, true);
if (object != NULL)
{
return object->remove(connection);
}
// not in cache, run a SQL query
std::string qs;
qs = "DELETE FROM ring_users ";
qs += " WHERE user_id = '"+NLMISC::toString(id)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
// ok, the row is removed
return true;
}
}
return false;
}
// Try to load the specified object from the memory cache, return NULL if the object is not in the cache
CRingUser *CRingUser::loadFromCache(uint32 objectId, bool unrelease)
{
// look in the cache
TObjectCache::iterator it(_ObjectCache.find(objectId));
if (it == _ObjectCache.end())
{
// not found, return null
return NULL;
}
else
{
CRingUser *object = it->second;
if (object->_ObjectState == NOPE::os_released)
{
if (unrelease)
{
// we need to remove this object from the released object set.
object->removeFromReleased();
object->_ObjectState = NOPE::os_clean;
}
}
return it->second;
}
}
// Receive and execute command from the cache manager.
uint32 CRingUser::cacheCmd(NOPE::TCacheCmd cmd)
{
if (cmd == NOPE::cc_update)
{
updateCache();
}
else if (cmd == NOPE::cc_clear)
{
clearCache();
}
else if (cmd == NOPE::cc_dump)
{
dump();
}
else if (cmd == NOPE::cc_instance_count)
{
return (uint32)_ObjectCache.size();
}
// default return value
return 0;
}
void CRingUser::dump()
{
nlinfo(" Cache info for class CRingUser :");
nlinfo(" There are %u object instances in cache", _ObjectCache.size());
// count the number of object in the released object set
uint32 nbReleased = 0;
TReleasedObject::iterator first(_ReleasedObject.begin()), last(_ReleasedObject.end());
for (; first != last; ++first)
{
nbReleased += (uint32)first->second.size();
}
nlinfo(" There are %u object instances in cache not referenced (waiting deletion or re-use))", nbReleased);
}
void CRingUser::updateCache()
{
if (_ReleasedObject.empty())
return;
// 30 s hold in cache
const time_t MAX_CACHE_OLD_TIME = 30;
time_t now = NLMISC::CTime::getSecondsSince1970();
// look for object set older than MAX_CACHE_OLD_TIME and delete them
while (!_ReleasedObject.empty() && _ReleasedObject.begin()->first < now-MAX_CACHE_OLD_TIME)
{
TObjectSet &delSet = _ReleasedObject.begin()->second;
// unload this objects
while (!delSet.empty())
{
CRingUser *object = *delSet.begin();
delete object;
}
_ReleasedObject.erase(_ReleasedObject.begin());
}
}
void CRingUser::clearCache()
{
// remove any unreferenced object from the cache
while (!_ReleasedObject.empty())
{
TObjectSet &delSet = _ReleasedObject.begin()->second;
// unload this objects
while (!delSet.empty())
{
CRingUser *object = *delSet.begin();
delete object;
}
_ReleasedObject.erase(_ReleasedObject.begin());
}
}
void CRingUser::registerUpdatable()
{
static bool registered = false;
if (!registered)
{
NOPE::CPersistentCache::getInstance().registerCache(&CRingUser::cacheCmd);
registered = true;
}
}
// set the pointer on the first pointer of the pointer list (set to null when there is no more pointer)
void CRingUser::setFirstPtr(CRingUserPtr *ptr)
{
_PtrList = ptr;
if (ptr == NULL)
{
// this is the last pointer !
if (_ObjectState == NOPE::os_transient
|| _ObjectState == NOPE::os_removed)
{
// not a persistent object, or removed object, just delet it
delete this;
}
else if (_ObjectState != NOPE::os_removed)
{
setPersistentState(NOPE::os_released);
}
}
}
// Set the persistent state of the object and do some house keeping
void CRingUser::setPersistentState(NOPE::TObjectState state)
{
nlassert(NOPE::AllowedTransition[_ObjectState][state] == true);
if(_ObjectState == NOPE::os_released && state == NOPE::os_removed)
{
// a release object gets removed (e.g. by remove by id)
// delete the object
delete this;
// no more to do
return;
}
if (_ObjectState == NOPE::os_transient && state != NOPE::os_transient)
{
nldebug("NOPE: inserting CRingUser @%p in cache with id %u", this, static_cast(_UserId));
nlverify(_ObjectCache.insert(std::make_pair(_UserId, this)).second);
}
if (_ObjectState != NOPE::os_transient)
nlassert(_ObjectCache.find(_UserId) != _ObjectCache.end());
_ObjectState = state;
if (state == NOPE::os_released)
{
_ReleaseDate = NLMISC::CTime::getSecondsSince1970();
nlverify(_ReleasedObject[_ReleaseDate].insert(this).second);
}
else if (state == NOPE::os_removed)
{
nldebug("NOPE: erasing CRingUser @%p in cache with id %u", this, static_cast(_UserId));
nlverify(_ObjectCache.erase(_UserId) == 1);
}
}
CRingUserPtr CRingUser::load(MSW::CConnection &connection, uint32 id, const char *filename, uint32 lineNum)
{
CRingUser *inCache = loadFromCache(id, true);
if (inCache != NULL)
{
return CRingUserPtr(inCache, filename, lineNum);
}
std::string qs;
qs = "SELECT ";
qs += "user_id, user_name, current_char, current_session, current_activity, current_status, public_level, account_type, content_access_level, description, lang, cookie, current_domain_id, add_privileges";
qs += " FROM ring_users";
qs += " WHERE user_id = '"+NLMISC::toString(id)+"'";
CRingUserPtr ret;
if (!connection.query(qs))
{
return ret;
}
MSW::CStoreResult *result = connection.storeResult().release();
nlassert(result->getNumRows() <= 1);
if (result->getNumRows() == 1)
{
ret.assign(new CRingUser, filename, lineNum);
// ok, we have an object
result->fetchRow();
result->getField(0, ret->_UserId);
result->getField(1, ret->_UserName);
result->getField(2, ret->_CurrentCharacter);
result->getField(3, ret->_CurrentSession);
{
std::string s;
result->getField(4, s);
ret->_CurrentActivity = TCurrentActivity(s);
}
{
std::string s;
result->getField(5, s);
ret->_CurrentStatus = TCurrentStatus(s);
}
{
std::string s;
result->getField(6, s);
ret->_PublicLevel = TPublicLevel(s);
}
{
std::string s;
result->getField(7, s);
ret->_AccountType = TAccountType(s);
}
result->getField(8, ret->_ContentAccessLevel);
result->getField(9, ret->_Description);
{
std::string s;
result->getField(10, s);
ret->_Lang = TLanguage(s);
}
result->getField(11, ret->_Cookie);
result->getField(12, ret->_CurrentDomainId);
result->getField(13, ret->_AddedPrivileges);
ret->setPersistentState(NOPE::os_clean);
}
delete result;
return ret;
}
bool CRingUser::loadKnownUsers(MSW::CConnection &connection, const char *filename, uint32 lineNum)
{
bool ret = true;
if (_KnownUsers != NULL)
{
// the children are already loaded, just return true
return true;
}
// allocate the container
_KnownUsers = new std::vector < CKnownUserPtr >;
// load the childs
ret &= CKnownUser::loadChildrenOfCRingUser(connection, getObjectId(), *_KnownUsers, filename, lineNum);
return ret;
}
const std::vector &CRingUser::getKnownUsers() const
{
nlassert(_KnownUsers != NULL);
return *_KnownUsers;
}
CKnownUserPtr &CRingUser::getKnownUsersByIndex(uint32 index) const
{
nlassert(_KnownUsers != NULL);
nlassert(index < _KnownUsers->size());
return const_cast< CKnownUserPtr & >(_KnownUsers->operator[](index));
}
CKnownUserPtr &CRingUser::getKnownUsersById(uint32 id) const
{
nlassert(_KnownUsers != NULL);
std::vector::const_iterator first(_KnownUsers->begin()), last(_KnownUsers->end());
for (; first != last; ++first)
{
const CKnownUserPtr &child = *first;
if (child->getObjectId() == id)
{
return const_cast< CKnownUserPtr & >(child);
}
}
// no object with this id, return a null pointer
static CKnownUserPtr nil;
return nil;
}
bool CRingUser::loadCharacters(MSW::CConnection &connection, const char *filename, uint32 lineNum)
{
bool ret = true;
if (_Characters != NULL)
{
// the children are already loaded, just return true
return true;
}
// allocate the container
_Characters = new std::map < uint32, CCharacterPtr >;
// load the childs
ret &= CCharacter::loadChildrenOfCRingUser(connection, getObjectId(), *_Characters, filename, lineNum);
return ret;
}
const std::map &CRingUser::getCharacters() const
{
nlassert(_Characters != NULL);
return *_Characters;
}
CCharacterPtr &CRingUser::getCharactersById(uint32 id) const
{
nlassert(_Characters != NULL);
std::map::const_iterator it(_Characters->find(id));
if (it == _Characters->end())
{
// no object with this id, return a null pointer
static CCharacterPtr nil;
return nil;
}
return const_cast< CCharacterPtr & >(it->second);
}
bool CRingUser::loadFolders(MSW::CConnection &connection, const char *filename, uint32 lineNum)
{
bool ret = true;
if (_Folders != NULL)
{
// the children are already loaded, just return true
return true;
}
// allocate the container
_Folders = new std::vector < CFolderPtr >;
// load the childs
ret &= CFolder::loadChildrenOfCRingUser(connection, getObjectId(), *_Folders, filename, lineNum);
return ret;
}
const std::vector &CRingUser::getFolders() const
{
nlassert(_Folders != NULL);
return *_Folders;
}
CFolderPtr &CRingUser::getFoldersByIndex(uint32 index) const
{
nlassert(_Folders != NULL);
nlassert(index < _Folders->size());
return const_cast< CFolderPtr & >(_Folders->operator[](index));
}
CFolderPtr &CRingUser::getFoldersById(uint32 id) const
{
nlassert(_Folders != NULL);
std::vector::const_iterator first(_Folders->begin()), last(_Folders->end());
for (; first != last; ++first)
{
const CFolderPtr &child = *first;
if (child->getObjectId() == id)
{
return const_cast< CFolderPtr & >(child);
}
}
// no object with this id, return a null pointer
static CFolderPtr nil;
return nil;
}
bool CRingUser::loadFolderAccess(MSW::CConnection &connection, const char *filename, uint32 lineNum)
{
bool ret = true;
if (_FolderAccess != NULL)
{
// the children are already loaded, just return true
return true;
}
// allocate the container
_FolderAccess = new std::vector < CFolderAccessPtr >;
// load the childs
ret &= CFolderAccess::loadChildrenOfCRingUser(connection, getObjectId(), *_FolderAccess, filename, lineNum);
return ret;
}
const std::vector &CRingUser::getFolderAccess() const
{
nlassert(_FolderAccess != NULL);
return *_FolderAccess;
}
CFolderAccessPtr &CRingUser::getFolderAccessByIndex(uint32 index) const
{
nlassert(_FolderAccess != NULL);
nlassert(index < _FolderAccess->size());
return const_cast< CFolderAccessPtr & >(_FolderAccess->operator[](index));
}
CFolderAccessPtr &CRingUser::getFolderAccessById(uint32 id) const
{
nlassert(_FolderAccess != NULL);
std::vector::const_iterator first(_FolderAccess->begin()), last(_FolderAccess->end());
for (; first != last; ++first)
{
const CFolderAccessPtr &child = *first;
if (child->getObjectId() == id)
{
return const_cast< CFolderAccessPtr & >(child);
}
}
// no object with this id, return a null pointer
static CFolderAccessPtr nil;
return nil;
}
bool CRingUser::loadGMStatus(MSW::CConnection &connection, const char *filename, uint32 lineNum)
{
if (_GMStatusLoaded)
{
// the child is already loaded, just return true
return true;
}
bool ret = CGmStatus::loadChildOfCRingUser(connection, getObjectId(), _GMStatus, filename, lineNum);
_GMStatusLoaded = true;
return ret;
}
/** Return the one child object (or null if not) */
CGmStatusPtr CRingUser::getGMStatus()
{
nlassert(_GMStatusLoaded);
return _GMStatus;
}
void CSessionPtr::linkPtr()
{
nlassert(_NextPtr == NULL);
nlassert(_PrevPtr == NULL);
if (_Ptr != NULL)
{
_NextPtr = _Ptr->getFirstPtr();
if (_NextPtr != NULL)
{
_PrevPtr = _NextPtr->_PrevPtr;
_PrevPtr->_NextPtr = this;
_NextPtr->_PrevPtr = this;
}
else
{
_NextPtr = this;
_PrevPtr = this;
_Ptr->setFirstPtr(this);
}
}
}
void CSessionPtr::unlinkPtr()
{
if (_NextPtr == NULL)
{
nlassert(_PrevPtr == NULL);
return;
}
if (_Ptr != NULL)
{
if (_NextPtr == this)
{
nlassert(_PrevPtr == this);
// last pointer !
_Ptr->setFirstPtr(NULL);
}
else
{
if (_Ptr->getFirstPtr() == this)
{
// the first ptr is the current one, we need to switch to next one
_Ptr->setFirstPtr(_NextPtr);
}
}
}
if (_NextPtr != this)
{
nlassert(_PrevPtr != this);
_NextPtr->_PrevPtr = _PrevPtr;
_PrevPtr->_NextPtr = _NextPtr;
}
_NextPtr = NULL;
_PrevPtr = NULL;
}
CSession::TObjectCache CSession::_ObjectCache;
CSession::TReleasedObject CSession::_ReleasedObject;
// Destructor, delete any children
CSession::~CSession()
{
// release childs reference
if (_SessionParticipants != NULL)
delete _SessionParticipants;
if (_GuildInvites != NULL)
delete _GuildInvites;
if (_JournalEntries != NULL)
delete _JournalEntries;
if (_PtrList != NULL)
{
nlwarning("ERROR : someone try to delete this object, but there are still ptr on it !");
CSessionPtr *ptr = _PtrList;
do
{
nlwarning(" Pointer created from '%s', line %u", ptr->_FileName, ptr->_LineNum);
ptr = _PtrList->getNextPtr();
} while(ptr != _PtrList);
nlstop;
}
// remove object from cache map
if (_SessionId != NOPE::INVALID_OBJECT_ID
&& _ObjectState != NOPE::os_removed
&& _ObjectState != NOPE::os_transient)
{
nldebug("NOPE: clearing CSession @%p from cache with id %u", this, static_cast(_SessionId));
nlverify(_ObjectCache.erase(_SessionId) == 1);
}
else if (_ObjectState != NOPE::os_transient)
{
nlassert(_ObjectCache.find(_SessionId) == _ObjectCache.end());
}
if (_ObjectState == NOPE::os_released)
{
removeFromReleased();
}
else
{
TReleasedObject::iterator it(_ReleasedObject.find(_ReleaseDate));
if (it != _ReleasedObject.end())
{
nlassert(it->second.find(this) == it->second.end());
}
}
}
void CSession::removeFromReleased()
{
TReleasedObject::iterator it(_ReleasedObject.find(_ReleaseDate));
nlassert(it != _ReleasedObject.end());
TObjectSet &os = it->second;
nlverify(os.erase(this) == 1);
// nb : _ReleasedObject time entry are removed by the cache update
}
bool CSession::create(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_transient);
std::string qs;
qs = "INSERT INTO sessions (";
qs += "session_type, title, owner, plan_date, start_date, description, orientation, level, rule_type, access_type, state, host_shard_id, subscription_slots, reserved_slots, estimated_duration, final_duration, folder_id, lang, icone, anim_mode, race_filter, religion_filter, guild_filter, shard_filter, level_filter, subscription_closed, newcomer";
qs += ") VALUES (";
qs += "'"+_SessionType.toString()+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_Title), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_OwnerId), connection)+"'";
qs += ", ";
qs += "'"+MSW::encodeDate(_PlanDate)+"'";
qs += ", ";
qs += "'"+MSW::encodeDate(_StartDate)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_Description), connection)+"'";
qs += ", ";
qs += "'"+_Orientation.toString()+"'";
qs += ", ";
qs += "'"+_Level.toString()+"'";
qs += ", ";
qs += "'"+_RuleType.toString()+"'";
qs += ", ";
qs += "'"+_AccessType.toString()+"'";
qs += ", ";
qs += "'"+_State.toString()+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_HostShardId), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_SubscriptionSlots), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_ReservedSlots), connection)+"'";
qs += ", ";
qs += "'"+_EstimatedDuration.toString()+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_FinalDuration), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_FolderId), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_Lang), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_Icone), connection)+"'";
qs += ", ";
qs += "'"+_AnimMode.toString()+"'";
qs += ", ";
qs += "'"+_RaceFilter.toString()+"'";
qs += ", ";
qs += "'"+_ReligionFilter.toString()+"'";
qs += ", ";
qs += "'"+_GuildFilter.toString()+"'";
qs += ", ";
qs += "'"+_ShardFilter.toString()+"'";
qs += ", ";
qs += "'"+_LevelFilter.toString()+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_SubscriptionClosed), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_Newcomer), connection)+"'";
qs += ")";
if (connection.query(qs))
{
uint32 _id_ = connection.getLastGeneratedId();
setObjectId(_id_);
setPersistentState(NOPE::os_clean);
// update the parent class instance in cache if any
if (_OwnerId != 0)
{
// need to update the parent class child list if it is in the cache
CCharacter *parent = CCharacter::loadFromCache(_OwnerId, false);
if (parent && parent->_Sessions != NULL)
{
nlassert(std::find(parent->_Sessions->begin(), parent->_Sessions->end(), CSessionPtr(this, __FILE__, __LINE__)) == parent->_Sessions->end());
parent->_Sessions->push_back(CSessionPtr(this, __FILE__, __LINE__));
}
}
if (_FolderId != 0)
{
// need to update the parent class child list if it is in the cache
CFolder *parent = CFolder::loadFromCache(_FolderId, false);
if (parent && parent->_Sessions != NULL)
{
nlassert(std::find(parent->_Sessions->begin(), parent->_Sessions->end(), CSessionPtr(this, __FILE__, __LINE__)) == parent->_Sessions->end());
parent->_Sessions->push_back(CSessionPtr(this, __FILE__, __LINE__));
}
}
return true;
}
return false;
}
bool CSession::update(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_dirty || getPersistentState() == NOPE::os_clean);
if (getPersistentState() == NOPE::os_clean)
// the object is clean, just ignore the save
return true;
std::string qs;
qs = "UPDATE sessions SET ";
qs += "session_type = '"+_SessionType.toString()+"'";
qs += ", ";
qs += "title = '"+MSW::escapeString(NLMISC::toString(_Title), connection)+"'";
qs += ", ";
qs += "owner = '"+MSW::escapeString(NLMISC::toString(_OwnerId), connection)+"'";
qs += ", ";
qs += "plan_date = '"+MSW::encodeDate(_PlanDate)+"'";
qs += ", ";
qs += "start_date = '"+MSW::encodeDate(_StartDate)+"'";
qs += ", ";
qs += "description = '"+MSW::escapeString(NLMISC::toString(_Description), connection)+"'";
qs += ", ";
qs += "orientation = '"+_Orientation.toString()+"'";
qs += ", ";
qs += "level = '"+_Level.toString()+"'";
qs += ", ";
qs += "rule_type = '"+_RuleType.toString()+"'";
qs += ", ";
qs += "access_type = '"+_AccessType.toString()+"'";
qs += ", ";
qs += "state = '"+_State.toString()+"'";
qs += ", ";
qs += "host_shard_id = '"+MSW::escapeString(NLMISC::toString(_HostShardId), connection)+"'";
qs += ", ";
qs += "subscription_slots = '"+MSW::escapeString(NLMISC::toString(_SubscriptionSlots), connection)+"'";
qs += ", ";
qs += "reserved_slots = '"+MSW::escapeString(NLMISC::toString(_ReservedSlots), connection)+"'";
qs += ", ";
qs += "estimated_duration = '"+_EstimatedDuration.toString()+"'";
qs += ", ";
qs += "final_duration = '"+MSW::escapeString(NLMISC::toString(_FinalDuration), connection)+"'";
qs += ", ";
qs += "folder_id = '"+MSW::escapeString(NLMISC::toString(_FolderId), connection)+"'";
qs += ", ";
qs += "lang = '"+MSW::escapeString(NLMISC::toString(_Lang), connection)+"'";
qs += ", ";
qs += "icone = '"+MSW::escapeString(NLMISC::toString(_Icone), connection)+"'";
qs += ", ";
qs += "anim_mode = '"+_AnimMode.toString()+"'";
qs += ", ";
qs += "race_filter = '"+_RaceFilter.toString()+"'";
qs += ", ";
qs += "religion_filter = '"+_ReligionFilter.toString()+"'";
qs += ", ";
qs += "guild_filter = '"+_GuildFilter.toString()+"'";
qs += ", ";
qs += "shard_filter = '"+_ShardFilter.toString()+"'";
qs += ", ";
qs += "level_filter = '"+_LevelFilter.toString()+"'";
qs += ", ";
qs += "subscription_closed = '"+MSW::escapeString(NLMISC::toString(_SubscriptionClosed), connection)+"'";
qs += ", ";
qs += "newcomer = '"+MSW::escapeString(NLMISC::toString(_Newcomer), connection)+"'";
qs += " WHERE session_id = '"+NLMISC::toString(_SessionId)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
setPersistentState(NOPE::os_clean);
return true;
}
}
return false;
}
bool CSession::remove(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_dirty || getPersistentState() == NOPE::os_clean);
std::string qs;
qs = "DELETE FROM sessions ";
qs += " WHERE session_id = '"+NLMISC::toString(_SessionId)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
{
// cascading deletion for vector child SessionParticipants
nlassert(loadSessionParticipants(connection, __FILE__, __LINE__));
const std::vector < CSessionParticipantPtr > & childs = getSessionParticipants();
while (!childs.empty())
{
getSessionParticipantsByIndex((uint32)childs.size()-1)->remove(connection);
}
}
{
// cascading deletion for vector child GuildInvites
nlassert(loadGuildInvites(connection, __FILE__, __LINE__));
const std::vector < CGuildInvitePtr > & childs = getGuildInvites();
while (!childs.empty())
{
getGuildInvitesByIndex((uint32)childs.size()-1)->remove(connection);
}
}
{
// cascading deletion for vector child JournalEntries
nlassert(loadJournalEntries(connection, __FILE__, __LINE__));
const std::vector < CJournalEntryPtr > & childs = getJournalEntries();
while (!childs.empty())
{
getJournalEntriesByIndex((uint32)childs.size()-1)->remove(connection);
}
}
// change the persistant state to 'removed'.
setPersistentState(NOPE::os_removed);
// need to remove ref from parent class container (if any)
{
CCharacterPtr parent(CCharacter::loadFromCache(_OwnerId, true), __FILE__, __LINE__);
if (parent != NULL && parent->_Sessions != NULL)
{
std::vector < CSessionPtr >::iterator it = std::find(parent->_Sessions->begin(), parent->_Sessions->end(), this);
if (it != parent->_Sessions->end())
{
parent->_Sessions->erase(it);
}
}
}
{
CFolderPtr parent(CFolder::loadFromCache(_FolderId, true), __FILE__, __LINE__);
if (parent != NULL && parent->_Sessions != NULL)
{
std::vector < CSessionPtr >::iterator it = std::find(parent->_Sessions->begin(), parent->_Sessions->end(), this);
if (it != parent->_Sessions->end())
{
parent->_Sessions->erase(it);
}
}
}
// need to remove ref from parent (if any)
return true;
}
}
return false;
}
bool CSession::removeById(MSW::CConnection &connection, uint32 id)
{
CSession *object = loadFromCache(id, true);
if (object != NULL)
{
return object->remove(connection);
}
// not in cache, run a SQL query
std::string qs;
qs = "DELETE FROM sessions ";
qs += " WHERE session_id = '"+NLMISC::toString(id)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
// ok, the row is removed
return true;
}
}
return false;
}
// Try to load the specified object from the memory cache, return NULL if the object is not in the cache
CSession *CSession::loadFromCache(uint32 objectId, bool unrelease)
{
// look in the cache
TObjectCache::iterator it(_ObjectCache.find(objectId));
if (it == _ObjectCache.end())
{
// not found, return null
return NULL;
}
else
{
CSession *object = it->second;
if (object->_ObjectState == NOPE::os_released)
{
if (unrelease)
{
// we need to remove this object from the released object set.
object->removeFromReleased();
object->_ObjectState = NOPE::os_clean;
}
}
return it->second;
}
}
// Receive and execute command from the cache manager.
uint32 CSession::cacheCmd(NOPE::TCacheCmd cmd)
{
if (cmd == NOPE::cc_update)
{
updateCache();
}
else if (cmd == NOPE::cc_clear)
{
clearCache();
}
else if (cmd == NOPE::cc_dump)
{
dump();
}
else if (cmd == NOPE::cc_instance_count)
{
return (uint32)_ObjectCache.size();
}
// default return value
return 0;
}
void CSession::dump()
{
nlinfo(" Cache info for class CSession :");
nlinfo(" There are %u object instances in cache", _ObjectCache.size());
// count the number of object in the released object set
uint32 nbReleased = 0;
TReleasedObject::iterator first(_ReleasedObject.begin()), last(_ReleasedObject.end());
for (; first != last; ++first)
{
nbReleased += (uint32)first->second.size();
}
nlinfo(" There are %u object instances in cache not referenced (waiting deletion or re-use))", nbReleased);
}
void CSession::updateCache()
{
if (_ReleasedObject.empty())
return;
// 30 s hold in cache
const time_t MAX_CACHE_OLD_TIME = 30;
time_t now = NLMISC::CTime::getSecondsSince1970();
// look for object set older than MAX_CACHE_OLD_TIME and delete them
while (!_ReleasedObject.empty() && _ReleasedObject.begin()->first < now-MAX_CACHE_OLD_TIME)
{
TObjectSet &delSet = _ReleasedObject.begin()->second;
// unload this objects
while (!delSet.empty())
{
CSession *object = *delSet.begin();
delete object;
}
_ReleasedObject.erase(_ReleasedObject.begin());
}
}
void CSession::clearCache()
{
// remove any unreferenced object from the cache
while (!_ReleasedObject.empty())
{
TObjectSet &delSet = _ReleasedObject.begin()->second;
// unload this objects
while (!delSet.empty())
{
CSession *object = *delSet.begin();
delete object;
}
_ReleasedObject.erase(_ReleasedObject.begin());
}
}
void CSession::registerUpdatable()
{
static bool registered = false;
if (!registered)
{
NOPE::CPersistentCache::getInstance().registerCache(&CSession::cacheCmd);
registered = true;
}
}
// set the pointer on the first pointer of the pointer list (set to null when there is no more pointer)
void CSession::setFirstPtr(CSessionPtr *ptr)
{
_PtrList = ptr;
if (ptr == NULL)
{
// this is the last pointer !
if (_ObjectState == NOPE::os_transient
|| _ObjectState == NOPE::os_removed)
{
// not a persistent object, or removed object, just delet it
delete this;
}
else if (_ObjectState != NOPE::os_removed)
{
setPersistentState(NOPE::os_released);
}
}
}
// Set the persistent state of the object and do some house keeping
void CSession::setPersistentState(NOPE::TObjectState state)
{
nlassert(NOPE::AllowedTransition[_ObjectState][state] == true);
if(_ObjectState == NOPE::os_released && state == NOPE::os_removed)
{
// a release object gets removed (e.g. by remove by id)
// delete the object
delete this;
// no more to do
return;
}
if (_ObjectState == NOPE::os_transient && state != NOPE::os_transient)
{
nldebug("NOPE: inserting CSession @%p in cache with id %u", this, static_cast(_SessionId));
nlverify(_ObjectCache.insert(std::make_pair(_SessionId, this)).second);
}
if (_ObjectState != NOPE::os_transient)
nlassert(_ObjectCache.find(_SessionId) != _ObjectCache.end());
_ObjectState = state;
if (state == NOPE::os_released)
{
_ReleaseDate = NLMISC::CTime::getSecondsSince1970();
nlverify(_ReleasedObject[_ReleaseDate].insert(this).second);
}
else if (state == NOPE::os_removed)
{
nldebug("NOPE: erasing CSession @%p in cache with id %u", this, static_cast(_SessionId));
nlverify(_ObjectCache.erase(_SessionId) == 1);
}
}
CSessionPtr CSession::load(MSW::CConnection &connection, uint32 id, const char *filename, uint32 lineNum)
{
CSession *inCache = loadFromCache(id, true);
if (inCache != NULL)
{
return CSessionPtr(inCache, filename, lineNum);
}
std::string qs;
qs = "SELECT ";
qs += "session_id, session_type, title, owner, plan_date, start_date, description, orientation, level, rule_type, access_type, state, host_shard_id, subscription_slots, reserved_slots, estimated_duration, final_duration, folder_id, lang, icone, anim_mode, race_filter, religion_filter, guild_filter, shard_filter, level_filter, subscription_closed, newcomer";
qs += " FROM sessions";
qs += " WHERE session_id = '"+NLMISC::toString(id)+"'";
CSessionPtr ret;
if (!connection.query(qs))
{
return ret;
}
MSW::CStoreResult *result = connection.storeResult().release();
nlassert(result->getNumRows() <= 1);
if (result->getNumRows() == 1)
{
ret.assign(new CSession, filename, lineNum);
// ok, we have an object
result->fetchRow();
result->getField(0, ret->_SessionId);
{
std::string s;
result->getField(1, s);
ret->_SessionType = TSessionType(s);
}
result->getField(2, ret->_Title);
result->getField(3, ret->_OwnerId);
result->getDateField(4, ret->_PlanDate);
result->getDateField(5, ret->_StartDate);
result->getField(6, ret->_Description);
{
std::string s;
result->getField(7, s);
ret->_Orientation = TSessionOrientation(s);
}
{
std::string s;
result->getField(8, s);
ret->_Level = R2::TSessionLevel(s);
}
{
std::string s;
result->getField(9, s);
ret->_RuleType = TRuleType(s);
}
{
std::string s;
result->getField(10, s);
ret->_AccessType = TAccessType(s);
}
{
std::string s;
result->getField(11, s);
ret->_State = TSessionState(s);
}
result->getField(12, ret->_HostShardId);
result->getField(13, ret->_SubscriptionSlots);
result->getField(14, ret->_ReservedSlots);
{
std::string s;
result->getField(15, s);
ret->_EstimatedDuration = TEstimatedDuration(s);
}
result->getField(16, ret->_FinalDuration);
result->getField(17, ret->_FolderId);
result->getField(18, ret->_Lang);
result->getField(19, ret->_Icone);
{
std::string s;
result->getField(20, s);
ret->_AnimMode = TAnimMode(s);
}
{
std::string s;
result->getField(21, s);
ret->_RaceFilter = TRaceFilter(s);
}
{
std::string s;
result->getField(22, s);
ret->_ReligionFilter = TReligionFilter(s);
}
{
std::string s;
result->getField(23, s);
ret->_GuildFilter = TGuildFilter(s);
}
{
std::string s;
result->getField(24, s);
ret->_ShardFilter = TShardFilter(s);
}
{
std::string s;
result->getField(25, s);
ret->_LevelFilter = TLevelFilter(s);
}
result->getField(26, ret->_SubscriptionClosed);
result->getField(27, ret->_Newcomer);
ret->setPersistentState(NOPE::os_clean);
}
delete result;
return ret;
}
bool CSession::loadChildrenOfCCharacter(MSW::CConnection &connection, uint32 parentId, std::vector < CSessionPtr > & container, const char *filename, uint32 lineNum)
{
std::string qs;
qs = "SELECT ";
qs += "session_id, session_type, title, owner, plan_date, start_date, description, orientation, level, rule_type, access_type, state, host_shard_id, subscription_slots, reserved_slots, estimated_duration, final_duration, folder_id, lang, icone, anim_mode, race_filter, religion_filter, guild_filter, shard_filter, level_filter, subscription_closed, newcomer";
qs += " FROM sessions";
qs += " WHERE owner = '"+NLMISC::toString(parentId)+"'";
if (!connection.query(qs))
{
return false;
}
std::unique_ptr result(connection.storeResult());
for (uint i=0; igetNumRows(); ++i)
{
CSession *ret = new CSession();
// ok, we have an object
result->fetchRow();
result->getField(0, ret->_SessionId);
{
std::string s;
result->getField(1, s);
ret->_SessionType = TSessionType(s);
}
result->getField(2, ret->_Title);
result->getField(3, ret->_OwnerId);
result->getDateField(4, ret->_PlanDate);
result->getDateField(5, ret->_StartDate);
result->getField(6, ret->_Description);
{
std::string s;
result->getField(7, s);
ret->_Orientation = TSessionOrientation(s);
}
{
std::string s;
result->getField(8, s);
ret->_Level = R2::TSessionLevel(s);
}
{
std::string s;
result->getField(9, s);
ret->_RuleType = TRuleType(s);
}
{
std::string s;
result->getField(10, s);
ret->_AccessType = TAccessType(s);
}
{
std::string s;
result->getField(11, s);
ret->_State = TSessionState(s);
}
result->getField(12, ret->_HostShardId);
result->getField(13, ret->_SubscriptionSlots);
result->getField(14, ret->_ReservedSlots);
{
std::string s;
result->getField(15, s);
ret->_EstimatedDuration = TEstimatedDuration(s);
}
result->getField(16, ret->_FinalDuration);
result->getField(17, ret->_FolderId);
result->getField(18, ret->_Lang);
result->getField(19, ret->_Icone);
{
std::string s;
result->getField(20, s);
ret->_AnimMode = TAnimMode(s);
}
{
std::string s;
result->getField(21, s);
ret->_RaceFilter = TRaceFilter(s);
}
{
std::string s;
result->getField(22, s);
ret->_ReligionFilter = TReligionFilter(s);
}
{
std::string s;
result->getField(23, s);
ret->_GuildFilter = TGuildFilter(s);
}
{
std::string s;
result->getField(24, s);
ret->_ShardFilter = TShardFilter(s);
}
{
std::string s;
result->getField(25, s);
ret->_LevelFilter = TLevelFilter(s);
}
result->getField(26, ret->_SubscriptionClosed);
result->getField(27, ret->_Newcomer);
CSession *inCache = loadFromCache(ret->_SessionId, true);
if (inCache != NULL)
{
container.push_back(CSessionPtr(inCache, filename, lineNum));
// no more needed
delete ret;
}
else
{
ret->setPersistentState(NOPE::os_clean);
container.push_back(CSessionPtr(ret, filename, lineNum));
}
}
return true;
}
bool CSession::loadChildrenOfCFolder(MSW::CConnection &connection, uint32 parentId, std::vector < CSessionPtr > & container, const char *filename, uint32 lineNum)
{
std::string qs;
qs = "SELECT ";
qs += "session_id, session_type, title, owner, plan_date, start_date, description, orientation, level, rule_type, access_type, state, host_shard_id, subscription_slots, reserved_slots, estimated_duration, final_duration, folder_id, lang, icone, anim_mode, race_filter, religion_filter, guild_filter, shard_filter, level_filter, subscription_closed, newcomer";
qs += " FROM sessions";
qs += " WHERE folder_id = '"+NLMISC::toString(parentId)+"'";
if (!connection.query(qs))
{
return false;
}
std::unique_ptr result(connection.storeResult());
for (uint i=0; igetNumRows(); ++i)
{
CSession *ret = new CSession();
// ok, we have an object
result->fetchRow();
result->getField(0, ret->_SessionId);
{
std::string s;
result->getField(1, s);
ret->_SessionType = TSessionType(s);
}
result->getField(2, ret->_Title);
result->getField(3, ret->_OwnerId);
result->getDateField(4, ret->_PlanDate);
result->getDateField(5, ret->_StartDate);
result->getField(6, ret->_Description);
{
std::string s;
result->getField(7, s);
ret->_Orientation = TSessionOrientation(s);
}
{
std::string s;
result->getField(8, s);
ret->_Level = R2::TSessionLevel(s);
}
{
std::string s;
result->getField(9, s);
ret->_RuleType = TRuleType(s);
}
{
std::string s;
result->getField(10, s);
ret->_AccessType = TAccessType(s);
}
{
std::string s;
result->getField(11, s);
ret->_State = TSessionState(s);
}
result->getField(12, ret->_HostShardId);
result->getField(13, ret->_SubscriptionSlots);
result->getField(14, ret->_ReservedSlots);
{
std::string s;
result->getField(15, s);
ret->_EstimatedDuration = TEstimatedDuration(s);
}
result->getField(16, ret->_FinalDuration);
result->getField(17, ret->_FolderId);
result->getField(18, ret->_Lang);
result->getField(19, ret->_Icone);
{
std::string s;
result->getField(20, s);
ret->_AnimMode = TAnimMode(s);
}
{
std::string s;
result->getField(21, s);
ret->_RaceFilter = TRaceFilter(s);
}
{
std::string s;
result->getField(22, s);
ret->_ReligionFilter = TReligionFilter(s);
}
{
std::string s;
result->getField(23, s);
ret->_GuildFilter = TGuildFilter(s);
}
{
std::string s;
result->getField(24, s);
ret->_ShardFilter = TShardFilter(s);
}
{
std::string s;
result->getField(25, s);
ret->_LevelFilter = TLevelFilter(s);
}
result->getField(26, ret->_SubscriptionClosed);
result->getField(27, ret->_Newcomer);
CSession *inCache = loadFromCache(ret->_SessionId, true);
if (inCache != NULL)
{
container.push_back(CSessionPtr(inCache, filename, lineNum));
// no more needed
delete ret;
}
else
{
ret->setPersistentState(NOPE::os_clean);
container.push_back(CSessionPtr(ret, filename, lineNum));
}
}
return true;
}
bool CSession::loadSessionParticipants(MSW::CConnection &connection, const char *filename, uint32 lineNum)
{
bool ret = true;
if (_SessionParticipants != NULL)
{
// the children are already loaded, just return true
return true;
}
// allocate the container
_SessionParticipants = new std::vector < CSessionParticipantPtr >;
// load the childs
ret &= CSessionParticipant::loadChildrenOfCSession(connection, getObjectId(), *_SessionParticipants, filename, lineNum);
return ret;
}
const std::vector &CSession::getSessionParticipants() const
{
nlassert(_SessionParticipants != NULL);
return *_SessionParticipants;
}
CSessionParticipantPtr &CSession::getSessionParticipantsByIndex(uint32 index) const
{
nlassert(_SessionParticipants != NULL);
nlassert(index < _SessionParticipants->size());
return const_cast< CSessionParticipantPtr & >(_SessionParticipants->operator[](index));
}
CSessionParticipantPtr &CSession::getSessionParticipantsById(uint32 id) const
{
nlassert(_SessionParticipants != NULL);
std::vector::const_iterator first(_SessionParticipants->begin()), last(_SessionParticipants->end());
for (; first != last; ++first)
{
const CSessionParticipantPtr &child = *first;
if (child->getObjectId() == id)
{
return const_cast< CSessionParticipantPtr & >(child);
}
}
// no object with this id, return a null pointer
static CSessionParticipantPtr nil;
return nil;
}
bool CSession::loadGuildInvites(MSW::CConnection &connection, const char *filename, uint32 lineNum)
{
bool ret = true;
if (_GuildInvites != NULL)
{
// the children are already loaded, just return true
return true;
}
// allocate the container
_GuildInvites = new std::vector < CGuildInvitePtr >;
// load the childs
ret &= CGuildInvite::loadChildrenOfCSession(connection, getObjectId(), *_GuildInvites, filename, lineNum);
return ret;
}
const std::vector &CSession::getGuildInvites() const
{
nlassert(_GuildInvites != NULL);
return *_GuildInvites;
}
CGuildInvitePtr &CSession::getGuildInvitesByIndex(uint32 index) const
{
nlassert(_GuildInvites != NULL);
nlassert(index < _GuildInvites->size());
return const_cast< CGuildInvitePtr & >(_GuildInvites->operator[](index));
}
CGuildInvitePtr &CSession::getGuildInvitesById(uint32 id) const
{
nlassert(_GuildInvites != NULL);
std::vector::const_iterator first(_GuildInvites->begin()), last(_GuildInvites->end());
for (; first != last; ++first)
{
const CGuildInvitePtr &child = *first;
if (child->getObjectId() == id)
{
return const_cast< CGuildInvitePtr & >(child);
}
}
// no object with this id, return a null pointer
static CGuildInvitePtr nil;
return nil;
}
bool CSession::loadJournalEntries(MSW::CConnection &connection, const char *filename, uint32 lineNum)
{
bool ret = true;
if (_JournalEntries != NULL)
{
// the children are already loaded, just return true
return true;
}
// allocate the container
_JournalEntries = new std::vector < CJournalEntryPtr >;
// load the childs
ret &= CJournalEntry::loadChildrenOfCSession(connection, getObjectId(), *_JournalEntries, filename, lineNum);
return ret;
}
const std::vector &CSession::getJournalEntries() const
{
nlassert(_JournalEntries != NULL);
return *_JournalEntries;
}
CJournalEntryPtr &CSession::getJournalEntriesByIndex(uint32 index) const
{
nlassert(_JournalEntries != NULL);
nlassert(index < _JournalEntries->size());
return const_cast< CJournalEntryPtr & >(_JournalEntries->operator[](index));
}
CJournalEntryPtr &CSession::getJournalEntriesById(uint32 id) const
{
nlassert(_JournalEntries != NULL);
std::vector::const_iterator first(_JournalEntries->begin()), last(_JournalEntries->end());
for (; first != last; ++first)
{
const CJournalEntryPtr &child = *first;
if (child->getObjectId() == id)
{
return const_cast< CJournalEntryPtr & >(child);
}
}
// no object with this id, return a null pointer
static CJournalEntryPtr nil;
return nil;
}
void CShardPtr::linkPtr()
{
nlassert(_NextPtr == NULL);
nlassert(_PrevPtr == NULL);
if (_Ptr != NULL)
{
_NextPtr = _Ptr->getFirstPtr();
if (_NextPtr != NULL)
{
_PrevPtr = _NextPtr->_PrevPtr;
_PrevPtr->_NextPtr = this;
_NextPtr->_PrevPtr = this;
}
else
{
_NextPtr = this;
_PrevPtr = this;
_Ptr->setFirstPtr(this);
}
}
}
void CShardPtr::unlinkPtr()
{
if (_NextPtr == NULL)
{
nlassert(_PrevPtr == NULL);
return;
}
if (_Ptr != NULL)
{
if (_NextPtr == this)
{
nlassert(_PrevPtr == this);
// last pointer !
_Ptr->setFirstPtr(NULL);
}
else
{
if (_Ptr->getFirstPtr() == this)
{
// the first ptr is the current one, we need to switch to next one
_Ptr->setFirstPtr(_NextPtr);
}
}
}
if (_NextPtr != this)
{
nlassert(_PrevPtr != this);
_NextPtr->_PrevPtr = _PrevPtr;
_PrevPtr->_NextPtr = _NextPtr;
}
_NextPtr = NULL;
_PrevPtr = NULL;
}
CShard::TObjectCache CShard::_ObjectCache;
CShard::TReleasedObject CShard::_ReleasedObject;
// Destructor, delete any children
CShard::~CShard()
{
// release childs reference
if (_Guilds != NULL)
delete _Guilds;
if (_PtrList != NULL)
{
nlwarning("ERROR : someone try to delete this object, but there are still ptr on it !");
CShardPtr *ptr = _PtrList;
do
{
nlwarning(" Pointer created from '%s', line %u", ptr->_FileName, ptr->_LineNum);
ptr = _PtrList->getNextPtr();
} while(ptr != _PtrList);
nlstop;
}
// remove object from cache map
if (_ShardId != NOPE::INVALID_OBJECT_ID
&& _ObjectState != NOPE::os_removed
&& _ObjectState != NOPE::os_transient)
{
nldebug("NOPE: clearing CShard @%p from cache with id %u", this, static_cast(_ShardId));
nlverify(_ObjectCache.erase(_ShardId) == 1);
}
else if (_ObjectState != NOPE::os_transient)
{
nlassert(_ObjectCache.find(_ShardId) == _ObjectCache.end());
}
if (_ObjectState == NOPE::os_released)
{
removeFromReleased();
}
else
{
TReleasedObject::iterator it(_ReleasedObject.find(_ReleaseDate));
if (it != _ReleasedObject.end())
{
nlassert(it->second.find(this) == it->second.end());
}
}
}
void CShard::removeFromReleased()
{
TReleasedObject::iterator it(_ReleasedObject.find(_ReleaseDate));
nlassert(it != _ReleasedObject.end());
TObjectSet &os = it->second;
nlverify(os.erase(this) == 1);
// nb : _ReleasedObject time entry are removed by the cache update
}
bool CShard::create(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_transient);
nlassert(_ShardId != 0);
std::string qs;
qs = "INSERT INTO shard (";
qs += "shard_id, WSOnline, RequiredState, MOTD";
qs += ") VALUES (";
qs += "'"+MSW::escapeString(NLMISC::toString(_ShardId), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_WSOnline), connection)+"'";
qs += ", ";
qs += "'"+_RequiredState.toString()+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_MOTD), connection)+"'";
qs += ")";
if (connection.query(qs))
{
setPersistentState(NOPE::os_clean);
// update the parent class instance in cache if any
return true;
}
return false;
}
bool CShard::update(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_dirty || getPersistentState() == NOPE::os_clean);
if (getPersistentState() == NOPE::os_clean)
// the object is clean, just ignore the save
return true;
std::string qs;
qs = "UPDATE shard SET ";
qs += "shard_id = '"+MSW::escapeString(NLMISC::toString(_ShardId), connection)+"'";
qs += ", ";
qs += "WSOnline = '"+MSW::escapeString(NLMISC::toString(_WSOnline), connection)+"'";
qs += ", ";
qs += "RequiredState = '"+_RequiredState.toString()+"'";
qs += ", ";
qs += "MOTD = '"+MSW::escapeString(NLMISC::toString(_MOTD), connection)+"'";
qs += " WHERE shard_id = '"+NLMISC::toString(_ShardId)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
setPersistentState(NOPE::os_clean);
return true;
}
}
return false;
}
bool CShard::remove(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_dirty || getPersistentState() == NOPE::os_clean);
std::string qs;
qs = "DELETE FROM shard ";
qs += " WHERE shard_id = '"+NLMISC::toString(_ShardId)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
// change the persistant state to 'removed'.
setPersistentState(NOPE::os_removed);
// need to remove ref from parent class container (if any)
// need to remove ref from parent (if any)
return true;
}
}
return false;
}
bool CShard::removeById(MSW::CConnection &connection, uint32 id)
{
CShard *object = loadFromCache(id, true);
if (object != NULL)
{
return object->remove(connection);
}
// not in cache, run a SQL query
std::string qs;
qs = "DELETE FROM shard ";
qs += " WHERE shard_id = '"+NLMISC::toString(id)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
// ok, the row is removed
return true;
}
}
return false;
}
// Try to load the specified object from the memory cache, return NULL if the object is not in the cache
CShard *CShard::loadFromCache(uint32 objectId, bool unrelease)
{
// look in the cache
TObjectCache::iterator it(_ObjectCache.find(objectId));
if (it == _ObjectCache.end())
{
// not found, return null
return NULL;
}
else
{
CShard *object = it->second;
if (object->_ObjectState == NOPE::os_released)
{
if (unrelease)
{
// we need to remove this object from the released object set.
object->removeFromReleased();
object->_ObjectState = NOPE::os_clean;
}
}
return it->second;
}
}
// Receive and execute command from the cache manager.
uint32 CShard::cacheCmd(NOPE::TCacheCmd cmd)
{
if (cmd == NOPE::cc_update)
{
updateCache();
}
else if (cmd == NOPE::cc_clear)
{
clearCache();
}
else if (cmd == NOPE::cc_dump)
{
dump();
}
else if (cmd == NOPE::cc_instance_count)
{
return (uint32)_ObjectCache.size();
}
// default return value
return 0;
}
void CShard::dump()
{
nlinfo(" Cache info for class CShard :");
nlinfo(" There are %u object instances in cache", _ObjectCache.size());
// count the number of object in the released object set
uint32 nbReleased = 0;
TReleasedObject::iterator first(_ReleasedObject.begin()), last(_ReleasedObject.end());
for (; first != last; ++first)
{
nbReleased += (uint32)first->second.size();
}
nlinfo(" There are %u object instances in cache not referenced (waiting deletion or re-use))", nbReleased);
}
void CShard::updateCache()
{
if (_ReleasedObject.empty())
return;
// 30 s hold in cache
const time_t MAX_CACHE_OLD_TIME = 30;
time_t now = NLMISC::CTime::getSecondsSince1970();
// look for object set older than MAX_CACHE_OLD_TIME and delete them
while (!_ReleasedObject.empty() && _ReleasedObject.begin()->first < now-MAX_CACHE_OLD_TIME)
{
TObjectSet &delSet = _ReleasedObject.begin()->second;
// unload this objects
while (!delSet.empty())
{
CShard *object = *delSet.begin();
delete object;
}
_ReleasedObject.erase(_ReleasedObject.begin());
}
}
void CShard::clearCache()
{
// remove any unreferenced object from the cache
while (!_ReleasedObject.empty())
{
TObjectSet &delSet = _ReleasedObject.begin()->second;
// unload this objects
while (!delSet.empty())
{
CShard *object = *delSet.begin();
delete object;
}
_ReleasedObject.erase(_ReleasedObject.begin());
}
}
void CShard::registerUpdatable()
{
static bool registered = false;
if (!registered)
{
NOPE::CPersistentCache::getInstance().registerCache(&CShard::cacheCmd);
registered = true;
}
}
// set the pointer on the first pointer of the pointer list (set to null when there is no more pointer)
void CShard::setFirstPtr(CShardPtr *ptr)
{
_PtrList = ptr;
if (ptr == NULL)
{
// this is the last pointer !
if (_ObjectState == NOPE::os_transient
|| _ObjectState == NOPE::os_removed)
{
// not a persistent object, or removed object, just delet it
delete this;
}
else if (_ObjectState != NOPE::os_removed)
{
setPersistentState(NOPE::os_released);
}
}
}
// Set the persistent state of the object and do some house keeping
void CShard::setPersistentState(NOPE::TObjectState state)
{
nlassert(NOPE::AllowedTransition[_ObjectState][state] == true);
if(_ObjectState == NOPE::os_released && state == NOPE::os_removed)
{
// a release object gets removed (e.g. by remove by id)
// delete the object
delete this;
// no more to do
return;
}
if (_ObjectState == NOPE::os_transient && state != NOPE::os_transient)
{
nldebug("NOPE: inserting CShard @%p in cache with id %u", this, static_cast(_ShardId));
nlverify(_ObjectCache.insert(std::make_pair(_ShardId, this)).second);
}
if (_ObjectState != NOPE::os_transient)
nlassert(_ObjectCache.find(_ShardId) != _ObjectCache.end());
_ObjectState = state;
if (state == NOPE::os_released)
{
_ReleaseDate = NLMISC::CTime::getSecondsSince1970();
nlverify(_ReleasedObject[_ReleaseDate].insert(this).second);
}
else if (state == NOPE::os_removed)
{
nldebug("NOPE: erasing CShard @%p in cache with id %u", this, static_cast(_ShardId));
nlverify(_ObjectCache.erase(_ShardId) == 1);
}
}
CShardPtr CShard::load(MSW::CConnection &connection, uint32 id, const char *filename, uint32 lineNum)
{
CShard *inCache = loadFromCache(id, true);
if (inCache != NULL)
{
return CShardPtr(inCache, filename, lineNum);
}
std::string qs;
qs = "SELECT ";
qs += "shard_id, WSOnline, RequiredState, MOTD";
qs += " FROM shard";
qs += " WHERE shard_id = '"+NLMISC::toString(id)+"'";
CShardPtr ret;
if (!connection.query(qs))
{
return ret;
}
MSW::CStoreResult *result = connection.storeResult().release();
nlassert(result->getNumRows() <= 1);
if (result->getNumRows() == 1)
{
ret.assign(new CShard, filename, lineNum);
// ok, we have an object
result->fetchRow();
result->getField(0, ret->_ShardId);
result->getField(1, ret->_WSOnline);
{
std::string s;
result->getField(2, s);
ret->_RequiredState = TAccessLevel(s);
}
result->getField(3, ret->_MOTD);
ret->setPersistentState(NOPE::os_clean);
}
delete result;
return ret;
}
bool CShard::loadGuilds(MSW::CConnection &connection, const char *filename, uint32 lineNum)
{
bool ret = true;
if (_Guilds != NULL)
{
// the children are already loaded, just return true
return true;
}
// allocate the container
_Guilds = new std::map < uint32, CGuildPtr >;
// load the childs
ret &= CGuild::loadChildrenOfCShard(connection, getObjectId(), *_Guilds, filename, lineNum);
return ret;
}
const std::map &CShard::getGuilds() const
{
nlassert(_Guilds != NULL);
return *_Guilds;
}
CGuildPtr &CShard::getGuildsById(uint32 id) const
{
nlassert(_Guilds != NULL);
std::map::const_iterator it(_Guilds->find(id));
if (it == _Guilds->end())
{
// no object with this id, return a null pointer
static CGuildPtr nil;
return nil;
}
return const_cast< CGuildPtr & >(it->second);
}
void CGuildPtr::linkPtr()
{
nlassert(_NextPtr == NULL);
nlassert(_PrevPtr == NULL);
if (_Ptr != NULL)
{
_NextPtr = _Ptr->getFirstPtr();
if (_NextPtr != NULL)
{
_PrevPtr = _NextPtr->_PrevPtr;
_PrevPtr->_NextPtr = this;
_NextPtr->_PrevPtr = this;
}
else
{
_NextPtr = this;
_PrevPtr = this;
_Ptr->setFirstPtr(this);
}
}
}
void CGuildPtr::unlinkPtr()
{
if (_NextPtr == NULL)
{
nlassert(_PrevPtr == NULL);
return;
}
if (_Ptr != NULL)
{
if (_NextPtr == this)
{
nlassert(_PrevPtr == this);
// last pointer !
_Ptr->setFirstPtr(NULL);
}
else
{
if (_Ptr->getFirstPtr() == this)
{
// the first ptr is the current one, we need to switch to next one
_Ptr->setFirstPtr(_NextPtr);
}
}
}
if (_NextPtr != this)
{
nlassert(_PrevPtr != this);
_NextPtr->_PrevPtr = _PrevPtr;
_PrevPtr->_NextPtr = _NextPtr;
}
_NextPtr = NULL;
_PrevPtr = NULL;
}
CGuild::TObjectCache CGuild::_ObjectCache;
CGuild::TReleasedObject CGuild::_ReleasedObject;
// Destructor, delete any children
CGuild::~CGuild()
{
// release childs reference
if (_Characters != NULL)
delete _Characters;
if (_Invites != NULL)
delete _Invites;
if (_PtrList != NULL)
{
nlwarning("ERROR : someone try to delete this object, but there are still ptr on it !");
CGuildPtr *ptr = _PtrList;
do
{
nlwarning(" Pointer created from '%s', line %u", ptr->_FileName, ptr->_LineNum);
ptr = _PtrList->getNextPtr();
} while(ptr != _PtrList);
nlstop;
}
// remove object from cache map
if (_GuildId != NOPE::INVALID_OBJECT_ID
&& _ObjectState != NOPE::os_removed
&& _ObjectState != NOPE::os_transient)
{
nldebug("NOPE: clearing CGuild @%p from cache with id %u", this, static_cast(_GuildId));
nlverify(_ObjectCache.erase(_GuildId) == 1);
}
else if (_ObjectState != NOPE::os_transient)
{
nlassert(_ObjectCache.find(_GuildId) == _ObjectCache.end());
}
if (_ObjectState == NOPE::os_released)
{
removeFromReleased();
}
else
{
TReleasedObject::iterator it(_ReleasedObject.find(_ReleaseDate));
if (it != _ReleasedObject.end())
{
nlassert(it->second.find(this) == it->second.end());
}
}
}
void CGuild::removeFromReleased()
{
TReleasedObject::iterator it(_ReleasedObject.find(_ReleaseDate));
nlassert(it != _ReleasedObject.end());
TObjectSet &os = it->second;
nlverify(os.erase(this) == 1);
// nb : _ReleasedObject time entry are removed by the cache update
}
bool CGuild::create(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_transient);
nlassert(_GuildId != 0);
std::string qs;
qs = "INSERT INTO guilds (";
qs += "guild_id, guild_name, shard_id";
qs += ") VALUES (";
qs += "'"+MSW::escapeString(NLMISC::toString(_GuildId), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_GuildName), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_ShardId), connection)+"'";
qs += ")";
if (connection.query(qs))
{
setPersistentState(NOPE::os_clean);
// update the parent class instance in cache if any
if (_ShardId != 0)
{
// need to update the parent class child list if it is in the cache
CShard *parent = CShard::loadFromCache(_ShardId, false);
if (parent && parent->_Guilds != NULL)
{
nlverify(parent->_Guilds->insert(std::make_pair(getObjectId(), CGuildPtr(this, __FILE__, __LINE__))).second);
}
}
return true;
}
return false;
}
bool CGuild::update(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_dirty || getPersistentState() == NOPE::os_clean);
if (getPersistentState() == NOPE::os_clean)
// the object is clean, just ignore the save
return true;
std::string qs;
qs = "UPDATE guilds SET ";
qs += "guild_id = '"+MSW::escapeString(NLMISC::toString(_GuildId), connection)+"'";
qs += ", ";
qs += "guild_name = '"+MSW::escapeString(NLMISC::toString(_GuildName), connection)+"'";
qs += ", ";
qs += "shard_id = '"+MSW::escapeString(NLMISC::toString(_ShardId), connection)+"'";
qs += " WHERE guild_id = '"+NLMISC::toString(_GuildId)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
setPersistentState(NOPE::os_clean);
return true;
}
}
return false;
}
bool CGuild::remove(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_dirty || getPersistentState() == NOPE::os_clean);
std::string qs;
qs = "DELETE FROM guilds ";
qs += " WHERE guild_id = '"+NLMISC::toString(_GuildId)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
{
// cascading deletion for vector child Invites
nlassert(loadInvites(connection, __FILE__, __LINE__));
const std::vector < CGuildInvitePtr > & childs = getInvites();
while (!childs.empty())
{
getInvitesByIndex((uint32)childs.size()-1)->remove(connection);
}
}
{
// unreference (and update) for vector child Characters
nlassert(loadCharacters(connection, __FILE__, __LINE__));
const std::vector < CCharacterPtr > & childs = getCharacters();
for (uint i=0; i < childs.size(); ++i)
{
getCharactersByIndex(i)->setGuildId(0);
getCharactersByIndex(i)->update(connection);
}
}
// change the persistant state to 'removed'.
setPersistentState(NOPE::os_removed);
// need to remove ref from parent class container (if any)
{
CShardPtr parent(CShard::loadFromCache(_ShardId, true), __FILE__, __LINE__);
if (parent != NULL && parent->_Guilds != NULL)
{
parent->_Guilds->erase(getObjectId());
}
}
// need to remove ref from parent (if any)
return true;
}
}
return false;
}
bool CGuild::removeById(MSW::CConnection &connection, uint32 id)
{
CGuild *object = loadFromCache(id, true);
if (object != NULL)
{
return object->remove(connection);
}
// not in cache, run a SQL query
std::string qs;
qs = "DELETE FROM guilds ";
qs += " WHERE guild_id = '"+NLMISC::toString(id)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
// ok, the row is removed
return true;
}
}
return false;
}
// Try to load the specified object from the memory cache, return NULL if the object is not in the cache
CGuild *CGuild::loadFromCache(uint32 objectId, bool unrelease)
{
// look in the cache
TObjectCache::iterator it(_ObjectCache.find(objectId));
if (it == _ObjectCache.end())
{
// not found, return null
return NULL;
}
else
{
CGuild *object = it->second;
if (object->_ObjectState == NOPE::os_released)
{
if (unrelease)
{
// we need to remove this object from the released object set.
object->removeFromReleased();
object->_ObjectState = NOPE::os_clean;
}
}
return it->second;
}
}
// Receive and execute command from the cache manager.
uint32 CGuild::cacheCmd(NOPE::TCacheCmd cmd)
{
if (cmd == NOPE::cc_update)
{
updateCache();
}
else if (cmd == NOPE::cc_clear)
{
clearCache();
}
else if (cmd == NOPE::cc_dump)
{
dump();
}
else if (cmd == NOPE::cc_instance_count)
{
return (uint32)_ObjectCache.size();
}
// default return value
return 0;
}
void CGuild::dump()
{
nlinfo(" Cache info for class CGuild :");
nlinfo(" There are %u object instances in cache", _ObjectCache.size());
// count the number of object in the released object set
uint32 nbReleased = 0;
TReleasedObject::iterator first(_ReleasedObject.begin()), last(_ReleasedObject.end());
for (; first != last; ++first)
{
nbReleased += (uint32)first->second.size();
}
nlinfo(" There are %u object instances in cache not referenced (waiting deletion or re-use))", nbReleased);
}
void CGuild::updateCache()
{
if (_ReleasedObject.empty())
return;
// 30 s hold in cache
const time_t MAX_CACHE_OLD_TIME = 30;
time_t now = NLMISC::CTime::getSecondsSince1970();
// look for object set older than MAX_CACHE_OLD_TIME and delete them
while (!_ReleasedObject.empty() && _ReleasedObject.begin()->first < now-MAX_CACHE_OLD_TIME)
{
TObjectSet &delSet = _ReleasedObject.begin()->second;
// unload this objects
while (!delSet.empty())
{
CGuild *object = *delSet.begin();
delete object;
}
_ReleasedObject.erase(_ReleasedObject.begin());
}
}
void CGuild::clearCache()
{
// remove any unreferenced object from the cache
while (!_ReleasedObject.empty())
{
TObjectSet &delSet = _ReleasedObject.begin()->second;
// unload this objects
while (!delSet.empty())
{
CGuild *object = *delSet.begin();
delete object;
}
_ReleasedObject.erase(_ReleasedObject.begin());
}
}
void CGuild::registerUpdatable()
{
static bool registered = false;
if (!registered)
{
NOPE::CPersistentCache::getInstance().registerCache(&CGuild::cacheCmd);
registered = true;
}
}
// set the pointer on the first pointer of the pointer list (set to null when there is no more pointer)
void CGuild::setFirstPtr(CGuildPtr *ptr)
{
_PtrList = ptr;
if (ptr == NULL)
{
// this is the last pointer !
if (_ObjectState == NOPE::os_transient
|| _ObjectState == NOPE::os_removed)
{
// not a persistent object, or removed object, just delet it
delete this;
}
else if (_ObjectState != NOPE::os_removed)
{
setPersistentState(NOPE::os_released);
}
}
}
// Set the persistent state of the object and do some house keeping
void CGuild::setPersistentState(NOPE::TObjectState state)
{
nlassert(NOPE::AllowedTransition[_ObjectState][state] == true);
if(_ObjectState == NOPE::os_released && state == NOPE::os_removed)
{
// a release object gets removed (e.g. by remove by id)
// delete the object
delete this;
// no more to do
return;
}
if (_ObjectState == NOPE::os_transient && state != NOPE::os_transient)
{
nldebug("NOPE: inserting CGuild @%p in cache with id %u", this, static_cast(_GuildId));
nlverify(_ObjectCache.insert(std::make_pair(_GuildId, this)).second);
}
if (_ObjectState != NOPE::os_transient)
nlassert(_ObjectCache.find(_GuildId) != _ObjectCache.end());
_ObjectState = state;
if (state == NOPE::os_released)
{
_ReleaseDate = NLMISC::CTime::getSecondsSince1970();
nlverify(_ReleasedObject[_ReleaseDate].insert(this).second);
}
else if (state == NOPE::os_removed)
{
nldebug("NOPE: erasing CGuild @%p in cache with id %u", this, static_cast(_GuildId));
nlverify(_ObjectCache.erase(_GuildId) == 1);
}
}
CGuildPtr CGuild::load(MSW::CConnection &connection, uint32 id, const char *filename, uint32 lineNum)
{
CGuild *inCache = loadFromCache(id, true);
if (inCache != NULL)
{
return CGuildPtr(inCache, filename, lineNum);
}
std::string qs;
qs = "SELECT ";
qs += "guild_id, guild_name, shard_id";
qs += " FROM guilds";
qs += " WHERE guild_id = '"+NLMISC::toString(id)+"'";
CGuildPtr ret;
if (!connection.query(qs))
{
return ret;
}
MSW::CStoreResult *result = connection.storeResult().release();
nlassert(result->getNumRows() <= 1);
if (result->getNumRows() == 1)
{
ret.assign(new CGuild, filename, lineNum);
// ok, we have an object
result->fetchRow();
result->getField(0, ret->_GuildId);
result->getField(1, ret->_GuildName);
result->getField(2, ret->_ShardId);
ret->setPersistentState(NOPE::os_clean);
}
delete result;
return ret;
}
bool CGuild::loadChildrenOfCShard(MSW::CConnection &connection, uint32 parentId, std::map < uint32, CGuildPtr > & container, const char *filename, uint32 lineNum)
{
std::string qs;
qs = "SELECT ";
qs += "guild_id, guild_name, shard_id";
qs += " FROM guilds";
qs += " WHERE shard_id = '"+NLMISC::toString(parentId)+"'";
if (!connection.query(qs))
{
return false;
}
std::unique_ptr result(connection.storeResult());
for (uint i=0; igetNumRows(); ++i)
{
CGuild *ret = new CGuild();
// ok, we have an object
result->fetchRow();
result->getField(0, ret->_GuildId);
result->getField(1, ret->_GuildName);
result->getField(2, ret->_ShardId);
CGuild *inCache = loadFromCache(ret->_GuildId, true);
if (inCache != NULL)
{
container.insert(std::make_pair(inCache->getObjectId(), CGuildPtr(inCache, filename, lineNum)));
// no more needed
delete ret;
}
else
{
ret->setPersistentState(NOPE::os_clean);
container.insert(std::make_pair(ret->getObjectId(), CGuildPtr(ret, filename, lineNum)));
}
}
return true;
}
bool CGuild::loadCharacters(MSW::CConnection &connection, const char *filename, uint32 lineNum)
{
bool ret = true;
if (_Characters != NULL)
{
// the children are already loaded, just return true
return true;
}
// allocate the container
_Characters = new std::vector < CCharacterPtr >;
// load the childs
ret &= CCharacter::loadChildrenOfCGuild(connection, getObjectId(), *_Characters, filename, lineNum);
return ret;
}
const std::vector &CGuild::getCharacters() const
{
nlassert(_Characters != NULL);
return *_Characters;
}
CCharacterPtr &CGuild::getCharactersByIndex(uint32 index) const
{
nlassert(_Characters != NULL);
nlassert(index < _Characters->size());
return const_cast< CCharacterPtr & >(_Characters->operator[](index));
}
CCharacterPtr &CGuild::getCharactersById(uint32 id) const
{
nlassert(_Characters != NULL);
std::vector::const_iterator first(_Characters->begin()), last(_Characters->end());
for (; first != last; ++first)
{
const CCharacterPtr &child = *first;
if (child->getObjectId() == id)
{
return const_cast< CCharacterPtr & >(child);
}
}
// no object with this id, return a null pointer
static CCharacterPtr nil;
return nil;
}
bool CGuild::loadInvites(MSW::CConnection &connection, const char *filename, uint32 lineNum)
{
bool ret = true;
if (_Invites != NULL)
{
// the children are already loaded, just return true
return true;
}
// allocate the container
_Invites = new std::vector < CGuildInvitePtr >;
// load the childs
ret &= CGuildInvite::loadChildrenOfCGuild(connection, getObjectId(), *_Invites, filename, lineNum);
return ret;
}
const std::vector &CGuild::getInvites() const
{
nlassert(_Invites != NULL);
return *_Invites;
}
CGuildInvitePtr &CGuild::getInvitesByIndex(uint32 index) const
{
nlassert(_Invites != NULL);
nlassert(index < _Invites->size());
return const_cast< CGuildInvitePtr & >(_Invites->operator[](index));
}
CGuildInvitePtr &CGuild::getInvitesById(uint32 id) const
{
nlassert(_Invites != NULL);
std::vector::const_iterator first(_Invites->begin()), last(_Invites->end());
for (; first != last; ++first)
{
const CGuildInvitePtr &child = *first;
if (child->getObjectId() == id)
{
return const_cast< CGuildInvitePtr & >(child);
}
}
// no object with this id, return a null pointer
static CGuildInvitePtr nil;
return nil;
}
void CGuildInvitePtr::linkPtr()
{
nlassert(_NextPtr == NULL);
nlassert(_PrevPtr == NULL);
if (_Ptr != NULL)
{
_NextPtr = _Ptr->getFirstPtr();
if (_NextPtr != NULL)
{
_PrevPtr = _NextPtr->_PrevPtr;
_PrevPtr->_NextPtr = this;
_NextPtr->_PrevPtr = this;
}
else
{
_NextPtr = this;
_PrevPtr = this;
_Ptr->setFirstPtr(this);
}
}
}
void CGuildInvitePtr::unlinkPtr()
{
if (_NextPtr == NULL)
{
nlassert(_PrevPtr == NULL);
return;
}
if (_Ptr != NULL)
{
if (_NextPtr == this)
{
nlassert(_PrevPtr == this);
// last pointer !
_Ptr->setFirstPtr(NULL);
}
else
{
if (_Ptr->getFirstPtr() == this)
{
// the first ptr is the current one, we need to switch to next one
_Ptr->setFirstPtr(_NextPtr);
}
}
}
if (_NextPtr != this)
{
nlassert(_PrevPtr != this);
_NextPtr->_PrevPtr = _PrevPtr;
_PrevPtr->_NextPtr = _NextPtr;
}
_NextPtr = NULL;
_PrevPtr = NULL;
}
CGuildInvite::TObjectCache CGuildInvite::_ObjectCache;
CGuildInvite::TReleasedObject CGuildInvite::_ReleasedObject;
// Destructor, delete any children
CGuildInvite::~CGuildInvite()
{
// release childs reference
if (_PtrList != NULL)
{
nlwarning("ERROR : someone try to delete this object, but there are still ptr on it !");
CGuildInvitePtr *ptr = _PtrList;
do
{
nlwarning(" Pointer created from '%s', line %u", ptr->_FileName, ptr->_LineNum);
ptr = _PtrList->getNextPtr();
} while(ptr != _PtrList);
nlstop;
}
// remove object from cache map
if (_Id != NOPE::INVALID_OBJECT_ID
&& _ObjectState != NOPE::os_removed
&& _ObjectState != NOPE::os_transient)
{
nldebug("NOPE: clearing CGuildInvite @%p from cache with id %u", this, static_cast(_Id));
nlverify(_ObjectCache.erase(_Id) == 1);
}
else if (_ObjectState != NOPE::os_transient)
{
nlassert(_ObjectCache.find(_Id) == _ObjectCache.end());
}
if (_ObjectState == NOPE::os_released)
{
removeFromReleased();
}
else
{
TReleasedObject::iterator it(_ReleasedObject.find(_ReleaseDate));
if (it != _ReleasedObject.end())
{
nlassert(it->second.find(this) == it->second.end());
}
}
}
void CGuildInvite::removeFromReleased()
{
TReleasedObject::iterator it(_ReleasedObject.find(_ReleaseDate));
nlassert(it != _ReleasedObject.end());
TObjectSet &os = it->second;
nlverify(os.erase(this) == 1);
// nb : _ReleasedObject time entry are removed by the cache update
}
bool CGuildInvite::create(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_transient);
std::string qs;
qs = "INSERT INTO guild_invites (";
qs += "guild_id, session_id";
qs += ") VALUES (";
qs += "'"+MSW::escapeString(NLMISC::toString(_GuildId), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_SessionId), connection)+"'";
qs += ")";
if (connection.query(qs))
{
uint32 _id_ = connection.getLastGeneratedId();
setObjectId(_id_);
setPersistentState(NOPE::os_clean);
// update the parent class instance in cache if any
if (_GuildId != 0)
{
// need to update the parent class child list if it is in the cache
CGuild *parent = CGuild::loadFromCache(_GuildId, false);
if (parent && parent->_Invites != NULL)
{
nlassert(std::find(parent->_Invites->begin(), parent->_Invites->end(), CGuildInvitePtr(this, __FILE__, __LINE__)) == parent->_Invites->end());
parent->_Invites->push_back(CGuildInvitePtr(this, __FILE__, __LINE__));
}
}
if (_SessionId != 0)
{
// need to update the parent class child list if it is in the cache
CSession *parent = CSession::loadFromCache(_SessionId, false);
if (parent && parent->_GuildInvites != NULL)
{
nlassert(std::find(parent->_GuildInvites->begin(), parent->_GuildInvites->end(), CGuildInvitePtr(this, __FILE__, __LINE__)) == parent->_GuildInvites->end());
parent->_GuildInvites->push_back(CGuildInvitePtr(this, __FILE__, __LINE__));
}
}
return true;
}
return false;
}
bool CGuildInvite::update(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_dirty || getPersistentState() == NOPE::os_clean);
if (getPersistentState() == NOPE::os_clean)
// the object is clean, just ignore the save
return true;
std::string qs;
qs = "UPDATE guild_invites SET ";
qs += "guild_id = '"+MSW::escapeString(NLMISC::toString(_GuildId), connection)+"'";
qs += ", ";
qs += "session_id = '"+MSW::escapeString(NLMISC::toString(_SessionId), connection)+"'";
qs += " WHERE Id = '"+NLMISC::toString(_Id)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
setPersistentState(NOPE::os_clean);
return true;
}
}
return false;
}
bool CGuildInvite::remove(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_dirty || getPersistentState() == NOPE::os_clean);
std::string qs;
qs = "DELETE FROM guild_invites ";
qs += " WHERE Id = '"+NLMISC::toString(_Id)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
// change the persistant state to 'removed'.
setPersistentState(NOPE::os_removed);
// need to remove ref from parent class container (if any)
{
CGuildPtr parent(CGuild::loadFromCache(_GuildId, true), __FILE__, __LINE__);
if (parent != NULL && parent->_Invites != NULL)
{
std::vector < CGuildInvitePtr >::iterator it = std::find(parent->_Invites->begin(), parent->_Invites->end(), this);
if (it != parent->_Invites->end())
{
parent->_Invites->erase(it);
}
}
}
{
CSessionPtr parent(CSession::loadFromCache(_SessionId, true), __FILE__, __LINE__);
if (parent != NULL && parent->_GuildInvites != NULL)
{
std::vector < CGuildInvitePtr >::iterator it = std::find(parent->_GuildInvites->begin(), parent->_GuildInvites->end(), this);
if (it != parent->_GuildInvites->end())
{
parent->_GuildInvites->erase(it);
}
}
}
// need to remove ref from parent (if any)
return true;
}
}
return false;
}
bool CGuildInvite::removeById(MSW::CConnection &connection, uint32 id)
{
CGuildInvite *object = loadFromCache(id, true);
if (object != NULL)
{
return object->remove(connection);
}
// not in cache, run a SQL query
std::string qs;
qs = "DELETE FROM guild_invites ";
qs += " WHERE Id = '"+NLMISC::toString(id)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
// ok, the row is removed
return true;
}
}
return false;
}
// Try to load the specified object from the memory cache, return NULL if the object is not in the cache
CGuildInvite *CGuildInvite::loadFromCache(uint32 objectId, bool unrelease)
{
// look in the cache
TObjectCache::iterator it(_ObjectCache.find(objectId));
if (it == _ObjectCache.end())
{
// not found, return null
return NULL;
}
else
{
CGuildInvite *object = it->second;
if (object->_ObjectState == NOPE::os_released)
{
if (unrelease)
{
// we need to remove this object from the released object set.
object->removeFromReleased();
object->_ObjectState = NOPE::os_clean;
}
}
return it->second;
}
}
// Receive and execute command from the cache manager.
uint32 CGuildInvite::cacheCmd(NOPE::TCacheCmd cmd)
{
if (cmd == NOPE::cc_update)
{
updateCache();
}
else if (cmd == NOPE::cc_clear)
{
clearCache();
}
else if (cmd == NOPE::cc_dump)
{
dump();
}
else if (cmd == NOPE::cc_instance_count)
{
return (uint32)_ObjectCache.size();
}
// default return value
return 0;
}
void CGuildInvite::dump()
{
nlinfo(" Cache info for class CGuildInvite :");
nlinfo(" There are %u object instances in cache", _ObjectCache.size());
// count the number of object in the released object set
uint32 nbReleased = 0;
TReleasedObject::iterator first(_ReleasedObject.begin()), last(_ReleasedObject.end());
for (; first != last; ++first)
{
nbReleased += (uint32)first->second.size();
}
nlinfo(" There are %u object instances in cache not referenced (waiting deletion or re-use))", nbReleased);
}
void CGuildInvite::updateCache()
{
if (_ReleasedObject.empty())
return;
// 30 s hold in cache
const time_t MAX_CACHE_OLD_TIME = 30;
time_t now = NLMISC::CTime::getSecondsSince1970();
// look for object set older than MAX_CACHE_OLD_TIME and delete them
while (!_ReleasedObject.empty() && _ReleasedObject.begin()->first < now-MAX_CACHE_OLD_TIME)
{
TObjectSet &delSet = _ReleasedObject.begin()->second;
// unload this objects
while (!delSet.empty())
{
CGuildInvite *object = *delSet.begin();
delete object;
}
_ReleasedObject.erase(_ReleasedObject.begin());
}
}
void CGuildInvite::clearCache()
{
// remove any unreferenced object from the cache
while (!_ReleasedObject.empty())
{
TObjectSet &delSet = _ReleasedObject.begin()->second;
// unload this objects
while (!delSet.empty())
{
CGuildInvite *object = *delSet.begin();
delete object;
}
_ReleasedObject.erase(_ReleasedObject.begin());
}
}
void CGuildInvite::registerUpdatable()
{
static bool registered = false;
if (!registered)
{
NOPE::CPersistentCache::getInstance().registerCache(&CGuildInvite::cacheCmd);
registered = true;
}
}
// set the pointer on the first pointer of the pointer list (set to null when there is no more pointer)
void CGuildInvite::setFirstPtr(CGuildInvitePtr *ptr)
{
_PtrList = ptr;
if (ptr == NULL)
{
// this is the last pointer !
if (_ObjectState == NOPE::os_transient
|| _ObjectState == NOPE::os_removed)
{
// not a persistent object, or removed object, just delet it
delete this;
}
else if (_ObjectState != NOPE::os_removed)
{
setPersistentState(NOPE::os_released);
}
}
}
// Set the persistent state of the object and do some house keeping
void CGuildInvite::setPersistentState(NOPE::TObjectState state)
{
nlassert(NOPE::AllowedTransition[_ObjectState][state] == true);
if(_ObjectState == NOPE::os_released && state == NOPE::os_removed)
{
// a release object gets removed (e.g. by remove by id)
// delete the object
delete this;
// no more to do
return;
}
if (_ObjectState == NOPE::os_transient && state != NOPE::os_transient)
{
nldebug("NOPE: inserting CGuildInvite @%p in cache with id %u", this, static_cast(_Id));
nlverify(_ObjectCache.insert(std::make_pair(_Id, this)).second);
}
if (_ObjectState != NOPE::os_transient)
nlassert(_ObjectCache.find(_Id) != _ObjectCache.end());
_ObjectState = state;
if (state == NOPE::os_released)
{
_ReleaseDate = NLMISC::CTime::getSecondsSince1970();
nlverify(_ReleasedObject[_ReleaseDate].insert(this).second);
}
else if (state == NOPE::os_removed)
{
nldebug("NOPE: erasing CGuildInvite @%p in cache with id %u", this, static_cast(_Id));
nlverify(_ObjectCache.erase(_Id) == 1);
}
}
CGuildInvitePtr CGuildInvite::load(MSW::CConnection &connection, uint32 id, const char *filename, uint32 lineNum)
{
CGuildInvite *inCache = loadFromCache(id, true);
if (inCache != NULL)
{
return CGuildInvitePtr(inCache, filename, lineNum);
}
std::string qs;
qs = "SELECT ";
qs += "Id, guild_id, session_id";
qs += " FROM guild_invites";
qs += " WHERE Id = '"+NLMISC::toString(id)+"'";
CGuildInvitePtr ret;
if (!connection.query(qs))
{
return ret;
}
MSW::CStoreResult *result = connection.storeResult().release();
nlassert(result->getNumRows() <= 1);
if (result->getNumRows() == 1)
{
ret.assign(new CGuildInvite, filename, lineNum);
// ok, we have an object
result->fetchRow();
result->getField(0, ret->_Id);
result->getField(1, ret->_GuildId);
result->getField(2, ret->_SessionId);
ret->setPersistentState(NOPE::os_clean);
}
delete result;
return ret;
}
bool CGuildInvite::loadChildrenOfCGuild(MSW::CConnection &connection, uint32 parentId, std::vector < CGuildInvitePtr > & container, const char *filename, uint32 lineNum)
{
std::string qs;
qs = "SELECT ";
qs += "Id, guild_id, session_id";
qs += " FROM guild_invites";
qs += " WHERE guild_id = '"+NLMISC::toString(parentId)+"'";
if (!connection.query(qs))
{
return false;
}
std::unique_ptr result(connection.storeResult());
for (uint i=0; igetNumRows(); ++i)
{
CGuildInvite *ret = new CGuildInvite();
// ok, we have an object
result->fetchRow();
result->getField(0, ret->_Id);
result->getField(1, ret->_GuildId);
result->getField(2, ret->_SessionId);
CGuildInvite *inCache = loadFromCache(ret->_Id, true);
if (inCache != NULL)
{
container.push_back(CGuildInvitePtr(inCache, filename, lineNum));
// no more needed
delete ret;
}
else
{
ret->setPersistentState(NOPE::os_clean);
container.push_back(CGuildInvitePtr(ret, filename, lineNum));
}
}
return true;
}
bool CGuildInvite::loadChildrenOfCSession(MSW::CConnection &connection, uint32 parentId, std::vector < CGuildInvitePtr > & container, const char *filename, uint32 lineNum)
{
std::string qs;
qs = "SELECT ";
qs += "Id, guild_id, session_id";
qs += " FROM guild_invites";
qs += " WHERE session_id = '"+NLMISC::toString(parentId)+"'";
if (!connection.query(qs))
{
return false;
}
std::unique_ptr result(connection.storeResult());
for (uint i=0; igetNumRows(); ++i)
{
CGuildInvite *ret = new CGuildInvite();
// ok, we have an object
result->fetchRow();
result->getField(0, ret->_Id);
result->getField(1, ret->_GuildId);
result->getField(2, ret->_SessionId);
CGuildInvite *inCache = loadFromCache(ret->_Id, true);
if (inCache != NULL)
{
container.push_back(CGuildInvitePtr(inCache, filename, lineNum));
// no more needed
delete ret;
}
else
{
ret->setPersistentState(NOPE::os_clean);
container.push_back(CGuildInvitePtr(ret, filename, lineNum));
}
}
return true;
}
void CPlayerRatingPtr::linkPtr()
{
nlassert(_NextPtr == NULL);
nlassert(_PrevPtr == NULL);
if (_Ptr != NULL)
{
_NextPtr = _Ptr->getFirstPtr();
if (_NextPtr != NULL)
{
_PrevPtr = _NextPtr->_PrevPtr;
_PrevPtr->_NextPtr = this;
_NextPtr->_PrevPtr = this;
}
else
{
_NextPtr = this;
_PrevPtr = this;
_Ptr->setFirstPtr(this);
}
}
}
void CPlayerRatingPtr::unlinkPtr()
{
if (_NextPtr == NULL)
{
nlassert(_PrevPtr == NULL);
return;
}
if (_Ptr != NULL)
{
if (_NextPtr == this)
{
nlassert(_PrevPtr == this);
// last pointer !
_Ptr->setFirstPtr(NULL);
}
else
{
if (_Ptr->getFirstPtr() == this)
{
// the first ptr is the current one, we need to switch to next one
_Ptr->setFirstPtr(_NextPtr);
}
}
}
if (_NextPtr != this)
{
nlassert(_PrevPtr != this);
_NextPtr->_PrevPtr = _PrevPtr;
_PrevPtr->_NextPtr = _NextPtr;
}
_NextPtr = NULL;
_PrevPtr = NULL;
}
CPlayerRating::TObjectCache CPlayerRating::_ObjectCache;
CPlayerRating::TReleasedObject CPlayerRating::_ReleasedObject;
// Destructor, delete any children
CPlayerRating::~CPlayerRating()
{
// release childs reference
if (_PtrList != NULL)
{
nlwarning("ERROR : someone try to delete this object, but there are still ptr on it !");
CPlayerRatingPtr *ptr = _PtrList;
do
{
nlwarning(" Pointer created from '%s', line %u", ptr->_FileName, ptr->_LineNum);
ptr = _PtrList->getNextPtr();
} while(ptr != _PtrList);
nlstop;
}
// remove object from cache map
if (_Id != NOPE::INVALID_OBJECT_ID
&& _ObjectState != NOPE::os_removed
&& _ObjectState != NOPE::os_transient)
{
nldebug("NOPE: clearing CPlayerRating @%p from cache with id %u", this, static_cast(_Id));
nlverify(_ObjectCache.erase(_Id) == 1);
}
else if (_ObjectState != NOPE::os_transient)
{
nlassert(_ObjectCache.find(_Id) == _ObjectCache.end());
}
if (_ObjectState == NOPE::os_released)
{
removeFromReleased();
}
else
{
TReleasedObject::iterator it(_ReleasedObject.find(_ReleaseDate));
if (it != _ReleasedObject.end())
{
nlassert(it->second.find(this) == it->second.end());
}
}
}
void CPlayerRating::removeFromReleased()
{
TReleasedObject::iterator it(_ReleasedObject.find(_ReleaseDate));
nlassert(it != _ReleasedObject.end());
TObjectSet &os = it->second;
nlverify(os.erase(this) == 1);
// nb : _ReleasedObject time entry are removed by the cache update
}
bool CPlayerRating::create(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_transient);
std::string qs;
qs = "INSERT INTO player_rating (";
qs += "scenario_id, author, rate_fun, rate_difficulty, rate_accessibility, rate_originality, rate_direction";
qs += ") VALUES (";
qs += "'"+MSW::escapeString(NLMISC::toString(_ScenarioId), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_Author), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_RateFun), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_RateDifficulty), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_RateAccessibility), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_RateOriginality), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_RateDirection), connection)+"'";
qs += ")";
if (connection.query(qs))
{
uint32 _id_ = connection.getLastGeneratedId();
setObjectId(_id_);
setPersistentState(NOPE::os_clean);
// update the parent class instance in cache if any
if (_ScenarioId != 0)
{
// need to update the parent class child list if it is in the cache
CScenario *parent = CScenario::loadFromCache(_ScenarioId, false);
if (parent && parent->_PlayerRatings != NULL)
{
nlassert(std::find(parent->_PlayerRatings->begin(), parent->_PlayerRatings->end(), CPlayerRatingPtr(this, __FILE__, __LINE__)) == parent->_PlayerRatings->end());
parent->_PlayerRatings->push_back(CPlayerRatingPtr(this, __FILE__, __LINE__));
}
}
if (_Author != 0)
{
// need to update the parent class child list if it is in the cache
CCharacter *parent = CCharacter::loadFromCache(_Author, false);
if (parent && parent->_PlayerRatings != NULL)
{
nlassert(std::find(parent->_PlayerRatings->begin(), parent->_PlayerRatings->end(), CPlayerRatingPtr(this, __FILE__, __LINE__)) == parent->_PlayerRatings->end());
parent->_PlayerRatings->push_back(CPlayerRatingPtr(this, __FILE__, __LINE__));
}
}
return true;
}
return false;
}
bool CPlayerRating::update(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_dirty || getPersistentState() == NOPE::os_clean);
if (getPersistentState() == NOPE::os_clean)
// the object is clean, just ignore the save
return true;
std::string qs;
qs = "UPDATE player_rating SET ";
qs += "scenario_id = '"+MSW::escapeString(NLMISC::toString(_ScenarioId), connection)+"'";
qs += ", ";
qs += "author = '"+MSW::escapeString(NLMISC::toString(_Author), connection)+"'";
qs += ", ";
qs += "rate_fun = '"+MSW::escapeString(NLMISC::toString(_RateFun), connection)+"'";
qs += ", ";
qs += "rate_difficulty = '"+MSW::escapeString(NLMISC::toString(_RateDifficulty), connection)+"'";
qs += ", ";
qs += "rate_accessibility = '"+MSW::escapeString(NLMISC::toString(_RateAccessibility), connection)+"'";
qs += ", ";
qs += "rate_originality = '"+MSW::escapeString(NLMISC::toString(_RateOriginality), connection)+"'";
qs += ", ";
qs += "rate_direction = '"+MSW::escapeString(NLMISC::toString(_RateDirection), connection)+"'";
qs += " WHERE Id = '"+NLMISC::toString(_Id)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
setPersistentState(NOPE::os_clean);
return true;
}
}
return false;
}
bool CPlayerRating::remove(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_dirty || getPersistentState() == NOPE::os_clean);
std::string qs;
qs = "DELETE FROM player_rating ";
qs += " WHERE Id = '"+NLMISC::toString(_Id)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
// change the persistant state to 'removed'.
setPersistentState(NOPE::os_removed);
// need to remove ref from parent class container (if any)
{
CScenarioPtr parent(CScenario::loadFromCache(_ScenarioId, true), __FILE__, __LINE__);
if (parent != NULL && parent->_PlayerRatings != NULL)
{
std::vector < CPlayerRatingPtr >::iterator it = std::find(parent->_PlayerRatings->begin(), parent->_PlayerRatings->end(), this);
if (it != parent->_PlayerRatings->end())
{
parent->_PlayerRatings->erase(it);
}
}
}
{
CCharacterPtr parent(CCharacter::loadFromCache(_Author, true), __FILE__, __LINE__);
if (parent != NULL && parent->_PlayerRatings != NULL)
{
std::vector < CPlayerRatingPtr >::iterator it = std::find(parent->_PlayerRatings->begin(), parent->_PlayerRatings->end(), this);
if (it != parent->_PlayerRatings->end())
{
parent->_PlayerRatings->erase(it);
}
}
}
// need to remove ref from parent (if any)
return true;
}
}
return false;
}
bool CPlayerRating::removeById(MSW::CConnection &connection, uint32 id)
{
CPlayerRating *object = loadFromCache(id, true);
if (object != NULL)
{
return object->remove(connection);
}
// not in cache, run a SQL query
std::string qs;
qs = "DELETE FROM player_rating ";
qs += " WHERE Id = '"+NLMISC::toString(id)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
// ok, the row is removed
return true;
}
}
return false;
}
// Try to load the specified object from the memory cache, return NULL if the object is not in the cache
CPlayerRating *CPlayerRating::loadFromCache(uint32 objectId, bool unrelease)
{
// look in the cache
TObjectCache::iterator it(_ObjectCache.find(objectId));
if (it == _ObjectCache.end())
{
// not found, return null
return NULL;
}
else
{
CPlayerRating *object = it->second;
if (object->_ObjectState == NOPE::os_released)
{
if (unrelease)
{
// we need to remove this object from the released object set.
object->removeFromReleased();
object->_ObjectState = NOPE::os_clean;
}
}
return it->second;
}
}
// Receive and execute command from the cache manager.
uint32 CPlayerRating::cacheCmd(NOPE::TCacheCmd cmd)
{
if (cmd == NOPE::cc_update)
{
updateCache();
}
else if (cmd == NOPE::cc_clear)
{
clearCache();
}
else if (cmd == NOPE::cc_dump)
{
dump();
}
else if (cmd == NOPE::cc_instance_count)
{
return (uint32)_ObjectCache.size();
}
// default return value
return 0;
}
void CPlayerRating::dump()
{
nlinfo(" Cache info for class CPlayerRating :");
nlinfo(" There are %u object instances in cache", _ObjectCache.size());
// count the number of object in the released object set
uint32 nbReleased = 0;
TReleasedObject::iterator first(_ReleasedObject.begin()), last(_ReleasedObject.end());
for (; first != last; ++first)
{
nbReleased += (uint32)first->second.size();
}
nlinfo(" There are %u object instances in cache not referenced (waiting deletion or re-use))", nbReleased);
}
void CPlayerRating::updateCache()
{
if (_ReleasedObject.empty())
return;
// 30 s hold in cache
const time_t MAX_CACHE_OLD_TIME = 30;
time_t now = NLMISC::CTime::getSecondsSince1970();
// look for object set older than MAX_CACHE_OLD_TIME and delete them
while (!_ReleasedObject.empty() && _ReleasedObject.begin()->first < now-MAX_CACHE_OLD_TIME)
{
TObjectSet &delSet = _ReleasedObject.begin()->second;
// unload this objects
while (!delSet.empty())
{
CPlayerRating *object = *delSet.begin();
delete object;
}
_ReleasedObject.erase(_ReleasedObject.begin());
}
}
void CPlayerRating::clearCache()
{
// remove any unreferenced object from the cache
while (!_ReleasedObject.empty())
{
TObjectSet &delSet = _ReleasedObject.begin()->second;
// unload this objects
while (!delSet.empty())
{
CPlayerRating *object = *delSet.begin();
delete object;
}
_ReleasedObject.erase(_ReleasedObject.begin());
}
}
void CPlayerRating::registerUpdatable()
{
static bool registered = false;
if (!registered)
{
NOPE::CPersistentCache::getInstance().registerCache(&CPlayerRating::cacheCmd);
registered = true;
}
}
// set the pointer on the first pointer of the pointer list (set to null when there is no more pointer)
void CPlayerRating::setFirstPtr(CPlayerRatingPtr *ptr)
{
_PtrList = ptr;
if (ptr == NULL)
{
// this is the last pointer !
if (_ObjectState == NOPE::os_transient
|| _ObjectState == NOPE::os_removed)
{
// not a persistent object, or removed object, just delet it
delete this;
}
else if (_ObjectState != NOPE::os_removed)
{
setPersistentState(NOPE::os_released);
}
}
}
// Set the persistent state of the object and do some house keeping
void CPlayerRating::setPersistentState(NOPE::TObjectState state)
{
nlassert(NOPE::AllowedTransition[_ObjectState][state] == true);
if(_ObjectState == NOPE::os_released && state == NOPE::os_removed)
{
// a release object gets removed (e.g. by remove by id)
// delete the object
delete this;
// no more to do
return;
}
if (_ObjectState == NOPE::os_transient && state != NOPE::os_transient)
{
nldebug("NOPE: inserting CPlayerRating @%p in cache with id %u", this, static_cast(_Id));
nlverify(_ObjectCache.insert(std::make_pair(_Id, this)).second);
}
if (_ObjectState != NOPE::os_transient)
nlassert(_ObjectCache.find(_Id) != _ObjectCache.end());
_ObjectState = state;
if (state == NOPE::os_released)
{
_ReleaseDate = NLMISC::CTime::getSecondsSince1970();
nlverify(_ReleasedObject[_ReleaseDate].insert(this).second);
}
else if (state == NOPE::os_removed)
{
nldebug("NOPE: erasing CPlayerRating @%p in cache with id %u", this, static_cast(_Id));
nlverify(_ObjectCache.erase(_Id) == 1);
}
}
CPlayerRatingPtr CPlayerRating::load(MSW::CConnection &connection, uint32 id, const char *filename, uint32 lineNum)
{
CPlayerRating *inCache = loadFromCache(id, true);
if (inCache != NULL)
{
return CPlayerRatingPtr(inCache, filename, lineNum);
}
std::string qs;
qs = "SELECT ";
qs += "Id, scenario_id, author, rate_fun, rate_difficulty, rate_accessibility, rate_originality, rate_direction";
qs += " FROM player_rating";
qs += " WHERE Id = '"+NLMISC::toString(id)+"'";
CPlayerRatingPtr ret;
if (!connection.query(qs))
{
return ret;
}
MSW::CStoreResult *result = connection.storeResult().release();
nlassert(result->getNumRows() <= 1);
if (result->getNumRows() == 1)
{
ret.assign(new CPlayerRating, filename, lineNum);
// ok, we have an object
result->fetchRow();
result->getField(0, ret->_Id);
result->getField(1, ret->_ScenarioId);
result->getField(2, ret->_Author);
result->getField(3, ret->_RateFun);
result->getField(4, ret->_RateDifficulty);
result->getField(5, ret->_RateAccessibility);
result->getField(6, ret->_RateOriginality);
result->getField(7, ret->_RateDirection);
ret->setPersistentState(NOPE::os_clean);
}
delete result;
return ret;
}
bool CPlayerRating::loadChildrenOfCScenario(MSW::CConnection &connection, uint32 parentId, std::vector < CPlayerRatingPtr > & container, const char *filename, uint32 lineNum)
{
std::string qs;
qs = "SELECT ";
qs += "Id, scenario_id, author, rate_fun, rate_difficulty, rate_accessibility, rate_originality, rate_direction";
qs += " FROM player_rating";
qs += " WHERE scenario_id = '"+NLMISC::toString(parentId)+"'";
if (!connection.query(qs))
{
return false;
}
std::unique_ptr result(connection.storeResult());
for (uint i=0; igetNumRows(); ++i)
{
CPlayerRating *ret = new CPlayerRating();
// ok, we have an object
result->fetchRow();
result->getField(0, ret->_Id);
result->getField(1, ret->_ScenarioId);
result->getField(2, ret->_Author);
result->getField(3, ret->_RateFun);
result->getField(4, ret->_RateDifficulty);
result->getField(5, ret->_RateAccessibility);
result->getField(6, ret->_RateOriginality);
result->getField(7, ret->_RateDirection);
CPlayerRating *inCache = loadFromCache(ret->_Id, true);
if (inCache != NULL)
{
container.push_back(CPlayerRatingPtr(inCache, filename, lineNum));
// no more needed
delete ret;
}
else
{
ret->setPersistentState(NOPE::os_clean);
container.push_back(CPlayerRatingPtr(ret, filename, lineNum));
}
}
return true;
}
bool CPlayerRating::loadChildrenOfCCharacter(MSW::CConnection &connection, uint32 parentId, std::vector < CPlayerRatingPtr > & container, const char *filename, uint32 lineNum)
{
std::string qs;
qs = "SELECT ";
qs += "Id, scenario_id, author, rate_fun, rate_difficulty, rate_accessibility, rate_originality, rate_direction";
qs += " FROM player_rating";
qs += " WHERE author = '"+NLMISC::toString(parentId)+"'";
if (!connection.query(qs))
{
return false;
}
std::unique_ptr result(connection.storeResult());
for (uint i=0; igetNumRows(); ++i)
{
CPlayerRating *ret = new CPlayerRating();
// ok, we have an object
result->fetchRow();
result->getField(0, ret->_Id);
result->getField(1, ret->_ScenarioId);
result->getField(2, ret->_Author);
result->getField(3, ret->_RateFun);
result->getField(4, ret->_RateDifficulty);
result->getField(5, ret->_RateAccessibility);
result->getField(6, ret->_RateOriginality);
result->getField(7, ret->_RateDirection);
CPlayerRating *inCache = loadFromCache(ret->_Id, true);
if (inCache != NULL)
{
container.push_back(CPlayerRatingPtr(inCache, filename, lineNum));
// no more needed
delete ret;
}
else
{
ret->setPersistentState(NOPE::os_clean);
container.push_back(CPlayerRatingPtr(ret, filename, lineNum));
}
}
return true;
}
void CJournalEntryPtr::linkPtr()
{
nlassert(_NextPtr == NULL);
nlassert(_PrevPtr == NULL);
if (_Ptr != NULL)
{
_NextPtr = _Ptr->getFirstPtr();
if (_NextPtr != NULL)
{
_PrevPtr = _NextPtr->_PrevPtr;
_PrevPtr->_NextPtr = this;
_NextPtr->_PrevPtr = this;
}
else
{
_NextPtr = this;
_PrevPtr = this;
_Ptr->setFirstPtr(this);
}
}
}
void CJournalEntryPtr::unlinkPtr()
{
if (_NextPtr == NULL)
{
nlassert(_PrevPtr == NULL);
return;
}
if (_Ptr != NULL)
{
if (_NextPtr == this)
{
nlassert(_PrevPtr == this);
// last pointer !
_Ptr->setFirstPtr(NULL);
}
else
{
if (_Ptr->getFirstPtr() == this)
{
// the first ptr is the current one, we need to switch to next one
_Ptr->setFirstPtr(_NextPtr);
}
}
}
if (_NextPtr != this)
{
nlassert(_PrevPtr != this);
_NextPtr->_PrevPtr = _PrevPtr;
_PrevPtr->_NextPtr = _NextPtr;
}
_NextPtr = NULL;
_PrevPtr = NULL;
}
CJournalEntry::TObjectCache CJournalEntry::_ObjectCache;
CJournalEntry::TReleasedObject CJournalEntry::_ReleasedObject;
// Destructor, delete any children
CJournalEntry::~CJournalEntry()
{
// release childs reference
if (_PtrList != NULL)
{
nlwarning("ERROR : someone try to delete this object, but there are still ptr on it !");
CJournalEntryPtr *ptr = _PtrList;
do
{
nlwarning(" Pointer created from '%s', line %u", ptr->_FileName, ptr->_LineNum);
ptr = _PtrList->getNextPtr();
} while(ptr != _PtrList);
nlstop;
}
// remove object from cache map
if (_Id != NOPE::INVALID_OBJECT_ID
&& _ObjectState != NOPE::os_removed
&& _ObjectState != NOPE::os_transient)
{
nldebug("NOPE: clearing CJournalEntry @%p from cache with id %u", this, static_cast(_Id));
nlverify(_ObjectCache.erase(_Id) == 1);
}
else if (_ObjectState != NOPE::os_transient)
{
nlassert(_ObjectCache.find(_Id) == _ObjectCache.end());
}
if (_ObjectState == NOPE::os_released)
{
removeFromReleased();
}
else
{
TReleasedObject::iterator it(_ReleasedObject.find(_ReleaseDate));
if (it != _ReleasedObject.end())
{
nlassert(it->second.find(this) == it->second.end());
}
}
}
void CJournalEntry::removeFromReleased()
{
TReleasedObject::iterator it(_ReleasedObject.find(_ReleaseDate));
nlassert(it != _ReleasedObject.end());
TObjectSet &os = it->second;
nlverify(os.erase(this) == 1);
// nb : _ReleasedObject time entry are removed by the cache update
}
bool CJournalEntry::create(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_transient);
std::string qs;
qs = "INSERT INTO journal_entry (";
qs += "session_id, author, type, text, time_stamp";
qs += ") VALUES (";
qs += "'"+MSW::escapeString(NLMISC::toString(_SessionId), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_Author), connection)+"'";
qs += ", ";
qs += "'"+_Type.toString()+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_Text), connection)+"'";
qs += ", ";
qs += "'"+MSW::encodeDate(_TimeStamp)+"'";
qs += ")";
if (connection.query(qs))
{
uint32 _id_ = connection.getLastGeneratedId();
setObjectId(_id_);
setPersistentState(NOPE::os_clean);
// update the parent class instance in cache if any
if (_SessionId != 0)
{
// need to update the parent class child list if it is in the cache
CSession *parent = CSession::loadFromCache(_SessionId, false);
if (parent && parent->_JournalEntries != NULL)
{
nlassert(std::find(parent->_JournalEntries->begin(), parent->_JournalEntries->end(), CJournalEntryPtr(this, __FILE__, __LINE__)) == parent->_JournalEntries->end());
parent->_JournalEntries->push_back(CJournalEntryPtr(this, __FILE__, __LINE__));
}
}
return true;
}
return false;
}
bool CJournalEntry::update(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_dirty || getPersistentState() == NOPE::os_clean);
if (getPersistentState() == NOPE::os_clean)
// the object is clean, just ignore the save
return true;
std::string qs;
qs = "UPDATE journal_entry SET ";
qs += "session_id = '"+MSW::escapeString(NLMISC::toString(_SessionId), connection)+"'";
qs += ", ";
qs += "author = '"+MSW::escapeString(NLMISC::toString(_Author), connection)+"'";
qs += ", ";
qs += "type = '"+_Type.toString()+"'";
qs += ", ";
qs += "text = '"+MSW::escapeString(NLMISC::toString(_Text), connection)+"'";
qs += ", ";
qs += "time_stamp = '"+MSW::encodeDate(_TimeStamp)+"'";
qs += " WHERE Id = '"+NLMISC::toString(_Id)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
setPersistentState(NOPE::os_clean);
return true;
}
}
return false;
}
bool CJournalEntry::remove(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_dirty || getPersistentState() == NOPE::os_clean);
std::string qs;
qs = "DELETE FROM journal_entry ";
qs += " WHERE Id = '"+NLMISC::toString(_Id)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
// change the persistant state to 'removed'.
setPersistentState(NOPE::os_removed);
// need to remove ref from parent class container (if any)
{
CSessionPtr parent(CSession::loadFromCache(_SessionId, true), __FILE__, __LINE__);
if (parent != NULL && parent->_JournalEntries != NULL)
{
std::vector < CJournalEntryPtr >::iterator it = std::find(parent->_JournalEntries->begin(), parent->_JournalEntries->end(), this);
if (it != parent->_JournalEntries->end())
{
parent->_JournalEntries->erase(it);
}
}
}
// need to remove ref from parent (if any)
return true;
}
}
return false;
}
bool CJournalEntry::removeById(MSW::CConnection &connection, uint32 id)
{
CJournalEntry *object = loadFromCache(id, true);
if (object != NULL)
{
return object->remove(connection);
}
// not in cache, run a SQL query
std::string qs;
qs = "DELETE FROM journal_entry ";
qs += " WHERE Id = '"+NLMISC::toString(id)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
// ok, the row is removed
return true;
}
}
return false;
}
// Try to load the specified object from the memory cache, return NULL if the object is not in the cache
CJournalEntry *CJournalEntry::loadFromCache(uint32 objectId, bool unrelease)
{
// look in the cache
TObjectCache::iterator it(_ObjectCache.find(objectId));
if (it == _ObjectCache.end())
{
// not found, return null
return NULL;
}
else
{
CJournalEntry *object = it->second;
if (object->_ObjectState == NOPE::os_released)
{
if (unrelease)
{
// we need to remove this object from the released object set.
object->removeFromReleased();
object->_ObjectState = NOPE::os_clean;
}
}
return it->second;
}
}
// Receive and execute command from the cache manager.
uint32 CJournalEntry::cacheCmd(NOPE::TCacheCmd cmd)
{
if (cmd == NOPE::cc_update)
{
updateCache();
}
else if (cmd == NOPE::cc_clear)
{
clearCache();
}
else if (cmd == NOPE::cc_dump)
{
dump();
}
else if (cmd == NOPE::cc_instance_count)
{
return (uint32)_ObjectCache.size();
}
// default return value
return 0;
}
void CJournalEntry::dump()
{
nlinfo(" Cache info for class CJournalEntry :");
nlinfo(" There are %u object instances in cache", _ObjectCache.size());
// count the number of object in the released object set
uint32 nbReleased = 0;
TReleasedObject::iterator first(_ReleasedObject.begin()), last(_ReleasedObject.end());
for (; first != last; ++first)
{
nbReleased += (uint32)first->second.size();
}
nlinfo(" There are %u object instances in cache not referenced (waiting deletion or re-use))", nbReleased);
}
void CJournalEntry::updateCache()
{
if (_ReleasedObject.empty())
return;
// 30 s hold in cache
const time_t MAX_CACHE_OLD_TIME = 30;
time_t now = NLMISC::CTime::getSecondsSince1970();
// look for object set older than MAX_CACHE_OLD_TIME and delete them
while (!_ReleasedObject.empty() && _ReleasedObject.begin()->first < now-MAX_CACHE_OLD_TIME)
{
TObjectSet &delSet = _ReleasedObject.begin()->second;
// unload this objects
while (!delSet.empty())
{
CJournalEntry *object = *delSet.begin();
delete object;
}
_ReleasedObject.erase(_ReleasedObject.begin());
}
}
void CJournalEntry::clearCache()
{
// remove any unreferenced object from the cache
while (!_ReleasedObject.empty())
{
TObjectSet &delSet = _ReleasedObject.begin()->second;
// unload this objects
while (!delSet.empty())
{
CJournalEntry *object = *delSet.begin();
delete object;
}
_ReleasedObject.erase(_ReleasedObject.begin());
}
}
void CJournalEntry::registerUpdatable()
{
static bool registered = false;
if (!registered)
{
NOPE::CPersistentCache::getInstance().registerCache(&CJournalEntry::cacheCmd);
registered = true;
}
}
// set the pointer on the first pointer of the pointer list (set to null when there is no more pointer)
void CJournalEntry::setFirstPtr(CJournalEntryPtr *ptr)
{
_PtrList = ptr;
if (ptr == NULL)
{
// this is the last pointer !
if (_ObjectState == NOPE::os_transient
|| _ObjectState == NOPE::os_removed)
{
// not a persistent object, or removed object, just delet it
delete this;
}
else if (_ObjectState != NOPE::os_removed)
{
setPersistentState(NOPE::os_released);
}
}
}
// Set the persistent state of the object and do some house keeping
void CJournalEntry::setPersistentState(NOPE::TObjectState state)
{
nlassert(NOPE::AllowedTransition[_ObjectState][state] == true);
if(_ObjectState == NOPE::os_released && state == NOPE::os_removed)
{
// a release object gets removed (e.g. by remove by id)
// delete the object
delete this;
// no more to do
return;
}
if (_ObjectState == NOPE::os_transient && state != NOPE::os_transient)
{
nldebug("NOPE: inserting CJournalEntry @%p in cache with id %u", this, static_cast(_Id));
nlverify(_ObjectCache.insert(std::make_pair(_Id, this)).second);
}
if (_ObjectState != NOPE::os_transient)
nlassert(_ObjectCache.find(_Id) != _ObjectCache.end());
_ObjectState = state;
if (state == NOPE::os_released)
{
_ReleaseDate = NLMISC::CTime::getSecondsSince1970();
nlverify(_ReleasedObject[_ReleaseDate].insert(this).second);
}
else if (state == NOPE::os_removed)
{
nldebug("NOPE: erasing CJournalEntry @%p in cache with id %u", this, static_cast(_Id));
nlverify(_ObjectCache.erase(_Id) == 1);
}
}
CJournalEntryPtr CJournalEntry::load(MSW::CConnection &connection, uint32 id, const char *filename, uint32 lineNum)
{
CJournalEntry *inCache = loadFromCache(id, true);
if (inCache != NULL)
{
return CJournalEntryPtr(inCache, filename, lineNum);
}
std::string qs;
qs = "SELECT ";
qs += "Id, session_id, author, type, text, time_stamp";
qs += " FROM journal_entry";
qs += " WHERE Id = '"+NLMISC::toString(id)+"'";
CJournalEntryPtr ret;
if (!connection.query(qs))
{
return ret;
}
MSW::CStoreResult *result = connection.storeResult().release();
nlassert(result->getNumRows() <= 1);
if (result->getNumRows() == 1)
{
ret.assign(new CJournalEntry, filename, lineNum);
// ok, we have an object
result->fetchRow();
result->getField(0, ret->_Id);
result->getField(1, ret->_SessionId);
result->getField(2, ret->_Author);
{
std::string s;
result->getField(3, s);
ret->_Type = TJournalEntryType(s);
}
result->getField(4, ret->_Text);
result->getDateField(5, ret->_TimeStamp);
ret->setPersistentState(NOPE::os_clean);
}
delete result;
return ret;
}
bool CJournalEntry::loadChildrenOfCSession(MSW::CConnection &connection, uint32 parentId, std::vector < CJournalEntryPtr > & container, const char *filename, uint32 lineNum)
{
std::string qs;
qs = "SELECT ";
qs += "Id, session_id, author, type, text, time_stamp";
qs += " FROM journal_entry";
qs += " WHERE session_id = '"+NLMISC::toString(parentId)+"'";
if (!connection.query(qs))
{
return false;
}
std::unique_ptr result(connection.storeResult());
for (uint i=0; igetNumRows(); ++i)
{
CJournalEntry *ret = new CJournalEntry();
// ok, we have an object
result->fetchRow();
result->getField(0, ret->_Id);
result->getField(1, ret->_SessionId);
result->getField(2, ret->_Author);
{
std::string s;
result->getField(3, s);
ret->_Type = TJournalEntryType(s);
}
result->getField(4, ret->_Text);
result->getDateField(5, ret->_TimeStamp);
CJournalEntry *inCache = loadFromCache(ret->_Id, true);
if (inCache != NULL)
{
container.push_back(CJournalEntryPtr(inCache, filename, lineNum));
// no more needed
delete ret;
}
else
{
ret->setPersistentState(NOPE::os_clean);
container.push_back(CJournalEntryPtr(ret, filename, lineNum));
}
}
return true;
}
void CFolderPtr::linkPtr()
{
nlassert(_NextPtr == NULL);
nlassert(_PrevPtr == NULL);
if (_Ptr != NULL)
{
_NextPtr = _Ptr->getFirstPtr();
if (_NextPtr != NULL)
{
_PrevPtr = _NextPtr->_PrevPtr;
_PrevPtr->_NextPtr = this;
_NextPtr->_PrevPtr = this;
}
else
{
_NextPtr = this;
_PrevPtr = this;
_Ptr->setFirstPtr(this);
}
}
}
void CFolderPtr::unlinkPtr()
{
if (_NextPtr == NULL)
{
nlassert(_PrevPtr == NULL);
return;
}
if (_Ptr != NULL)
{
if (_NextPtr == this)
{
nlassert(_PrevPtr == this);
// last pointer !
_Ptr->setFirstPtr(NULL);
}
else
{
if (_Ptr->getFirstPtr() == this)
{
// the first ptr is the current one, we need to switch to next one
_Ptr->setFirstPtr(_NextPtr);
}
}
}
if (_NextPtr != this)
{
nlassert(_PrevPtr != this);
_NextPtr->_PrevPtr = _PrevPtr;
_PrevPtr->_NextPtr = _NextPtr;
}
_NextPtr = NULL;
_PrevPtr = NULL;
}
CFolder::TObjectCache CFolder::_ObjectCache;
CFolder::TReleasedObject CFolder::_ReleasedObject;
// Destructor, delete any children
CFolder::~CFolder()
{
// release childs reference
if (_FolderAccess != NULL)
delete _FolderAccess;
if (_Sessions != NULL)
delete _Sessions;
if (_PtrList != NULL)
{
nlwarning("ERROR : someone try to delete this object, but there are still ptr on it !");
CFolderPtr *ptr = _PtrList;
do
{
nlwarning(" Pointer created from '%s', line %u", ptr->_FileName, ptr->_LineNum);
ptr = _PtrList->getNextPtr();
} while(ptr != _PtrList);
nlstop;
}
// remove object from cache map
if (_Id != NOPE::INVALID_OBJECT_ID
&& _ObjectState != NOPE::os_removed
&& _ObjectState != NOPE::os_transient)
{
nldebug("NOPE: clearing CFolder @%p from cache with id %u", this, static_cast(_Id));
nlverify(_ObjectCache.erase(_Id) == 1);
}
else if (_ObjectState != NOPE::os_transient)
{
nlassert(_ObjectCache.find(_Id) == _ObjectCache.end());
}
if (_ObjectState == NOPE::os_released)
{
removeFromReleased();
}
else
{
TReleasedObject::iterator it(_ReleasedObject.find(_ReleaseDate));
if (it != _ReleasedObject.end())
{
nlassert(it->second.find(this) == it->second.end());
}
}
}
void CFolder::removeFromReleased()
{
TReleasedObject::iterator it(_ReleasedObject.find(_ReleaseDate));
nlassert(it != _ReleasedObject.end());
TObjectSet &os = it->second;
nlverify(os.erase(this) == 1);
// nb : _ReleasedObject time entry are removed by the cache update
}
bool CFolder::create(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_transient);
std::string qs;
qs = "INSERT INTO folder (";
qs += "author, title, comments";
qs += ") VALUES (";
qs += "'"+MSW::escapeString(NLMISC::toString(_Author), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_Title), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_Comments), connection)+"'";
qs += ")";
if (connection.query(qs))
{
uint32 _id_ = connection.getLastGeneratedId();
setObjectId(_id_);
setPersistentState(NOPE::os_clean);
// update the parent class instance in cache if any
if (_Author != 0)
{
// need to update the parent class child list if it is in the cache
CRingUser *parent = CRingUser::loadFromCache(_Author, false);
if (parent && parent->_Folders != NULL)
{
nlassert(std::find(parent->_Folders->begin(), parent->_Folders->end(), CFolderPtr(this, __FILE__, __LINE__)) == parent->_Folders->end());
parent->_Folders->push_back(CFolderPtr(this, __FILE__, __LINE__));
}
}
return true;
}
return false;
}
bool CFolder::update(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_dirty || getPersistentState() == NOPE::os_clean);
if (getPersistentState() == NOPE::os_clean)
// the object is clean, just ignore the save
return true;
std::string qs;
qs = "UPDATE folder SET ";
qs += "author = '"+MSW::escapeString(NLMISC::toString(_Author), connection)+"'";
qs += ", ";
qs += "title = '"+MSW::escapeString(NLMISC::toString(_Title), connection)+"'";
qs += ", ";
qs += "comments = '"+MSW::escapeString(NLMISC::toString(_Comments), connection)+"'";
qs += " WHERE Id = '"+NLMISC::toString(_Id)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
setPersistentState(NOPE::os_clean);
return true;
}
}
return false;
}
bool CFolder::remove(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_dirty || getPersistentState() == NOPE::os_clean);
std::string qs;
qs = "DELETE FROM folder ";
qs += " WHERE Id = '"+NLMISC::toString(_Id)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
{
// cascading deletion for vector child FolderAccess
nlassert(loadFolderAccess(connection, __FILE__, __LINE__));
const std::vector < CFolderAccessPtr > & childs = getFolderAccess();
while (!childs.empty())
{
getFolderAccessByIndex((uint32)childs.size()-1)->remove(connection);
}
}
{
// unreference (and update) for vector child Sessions
nlassert(loadSessions(connection, __FILE__, __LINE__));
const std::vector < CSessionPtr > & childs = getSessions();
for (uint i=0; i < childs.size(); ++i)
{
getSessionsByIndex(i)->setFolderId(0);
getSessionsByIndex(i)->update(connection);
}
}
// change the persistant state to 'removed'.
setPersistentState(NOPE::os_removed);
// need to remove ref from parent class container (if any)
{
CRingUserPtr parent(CRingUser::loadFromCache(_Author, true), __FILE__, __LINE__);
if (parent != NULL && parent->_Folders != NULL)
{
std::vector < CFolderPtr >::iterator it = std::find(parent->_Folders->begin(), parent->_Folders->end(), this);
if (it != parent->_Folders->end())
{
parent->_Folders->erase(it);
}
}
}
// need to remove ref from parent (if any)
return true;
}
}
return false;
}
bool CFolder::removeById(MSW::CConnection &connection, uint32 id)
{
CFolder *object = loadFromCache(id, true);
if (object != NULL)
{
return object->remove(connection);
}
// not in cache, run a SQL query
std::string qs;
qs = "DELETE FROM folder ";
qs += " WHERE Id = '"+NLMISC::toString(id)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
// ok, the row is removed
return true;
}
}
return false;
}
// Try to load the specified object from the memory cache, return NULL if the object is not in the cache
CFolder *CFolder::loadFromCache(uint32 objectId, bool unrelease)
{
// look in the cache
TObjectCache::iterator it(_ObjectCache.find(objectId));
if (it == _ObjectCache.end())
{
// not found, return null
return NULL;
}
else
{
CFolder *object = it->second;
if (object->_ObjectState == NOPE::os_released)
{
if (unrelease)
{
// we need to remove this object from the released object set.
object->removeFromReleased();
object->_ObjectState = NOPE::os_clean;
}
}
return it->second;
}
}
// Receive and execute command from the cache manager.
uint32 CFolder::cacheCmd(NOPE::TCacheCmd cmd)
{
if (cmd == NOPE::cc_update)
{
updateCache();
}
else if (cmd == NOPE::cc_clear)
{
clearCache();
}
else if (cmd == NOPE::cc_dump)
{
dump();
}
else if (cmd == NOPE::cc_instance_count)
{
return (uint32)_ObjectCache.size();
}
// default return value
return 0;
}
void CFolder::dump()
{
nlinfo(" Cache info for class CFolder :");
nlinfo(" There are %u object instances in cache", _ObjectCache.size());
// count the number of object in the released object set
uint32 nbReleased = 0;
TReleasedObject::iterator first(_ReleasedObject.begin()), last(_ReleasedObject.end());
for (; first != last; ++first)
{
nbReleased += (uint32)first->second.size();
}
nlinfo(" There are %u object instances in cache not referenced (waiting deletion or re-use))", nbReleased);
}
void CFolder::updateCache()
{
if (_ReleasedObject.empty())
return;
// 30 s hold in cache
const time_t MAX_CACHE_OLD_TIME = 30;
time_t now = NLMISC::CTime::getSecondsSince1970();
// look for object set older than MAX_CACHE_OLD_TIME and delete them
while (!_ReleasedObject.empty() && _ReleasedObject.begin()->first < now-MAX_CACHE_OLD_TIME)
{
TObjectSet &delSet = _ReleasedObject.begin()->second;
// unload this objects
while (!delSet.empty())
{
CFolder *object = *delSet.begin();
delete object;
}
_ReleasedObject.erase(_ReleasedObject.begin());
}
}
void CFolder::clearCache()
{
// remove any unreferenced object from the cache
while (!_ReleasedObject.empty())
{
TObjectSet &delSet = _ReleasedObject.begin()->second;
// unload this objects
while (!delSet.empty())
{
CFolder *object = *delSet.begin();
delete object;
}
_ReleasedObject.erase(_ReleasedObject.begin());
}
}
void CFolder::registerUpdatable()
{
static bool registered = false;
if (!registered)
{
NOPE::CPersistentCache::getInstance().registerCache(&CFolder::cacheCmd);
registered = true;
}
}
// set the pointer on the first pointer of the pointer list (set to null when there is no more pointer)
void CFolder::setFirstPtr(CFolderPtr *ptr)
{
_PtrList = ptr;
if (ptr == NULL)
{
// this is the last pointer !
if (_ObjectState == NOPE::os_transient
|| _ObjectState == NOPE::os_removed)
{
// not a persistent object, or removed object, just delet it
delete this;
}
else if (_ObjectState != NOPE::os_removed)
{
setPersistentState(NOPE::os_released);
}
}
}
// Set the persistent state of the object and do some house keeping
void CFolder::setPersistentState(NOPE::TObjectState state)
{
nlassert(NOPE::AllowedTransition[_ObjectState][state] == true);
if(_ObjectState == NOPE::os_released && state == NOPE::os_removed)
{
// a release object gets removed (e.g. by remove by id)
// delete the object
delete this;
// no more to do
return;
}
if (_ObjectState == NOPE::os_transient && state != NOPE::os_transient)
{
nldebug("NOPE: inserting CFolder @%p in cache with id %u", this, static_cast(_Id));
nlverify(_ObjectCache.insert(std::make_pair(_Id, this)).second);
}
if (_ObjectState != NOPE::os_transient)
nlassert(_ObjectCache.find(_Id) != _ObjectCache.end());
_ObjectState = state;
if (state == NOPE::os_released)
{
_ReleaseDate = NLMISC::CTime::getSecondsSince1970();
nlverify(_ReleasedObject[_ReleaseDate].insert(this).second);
}
else if (state == NOPE::os_removed)
{
nldebug("NOPE: erasing CFolder @%p in cache with id %u", this, static_cast(_Id));
nlverify(_ObjectCache.erase(_Id) == 1);
}
}
CFolderPtr CFolder::load(MSW::CConnection &connection, uint32 id, const char *filename, uint32 lineNum)
{
CFolder *inCache = loadFromCache(id, true);
if (inCache != NULL)
{
return CFolderPtr(inCache, filename, lineNum);
}
std::string qs;
qs = "SELECT ";
qs += "Id, author, title, comments";
qs += " FROM folder";
qs += " WHERE Id = '"+NLMISC::toString(id)+"'";
CFolderPtr ret;
if (!connection.query(qs))
{
return ret;
}
MSW::CStoreResult *result = connection.storeResult().release();
nlassert(result->getNumRows() <= 1);
if (result->getNumRows() == 1)
{
ret.assign(new CFolder, filename, lineNum);
// ok, we have an object
result->fetchRow();
result->getField(0, ret->_Id);
result->getField(1, ret->_Author);
result->getField(2, ret->_Title);
result->getField(3, ret->_Comments);
ret->setPersistentState(NOPE::os_clean);
}
delete result;
return ret;
}
bool CFolder::loadChildrenOfCRingUser(MSW::CConnection &connection, uint32 parentId, std::vector < CFolderPtr > & container, const char *filename, uint32 lineNum)
{
std::string qs;
qs = "SELECT ";
qs += "Id, author, title, comments";
qs += " FROM folder";
qs += " WHERE author = '"+NLMISC::toString(parentId)+"'";
if (!connection.query(qs))
{
return false;
}
std::unique_ptr result(connection.storeResult());
for (uint i=0; igetNumRows(); ++i)
{
CFolder *ret = new CFolder();
// ok, we have an object
result->fetchRow();
result->getField(0, ret->_Id);
result->getField(1, ret->_Author);
result->getField(2, ret->_Title);
result->getField(3, ret->_Comments);
CFolder *inCache = loadFromCache(ret->_Id, true);
if (inCache != NULL)
{
container.push_back(CFolderPtr(inCache, filename, lineNum));
// no more needed
delete ret;
}
else
{
ret->setPersistentState(NOPE::os_clean);
container.push_back(CFolderPtr(ret, filename, lineNum));
}
}
return true;
}
bool CFolder::loadFolderAccess(MSW::CConnection &connection, const char *filename, uint32 lineNum)
{
bool ret = true;
if (_FolderAccess != NULL)
{
// the children are already loaded, just return true
return true;
}
// allocate the container
_FolderAccess = new std::vector < CFolderAccessPtr >;
// load the childs
ret &= CFolderAccess::loadChildrenOfCFolder(connection, getObjectId(), *_FolderAccess, filename, lineNum);
return ret;
}
const std::vector &CFolder::getFolderAccess() const
{
nlassert(_FolderAccess != NULL);
return *_FolderAccess;
}
CFolderAccessPtr &CFolder::getFolderAccessByIndex(uint32 index) const
{
nlassert(_FolderAccess != NULL);
nlassert(index < _FolderAccess->size());
return const_cast< CFolderAccessPtr & >(_FolderAccess->operator[](index));
}
CFolderAccessPtr &CFolder::getFolderAccessById(uint32 id) const
{
nlassert(_FolderAccess != NULL);
std::vector::const_iterator first(_FolderAccess->begin()), last(_FolderAccess->end());
for (; first != last; ++first)
{
const CFolderAccessPtr &child = *first;
if (child->getObjectId() == id)
{
return const_cast< CFolderAccessPtr & >(child);
}
}
// no object with this id, return a null pointer
static CFolderAccessPtr nil;
return nil;
}
bool CFolder::loadSessions(MSW::CConnection &connection, const char *filename, uint32 lineNum)
{
bool ret = true;
if (_Sessions != NULL)
{
// the children are already loaded, just return true
return true;
}
// allocate the container
_Sessions = new std::vector < CSessionPtr >;
// load the childs
ret &= CSession::loadChildrenOfCFolder(connection, getObjectId(), *_Sessions, filename, lineNum);
return ret;
}
const std::vector &CFolder::getSessions() const
{
nlassert(_Sessions != NULL);
return *_Sessions;
}
CSessionPtr &CFolder::getSessionsByIndex(uint32 index) const
{
nlassert(_Sessions != NULL);
nlassert(index < _Sessions->size());
return const_cast< CSessionPtr & >(_Sessions->operator[](index));
}
CSessionPtr &CFolder::getSessionsById(uint32 id) const
{
nlassert(_Sessions != NULL);
std::vector::const_iterator first(_Sessions->begin()), last(_Sessions->end());
for (; first != last; ++first)
{
const CSessionPtr &child = *first;
if (child->getObjectId() == id)
{
return const_cast< CSessionPtr & >(child);
}
}
// no object with this id, return a null pointer
static CSessionPtr nil;
return nil;
}
void CFolderAccessPtr::linkPtr()
{
nlassert(_NextPtr == NULL);
nlassert(_PrevPtr == NULL);
if (_Ptr != NULL)
{
_NextPtr = _Ptr->getFirstPtr();
if (_NextPtr != NULL)
{
_PrevPtr = _NextPtr->_PrevPtr;
_PrevPtr->_NextPtr = this;
_NextPtr->_PrevPtr = this;
}
else
{
_NextPtr = this;
_PrevPtr = this;
_Ptr->setFirstPtr(this);
}
}
}
void CFolderAccessPtr::unlinkPtr()
{
if (_NextPtr == NULL)
{
nlassert(_PrevPtr == NULL);
return;
}
if (_Ptr != NULL)
{
if (_NextPtr == this)
{
nlassert(_PrevPtr == this);
// last pointer !
_Ptr->setFirstPtr(NULL);
}
else
{
if (_Ptr->getFirstPtr() == this)
{
// the first ptr is the current one, we need to switch to next one
_Ptr->setFirstPtr(_NextPtr);
}
}
}
if (_NextPtr != this)
{
nlassert(_PrevPtr != this);
_NextPtr->_PrevPtr = _PrevPtr;
_PrevPtr->_NextPtr = _NextPtr;
}
_NextPtr = NULL;
_PrevPtr = NULL;
}
CFolderAccess::TObjectCache CFolderAccess::_ObjectCache;
CFolderAccess::TReleasedObject CFolderAccess::_ReleasedObject;
// Destructor, delete any children
CFolderAccess::~CFolderAccess()
{
// release childs reference
if (_PtrList != NULL)
{
nlwarning("ERROR : someone try to delete this object, but there are still ptr on it !");
CFolderAccessPtr *ptr = _PtrList;
do
{
nlwarning(" Pointer created from '%s', line %u", ptr->_FileName, ptr->_LineNum);
ptr = _PtrList->getNextPtr();
} while(ptr != _PtrList);
nlstop;
}
// remove object from cache map
if (_Id != NOPE::INVALID_OBJECT_ID
&& _ObjectState != NOPE::os_removed
&& _ObjectState != NOPE::os_transient)
{
nldebug("NOPE: clearing CFolderAccess @%p from cache with id %u", this, static_cast(_Id));
nlverify(_ObjectCache.erase(_Id) == 1);
}
else if (_ObjectState != NOPE::os_transient)
{
nlassert(_ObjectCache.find(_Id) == _ObjectCache.end());
}
if (_ObjectState == NOPE::os_released)
{
removeFromReleased();
}
else
{
TReleasedObject::iterator it(_ReleasedObject.find(_ReleaseDate));
if (it != _ReleasedObject.end())
{
nlassert(it->second.find(this) == it->second.end());
}
}
}
void CFolderAccess::removeFromReleased()
{
TReleasedObject::iterator it(_ReleasedObject.find(_ReleaseDate));
nlassert(it != _ReleasedObject.end());
TObjectSet &os = it->second;
nlverify(os.erase(this) == 1);
// nb : _ReleasedObject time entry are removed by the cache update
}
bool CFolderAccess::create(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_transient);
std::string qs;
qs = "INSERT INTO folder_access (";
qs += "folder_id, user_id";
qs += ") VALUES (";
qs += "'"+MSW::escapeString(NLMISC::toString(_FolderId), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_UserId), connection)+"'";
qs += ")";
if (connection.query(qs))
{
uint32 _id_ = connection.getLastGeneratedId();
setObjectId(_id_);
setPersistentState(NOPE::os_clean);
// update the parent class instance in cache if any
if (_UserId != 0)
{
// need to update the parent class child list if it is in the cache
CRingUser *parent = CRingUser::loadFromCache(_UserId, false);
if (parent && parent->_FolderAccess != NULL)
{
nlassert(std::find(parent->_FolderAccess->begin(), parent->_FolderAccess->end(), CFolderAccessPtr(this, __FILE__, __LINE__)) == parent->_FolderAccess->end());
parent->_FolderAccess->push_back(CFolderAccessPtr(this, __FILE__, __LINE__));
}
}
if (_FolderId != 0)
{
// need to update the parent class child list if it is in the cache
CFolder *parent = CFolder::loadFromCache(_FolderId, false);
if (parent && parent->_FolderAccess != NULL)
{
nlassert(std::find(parent->_FolderAccess->begin(), parent->_FolderAccess->end(), CFolderAccessPtr(this, __FILE__, __LINE__)) == parent->_FolderAccess->end());
parent->_FolderAccess->push_back(CFolderAccessPtr(this, __FILE__, __LINE__));
}
}
return true;
}
return false;
}
bool CFolderAccess::update(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_dirty || getPersistentState() == NOPE::os_clean);
if (getPersistentState() == NOPE::os_clean)
// the object is clean, just ignore the save
return true;
std::string qs;
qs = "UPDATE folder_access SET ";
qs += "folder_id = '"+MSW::escapeString(NLMISC::toString(_FolderId), connection)+"'";
qs += ", ";
qs += "user_id = '"+MSW::escapeString(NLMISC::toString(_UserId), connection)+"'";
qs += " WHERE Id = '"+NLMISC::toString(_Id)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
setPersistentState(NOPE::os_clean);
return true;
}
}
return false;
}
bool CFolderAccess::remove(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_dirty || getPersistentState() == NOPE::os_clean);
std::string qs;
qs = "DELETE FROM folder_access ";
qs += " WHERE Id = '"+NLMISC::toString(_Id)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
// change the persistant state to 'removed'.
setPersistentState(NOPE::os_removed);
// need to remove ref from parent class container (if any)
{
CRingUserPtr parent(CRingUser::loadFromCache(_UserId, true), __FILE__, __LINE__);
if (parent != NULL && parent->_FolderAccess != NULL)
{
std::vector < CFolderAccessPtr >::iterator it = std::find(parent->_FolderAccess->begin(), parent->_FolderAccess->end(), this);
if (it != parent->_FolderAccess->end())
{
parent->_FolderAccess->erase(it);
}
}
}
{
CFolderPtr parent(CFolder::loadFromCache(_FolderId, true), __FILE__, __LINE__);
if (parent != NULL && parent->_FolderAccess != NULL)
{
std::vector < CFolderAccessPtr >::iterator it = std::find(parent->_FolderAccess->begin(), parent->_FolderAccess->end(), this);
if (it != parent->_FolderAccess->end())
{
parent->_FolderAccess->erase(it);
}
}
}
// need to remove ref from parent (if any)
return true;
}
}
return false;
}
bool CFolderAccess::removeById(MSW::CConnection &connection, uint32 id)
{
CFolderAccess *object = loadFromCache(id, true);
if (object != NULL)
{
return object->remove(connection);
}
// not in cache, run a SQL query
std::string qs;
qs = "DELETE FROM folder_access ";
qs += " WHERE Id = '"+NLMISC::toString(id)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
// ok, the row is removed
return true;
}
}
return false;
}
// Try to load the specified object from the memory cache, return NULL if the object is not in the cache
CFolderAccess *CFolderAccess::loadFromCache(uint32 objectId, bool unrelease)
{
// look in the cache
TObjectCache::iterator it(_ObjectCache.find(objectId));
if (it == _ObjectCache.end())
{
// not found, return null
return NULL;
}
else
{
CFolderAccess *object = it->second;
if (object->_ObjectState == NOPE::os_released)
{
if (unrelease)
{
// we need to remove this object from the released object set.
object->removeFromReleased();
object->_ObjectState = NOPE::os_clean;
}
}
return it->second;
}
}
// Receive and execute command from the cache manager.
uint32 CFolderAccess::cacheCmd(NOPE::TCacheCmd cmd)
{
if (cmd == NOPE::cc_update)
{
updateCache();
}
else if (cmd == NOPE::cc_clear)
{
clearCache();
}
else if (cmd == NOPE::cc_dump)
{
dump();
}
else if (cmd == NOPE::cc_instance_count)
{
return (uint32)_ObjectCache.size();
}
// default return value
return 0;
}
void CFolderAccess::dump()
{
nlinfo(" Cache info for class CFolderAccess :");
nlinfo(" There are %u object instances in cache", _ObjectCache.size());
// count the number of object in the released object set
uint32 nbReleased = 0;
TReleasedObject::iterator first(_ReleasedObject.begin()), last(_ReleasedObject.end());
for (; first != last; ++first)
{
nbReleased += (uint32)first->second.size();
}
nlinfo(" There are %u object instances in cache not referenced (waiting deletion or re-use))", nbReleased);
}
void CFolderAccess::updateCache()
{
if (_ReleasedObject.empty())
return;
// 30 s hold in cache
const time_t MAX_CACHE_OLD_TIME = 30;
time_t now = NLMISC::CTime::getSecondsSince1970();
// look for object set older than MAX_CACHE_OLD_TIME and delete them
while (!_ReleasedObject.empty() && _ReleasedObject.begin()->first < now-MAX_CACHE_OLD_TIME)
{
TObjectSet &delSet = _ReleasedObject.begin()->second;
// unload this objects
while (!delSet.empty())
{
CFolderAccess *object = *delSet.begin();
delete object;
}
_ReleasedObject.erase(_ReleasedObject.begin());
}
}
void CFolderAccess::clearCache()
{
// remove any unreferenced object from the cache
while (!_ReleasedObject.empty())
{
TObjectSet &delSet = _ReleasedObject.begin()->second;
// unload this objects
while (!delSet.empty())
{
CFolderAccess *object = *delSet.begin();
delete object;
}
_ReleasedObject.erase(_ReleasedObject.begin());
}
}
void CFolderAccess::registerUpdatable()
{
static bool registered = false;
if (!registered)
{
NOPE::CPersistentCache::getInstance().registerCache(&CFolderAccess::cacheCmd);
registered = true;
}
}
// set the pointer on the first pointer of the pointer list (set to null when there is no more pointer)
void CFolderAccess::setFirstPtr(CFolderAccessPtr *ptr)
{
_PtrList = ptr;
if (ptr == NULL)
{
// this is the last pointer !
if (_ObjectState == NOPE::os_transient
|| _ObjectState == NOPE::os_removed)
{
// not a persistent object, or removed object, just delet it
delete this;
}
else if (_ObjectState != NOPE::os_removed)
{
setPersistentState(NOPE::os_released);
}
}
}
// Set the persistent state of the object and do some house keeping
void CFolderAccess::setPersistentState(NOPE::TObjectState state)
{
nlassert(NOPE::AllowedTransition[_ObjectState][state] == true);
if(_ObjectState == NOPE::os_released && state == NOPE::os_removed)
{
// a release object gets removed (e.g. by remove by id)
// delete the object
delete this;
// no more to do
return;
}
if (_ObjectState == NOPE::os_transient && state != NOPE::os_transient)
{
nldebug("NOPE: inserting CFolderAccess @%p in cache with id %u", this, static_cast(_Id));
nlverify(_ObjectCache.insert(std::make_pair(_Id, this)).second);
}
if (_ObjectState != NOPE::os_transient)
nlassert(_ObjectCache.find(_Id) != _ObjectCache.end());
_ObjectState = state;
if (state == NOPE::os_released)
{
_ReleaseDate = NLMISC::CTime::getSecondsSince1970();
nlverify(_ReleasedObject[_ReleaseDate].insert(this).second);
}
else if (state == NOPE::os_removed)
{
nldebug("NOPE: erasing CFolderAccess @%p in cache with id %u", this, static_cast(_Id));
nlverify(_ObjectCache.erase(_Id) == 1);
}
}
CFolderAccessPtr CFolderAccess::load(MSW::CConnection &connection, uint32 id, const char *filename, uint32 lineNum)
{
CFolderAccess *inCache = loadFromCache(id, true);
if (inCache != NULL)
{
return CFolderAccessPtr(inCache, filename, lineNum);
}
std::string qs;
qs = "SELECT ";
qs += "Id, folder_id, user_id";
qs += " FROM folder_access";
qs += " WHERE Id = '"+NLMISC::toString(id)+"'";
CFolderAccessPtr ret;
if (!connection.query(qs))
{
return ret;
}
MSW::CStoreResult *result = connection.storeResult().release();
nlassert(result->getNumRows() <= 1);
if (result->getNumRows() == 1)
{
ret.assign(new CFolderAccess, filename, lineNum);
// ok, we have an object
result->fetchRow();
result->getField(0, ret->_Id);
result->getField(1, ret->_FolderId);
result->getField(2, ret->_UserId);
ret->setPersistentState(NOPE::os_clean);
}
delete result;
return ret;
}
bool CFolderAccess::loadChildrenOfCRingUser(MSW::CConnection &connection, uint32 parentId, std::vector < CFolderAccessPtr > & container, const char *filename, uint32 lineNum)
{
std::string qs;
qs = "SELECT ";
qs += "Id, folder_id, user_id";
qs += " FROM folder_access";
qs += " WHERE user_id = '"+NLMISC::toString(parentId)+"'";
if (!connection.query(qs))
{
return false;
}
std::unique_ptr result(connection.storeResult());
for (uint i=0; igetNumRows(); ++i)
{
CFolderAccess *ret = new CFolderAccess();
// ok, we have an object
result->fetchRow();
result->getField(0, ret->_Id);
result->getField(1, ret->_FolderId);
result->getField(2, ret->_UserId);
CFolderAccess *inCache = loadFromCache(ret->_Id, true);
if (inCache != NULL)
{
container.push_back(CFolderAccessPtr(inCache, filename, lineNum));
// no more needed
delete ret;
}
else
{
ret->setPersistentState(NOPE::os_clean);
container.push_back(CFolderAccessPtr(ret, filename, lineNum));
}
}
return true;
}
bool CFolderAccess::loadChildrenOfCFolder(MSW::CConnection &connection, uint32 parentId, std::vector < CFolderAccessPtr > & container, const char *filename, uint32 lineNum)
{
std::string qs;
qs = "SELECT ";
qs += "Id, folder_id, user_id";
qs += " FROM folder_access";
qs += " WHERE folder_id = '"+NLMISC::toString(parentId)+"'";
if (!connection.query(qs))
{
return false;
}
std::unique_ptr result(connection.storeResult());
for (uint i=0; igetNumRows(); ++i)
{
CFolderAccess *ret = new CFolderAccess();
// ok, we have an object
result->fetchRow();
result->getField(0, ret->_Id);
result->getField(1, ret->_FolderId);
result->getField(2, ret->_UserId);
CFolderAccess *inCache = loadFromCache(ret->_Id, true);
if (inCache != NULL)
{
container.push_back(CFolderAccessPtr(inCache, filename, lineNum));
// no more needed
delete ret;
}
else
{
ret->setPersistentState(NOPE::os_clean);
container.push_back(CFolderAccessPtr(ret, filename, lineNum));
}
}
return true;
}
void CScenarioPtr::linkPtr()
{
nlassert(_NextPtr == NULL);
nlassert(_PrevPtr == NULL);
if (_Ptr != NULL)
{
_NextPtr = _Ptr->getFirstPtr();
if (_NextPtr != NULL)
{
_PrevPtr = _NextPtr->_PrevPtr;
_PrevPtr->_NextPtr = this;
_NextPtr->_PrevPtr = this;
}
else
{
_NextPtr = this;
_PrevPtr = this;
_Ptr->setFirstPtr(this);
}
}
}
void CScenarioPtr::unlinkPtr()
{
if (_NextPtr == NULL)
{
nlassert(_PrevPtr == NULL);
return;
}
if (_Ptr != NULL)
{
if (_NextPtr == this)
{
nlassert(_PrevPtr == this);
// last pointer !
_Ptr->setFirstPtr(NULL);
}
else
{
if (_Ptr->getFirstPtr() == this)
{
// the first ptr is the current one, we need to switch to next one
_Ptr->setFirstPtr(_NextPtr);
}
}
}
if (_NextPtr != this)
{
nlassert(_PrevPtr != this);
_NextPtr->_PrevPtr = _PrevPtr;
_PrevPtr->_NextPtr = _NextPtr;
}
_NextPtr = NULL;
_PrevPtr = NULL;
}
CScenario::TObjectCache CScenario::_ObjectCache;
CScenario::TReleasedObject CScenario::_ReleasedObject;
// Destructor, delete any children
CScenario::~CScenario()
{
// release childs reference
if (_SessionLogs != NULL)
delete _SessionLogs;
if (_PlayerRatings != NULL)
delete _PlayerRatings;
if (_PtrList != NULL)
{
nlwarning("ERROR : someone try to delete this object, but there are still ptr on it !");
CScenarioPtr *ptr = _PtrList;
do
{
nlwarning(" Pointer created from '%s', line %u", ptr->_FileName, ptr->_LineNum);
ptr = _PtrList->getNextPtr();
} while(ptr != _PtrList);
nlstop;
}
// remove object from cache map
if (_Id != NOPE::INVALID_OBJECT_ID
&& _ObjectState != NOPE::os_removed
&& _ObjectState != NOPE::os_transient)
{
nldebug("NOPE: clearing CScenario @%p from cache with id %u", this, static_cast(_Id));
nlverify(_ObjectCache.erase(_Id) == 1);
}
else if (_ObjectState != NOPE::os_transient)
{
nlassert(_ObjectCache.find(_Id) == _ObjectCache.end());
}
if (_ObjectState == NOPE::os_released)
{
removeFromReleased();
}
else
{
TReleasedObject::iterator it(_ReleasedObject.find(_ReleaseDate));
if (it != _ReleasedObject.end())
{
nlassert(it->second.find(this) == it->second.end());
}
}
}
void CScenario::removeFromReleased()
{
TReleasedObject::iterator it(_ReleasedObject.find(_ReleaseDate));
nlassert(it != _ReleasedObject.end());
TObjectSet &os = it->second;
nlverify(os.erase(this) == 1);
// nb : _ReleasedObject time entry are removed by the cache update
}
bool CScenario::create(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_transient);
std::string qs;
qs = "INSERT INTO scenario (";
qs += "md5, title, description, author, rrp_total, anim_mode, language, orientation, level, allow_free_trial";
qs += ") VALUES (";
qs += "'"+MSW::escapeString(_MD5.toString(), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_Title), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_Description), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_Author), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_RRPTotal), connection)+"'";
qs += ", ";
qs += "'"+_AnimMode.toString()+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_Language), connection)+"'";
qs += ", ";
qs += "'"+_Orientation.toString()+"'";
qs += ", ";
qs += "'"+_Level.toString()+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_AllowFreeTrial), connection)+"'";
qs += ")";
if (connection.query(qs))
{
uint32 _id_ = connection.getLastGeneratedId();
setObjectId(_id_);
setPersistentState(NOPE::os_clean);
// update the parent class instance in cache if any
return true;
}
return false;
}
bool CScenario::update(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_dirty || getPersistentState() == NOPE::os_clean);
if (getPersistentState() == NOPE::os_clean)
// the object is clean, just ignore the save
return true;
std::string qs;
qs = "UPDATE scenario SET ";
qs += "md5 = '"+MSW::escapeString(_MD5.toString(), connection)+"'";
qs += ", ";
qs += "title = '"+MSW::escapeString(NLMISC::toString(_Title), connection)+"'";
qs += ", ";
qs += "description = '"+MSW::escapeString(NLMISC::toString(_Description), connection)+"'";
qs += ", ";
qs += "author = '"+MSW::escapeString(NLMISC::toString(_Author), connection)+"'";
qs += ", ";
qs += "rrp_total = '"+MSW::escapeString(NLMISC::toString(_RRPTotal), connection)+"'";
qs += ", ";
qs += "anim_mode = '"+_AnimMode.toString()+"'";
qs += ", ";
qs += "language = '"+MSW::escapeString(NLMISC::toString(_Language), connection)+"'";
qs += ", ";
qs += "orientation = '"+_Orientation.toString()+"'";
qs += ", ";
qs += "level = '"+_Level.toString()+"'";
qs += ", ";
qs += "allow_free_trial = '"+MSW::escapeString(NLMISC::toString(_AllowFreeTrial), connection)+"'";
qs += " WHERE id = '"+NLMISC::toString(_Id)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
setPersistentState(NOPE::os_clean);
return true;
}
}
return false;
}
bool CScenario::remove(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_dirty || getPersistentState() == NOPE::os_clean);
std::string qs;
qs = "DELETE FROM scenario ";
qs += " WHERE id = '"+NLMISC::toString(_Id)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
{
// cascading deletion for vector child SessionLogs
nlassert(loadSessionLogs(connection, __FILE__, __LINE__));
const std::vector < CSessionLogPtr > & childs = getSessionLogs();
while (!childs.empty())
{
getSessionLogsByIndex((uint32)childs.size()-1)->remove(connection);
}
}
{
// cascading deletion for vector child PlayerRatings
nlassert(loadPlayerRatings(connection, __FILE__, __LINE__));
const std::vector < CPlayerRatingPtr > & childs = getPlayerRatings();
while (!childs.empty())
{
getPlayerRatingsByIndex((uint32)childs.size()-1)->remove(connection);
}
}
// change the persistant state to 'removed'.
setPersistentState(NOPE::os_removed);
// need to remove ref from parent class container (if any)
// need to remove ref from parent (if any)
return true;
}
}
return false;
}
bool CScenario::removeById(MSW::CConnection &connection, uint32 id)
{
CScenario *object = loadFromCache(id, true);
if (object != NULL)
{
return object->remove(connection);
}
// not in cache, run a SQL query
std::string qs;
qs = "DELETE FROM scenario ";
qs += " WHERE id = '"+NLMISC::toString(id)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
// ok, the row is removed
return true;
}
}
return false;
}
// Try to load the specified object from the memory cache, return NULL if the object is not in the cache
CScenario *CScenario::loadFromCache(uint32 objectId, bool unrelease)
{
// look in the cache
TObjectCache::iterator it(_ObjectCache.find(objectId));
if (it == _ObjectCache.end())
{
// not found, return null
return NULL;
}
else
{
CScenario *object = it->second;
if (object->_ObjectState == NOPE::os_released)
{
if (unrelease)
{
// we need to remove this object from the released object set.
object->removeFromReleased();
object->_ObjectState = NOPE::os_clean;
}
}
return it->second;
}
}
// Receive and execute command from the cache manager.
uint32 CScenario::cacheCmd(NOPE::TCacheCmd cmd)
{
if (cmd == NOPE::cc_update)
{
updateCache();
}
else if (cmd == NOPE::cc_clear)
{
clearCache();
}
else if (cmd == NOPE::cc_dump)
{
dump();
}
else if (cmd == NOPE::cc_instance_count)
{
return (uint32)_ObjectCache.size();
}
// default return value
return 0;
}
void CScenario::dump()
{
nlinfo(" Cache info for class CScenario :");
nlinfo(" There are %u object instances in cache", _ObjectCache.size());
// count the number of object in the released object set
uint32 nbReleased = 0;
TReleasedObject::iterator first(_ReleasedObject.begin()), last(_ReleasedObject.end());
for (; first != last; ++first)
{
nbReleased += (uint32)first->second.size();
}
nlinfo(" There are %u object instances in cache not referenced (waiting deletion or re-use))", nbReleased);
}
void CScenario::updateCache()
{
if (_ReleasedObject.empty())
return;
// 30 s hold in cache
const time_t MAX_CACHE_OLD_TIME = 30;
time_t now = NLMISC::CTime::getSecondsSince1970();
// look for object set older than MAX_CACHE_OLD_TIME and delete them
while (!_ReleasedObject.empty() && _ReleasedObject.begin()->first < now-MAX_CACHE_OLD_TIME)
{
TObjectSet &delSet = _ReleasedObject.begin()->second;
// unload this objects
while (!delSet.empty())
{
CScenario *object = *delSet.begin();
delete object;
}
_ReleasedObject.erase(_ReleasedObject.begin());
}
}
void CScenario::clearCache()
{
// remove any unreferenced object from the cache
while (!_ReleasedObject.empty())
{
TObjectSet &delSet = _ReleasedObject.begin()->second;
// unload this objects
while (!delSet.empty())
{
CScenario *object = *delSet.begin();
delete object;
}
_ReleasedObject.erase(_ReleasedObject.begin());
}
}
void CScenario::registerUpdatable()
{
static bool registered = false;
if (!registered)
{
NOPE::CPersistentCache::getInstance().registerCache(&CScenario::cacheCmd);
registered = true;
}
}
// set the pointer on the first pointer of the pointer list (set to null when there is no more pointer)
void CScenario::setFirstPtr(CScenarioPtr *ptr)
{
_PtrList = ptr;
if (ptr == NULL)
{
// this is the last pointer !
if (_ObjectState == NOPE::os_transient
|| _ObjectState == NOPE::os_removed)
{
// not a persistent object, or removed object, just delet it
delete this;
}
else if (_ObjectState != NOPE::os_removed)
{
setPersistentState(NOPE::os_released);
}
}
}
// Set the persistent state of the object and do some house keeping
void CScenario::setPersistentState(NOPE::TObjectState state)
{
nlassert(NOPE::AllowedTransition[_ObjectState][state] == true);
if(_ObjectState == NOPE::os_released && state == NOPE::os_removed)
{
// a release object gets removed (e.g. by remove by id)
// delete the object
delete this;
// no more to do
return;
}
if (_ObjectState == NOPE::os_transient && state != NOPE::os_transient)
{
nldebug("NOPE: inserting CScenario @%p in cache with id %u", this, static_cast(_Id));
nlverify(_ObjectCache.insert(std::make_pair(_Id, this)).second);
}
if (_ObjectState != NOPE::os_transient)
nlassert(_ObjectCache.find(_Id) != _ObjectCache.end());
_ObjectState = state;
if (state == NOPE::os_released)
{
_ReleaseDate = NLMISC::CTime::getSecondsSince1970();
nlverify(_ReleasedObject[_ReleaseDate].insert(this).second);
}
else if (state == NOPE::os_removed)
{
nldebug("NOPE: erasing CScenario @%p in cache with id %u", this, static_cast(_Id));
nlverify(_ObjectCache.erase(_Id) == 1);
}
}
CScenarioPtr CScenario::load(MSW::CConnection &connection, uint32 id, const char *filename, uint32 lineNum)
{
CScenario *inCache = loadFromCache(id, true);
if (inCache != NULL)
{
return CScenarioPtr(inCache, filename, lineNum);
}
std::string qs;
qs = "SELECT ";
qs += "id, md5, title, description, author, rrp_total, anim_mode, language, orientation, level, allow_free_trial";
qs += " FROM scenario";
qs += " WHERE id = '"+NLMISC::toString(id)+"'";
CScenarioPtr ret;
if (!connection.query(qs))
{
return ret;
}
MSW::CStoreResult *result = connection.storeResult().release();
nlassert(result->getNumRows() <= 1);
if (result->getNumRows() == 1)
{
ret.assign(new CScenario, filename, lineNum);
// ok, we have an object
result->fetchRow();
result->getField(0, ret->_Id);
result->getMD5Field(1, ret->_MD5);
result->getField(2, ret->_Title);
result->getField(3, ret->_Description);
result->getField(4, ret->_Author);
result->getField(5, ret->_RRPTotal);
{
std::string s;
result->getField(6, s);
ret->_AnimMode = TAnimMode(s);
}
result->getField(7, ret->_Language);
{
std::string s;
result->getField(8, s);
ret->_Orientation = TSessionOrientation(s);
}
{
std::string s;
result->getField(9, s);
ret->_Level = R2::TSessionLevel(s);
}
result->getField(10, ret->_AllowFreeTrial);
ret->setPersistentState(NOPE::os_clean);
}
delete result;
return ret;
}
bool CScenario::loadSessionLogs(MSW::CConnection &connection, const char *filename, uint32 lineNum)
{
bool ret = true;
if (_SessionLogs != NULL)
{
// the children are already loaded, just return true
return true;
}
// allocate the container
_SessionLogs = new std::vector < CSessionLogPtr >;
// load the childs
ret &= CSessionLog::loadChildrenOfCScenario(connection, getObjectId(), *_SessionLogs, filename, lineNum);
return ret;
}
const std::vector &CScenario::getSessionLogs() const
{
nlassert(_SessionLogs != NULL);
return *_SessionLogs;
}
CSessionLogPtr &CScenario::getSessionLogsByIndex(uint32 index) const
{
nlassert(_SessionLogs != NULL);
nlassert(index < _SessionLogs->size());
return const_cast< CSessionLogPtr & >(_SessionLogs->operator[](index));
}
CSessionLogPtr &CScenario::getSessionLogsById(uint32 id) const
{
nlassert(_SessionLogs != NULL);
std::vector::const_iterator first(_SessionLogs->begin()), last(_SessionLogs->end());
for (; first != last; ++first)
{
const CSessionLogPtr &child = *first;
if (child->getObjectId() == id)
{
return const_cast< CSessionLogPtr & >(child);
}
}
// no object with this id, return a null pointer
static CSessionLogPtr nil;
return nil;
}
bool CScenario::loadPlayerRatings(MSW::CConnection &connection, const char *filename, uint32 lineNum)
{
bool ret = true;
if (_PlayerRatings != NULL)
{
// the children are already loaded, just return true
return true;
}
// allocate the container
_PlayerRatings = new std::vector < CPlayerRatingPtr >;
// load the childs
ret &= CPlayerRating::loadChildrenOfCScenario(connection, getObjectId(), *_PlayerRatings, filename, lineNum);
return ret;
}
const std::vector &CScenario::getPlayerRatings() const
{
nlassert(_PlayerRatings != NULL);
return *_PlayerRatings;
}
CPlayerRatingPtr &CScenario::getPlayerRatingsByIndex(uint32 index) const
{
nlassert(_PlayerRatings != NULL);
nlassert(index < _PlayerRatings->size());
return const_cast< CPlayerRatingPtr & >(_PlayerRatings->operator[](index));
}
CPlayerRatingPtr &CScenario::getPlayerRatingsById(uint32 id) const
{
nlassert(_PlayerRatings != NULL);
std::vector::const_iterator first(_PlayerRatings->begin()), last(_PlayerRatings->end());
for (; first != last; ++first)
{
const CPlayerRatingPtr &child = *first;
if (child->getObjectId() == id)
{
return const_cast< CPlayerRatingPtr & >(child);
}
}
// no object with this id, return a null pointer
static CPlayerRatingPtr nil;
return nil;
}
void CSessionLogPtr::linkPtr()
{
nlassert(_NextPtr == NULL);
nlassert(_PrevPtr == NULL);
if (_Ptr != NULL)
{
_NextPtr = _Ptr->getFirstPtr();
if (_NextPtr != NULL)
{
_PrevPtr = _NextPtr->_PrevPtr;
_PrevPtr->_NextPtr = this;
_NextPtr->_PrevPtr = this;
}
else
{
_NextPtr = this;
_PrevPtr = this;
_Ptr->setFirstPtr(this);
}
}
}
void CSessionLogPtr::unlinkPtr()
{
if (_NextPtr == NULL)
{
nlassert(_PrevPtr == NULL);
return;
}
if (_Ptr != NULL)
{
if (_NextPtr == this)
{
nlassert(_PrevPtr == this);
// last pointer !
_Ptr->setFirstPtr(NULL);
}
else
{
if (_Ptr->getFirstPtr() == this)
{
// the first ptr is the current one, we need to switch to next one
_Ptr->setFirstPtr(_NextPtr);
}
}
}
if (_NextPtr != this)
{
nlassert(_PrevPtr != this);
_NextPtr->_PrevPtr = _PrevPtr;
_PrevPtr->_NextPtr = _NextPtr;
}
_NextPtr = NULL;
_PrevPtr = NULL;
}
CSessionLog::TObjectCache CSessionLog::_ObjectCache;
CSessionLog::TReleasedObject CSessionLog::_ReleasedObject;
// Destructor, delete any children
CSessionLog::~CSessionLog()
{
// release childs reference
if (_PtrList != NULL)
{
nlwarning("ERROR : someone try to delete this object, but there are still ptr on it !");
CSessionLogPtr *ptr = _PtrList;
do
{
nlwarning(" Pointer created from '%s', line %u", ptr->_FileName, ptr->_LineNum);
ptr = _PtrList->getNextPtr();
} while(ptr != _PtrList);
nlstop;
}
// remove object from cache map
if (_Id != NOPE::INVALID_OBJECT_ID
&& _ObjectState != NOPE::os_removed
&& _ObjectState != NOPE::os_transient)
{
nldebug("NOPE: clearing CSessionLog @%p from cache with id %u", this, static_cast(_Id));
nlverify(_ObjectCache.erase(_Id) == 1);
}
else if (_ObjectState != NOPE::os_transient)
{
nlassert(_ObjectCache.find(_Id) == _ObjectCache.end());
}
if (_ObjectState == NOPE::os_released)
{
removeFromReleased();
}
else
{
TReleasedObject::iterator it(_ReleasedObject.find(_ReleaseDate));
if (it != _ReleasedObject.end())
{
nlassert(it->second.find(this) == it->second.end());
}
}
}
void CSessionLog::removeFromReleased()
{
TReleasedObject::iterator it(_ReleasedObject.find(_ReleaseDate));
nlassert(it != _ReleasedObject.end());
TObjectSet &os = it->second;
nlverify(os.erase(this) == 1);
// nb : _ReleasedObject time entry are removed by the cache update
}
bool CSessionLog::create(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_transient);
nlassert(_Id != 0);
std::string qs;
qs = "INSERT INTO session_log (";
qs += "id, scenario_id, rrp_scored, scenario_point_scored, time_taken, participants, launch_date, owner, guild_name";
qs += ") VALUES (";
qs += "'"+MSW::escapeString(NLMISC::toString(_Id), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_ScenarioId), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_RRPScored), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_ScenarioPointScored), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_TimeTaken), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_Participants), connection)+"'";
qs += ", ";
qs += "'"+MSW::encodeDate(_LaunchDate)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_Owner), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_GuildName), connection)+"'";
qs += ")";
if (connection.query(qs))
{
setPersistentState(NOPE::os_clean);
// update the parent class instance in cache if any
if (_ScenarioId != 0)
{
// need to update the parent class child list if it is in the cache
CScenario *parent = CScenario::loadFromCache(_ScenarioId, false);
if (parent && parent->_SessionLogs != NULL)
{
nlassert(std::find(parent->_SessionLogs->begin(), parent->_SessionLogs->end(), CSessionLogPtr(this, __FILE__, __LINE__)) == parent->_SessionLogs->end());
parent->_SessionLogs->push_back(CSessionLogPtr(this, __FILE__, __LINE__));
}
}
return true;
}
return false;
}
bool CSessionLog::update(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_dirty || getPersistentState() == NOPE::os_clean);
if (getPersistentState() == NOPE::os_clean)
// the object is clean, just ignore the save
return true;
std::string qs;
qs = "UPDATE session_log SET ";
qs += "id = '"+MSW::escapeString(NLMISC::toString(_Id), connection)+"'";
qs += ", ";
qs += "scenario_id = '"+MSW::escapeString(NLMISC::toString(_ScenarioId), connection)+"'";
qs += ", ";
qs += "rrp_scored = '"+MSW::escapeString(NLMISC::toString(_RRPScored), connection)+"'";
qs += ", ";
qs += "scenario_point_scored = '"+MSW::escapeString(NLMISC::toString(_ScenarioPointScored), connection)+"'";
qs += ", ";
qs += "time_taken = '"+MSW::escapeString(NLMISC::toString(_TimeTaken), connection)+"'";
qs += ", ";
qs += "participants = '"+MSW::escapeString(NLMISC::toString(_Participants), connection)+"'";
qs += ", ";
qs += "launch_date = '"+MSW::encodeDate(_LaunchDate)+"'";
qs += ", ";
qs += "owner = '"+MSW::escapeString(NLMISC::toString(_Owner), connection)+"'";
qs += ", ";
qs += "guild_name = '"+MSW::escapeString(NLMISC::toString(_GuildName), connection)+"'";
qs += " WHERE id = '"+NLMISC::toString(_Id)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
setPersistentState(NOPE::os_clean);
return true;
}
}
return false;
}
bool CSessionLog::remove(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_dirty || getPersistentState() == NOPE::os_clean);
std::string qs;
qs = "DELETE FROM session_log ";
qs += " WHERE id = '"+NLMISC::toString(_Id)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
// change the persistant state to 'removed'.
setPersistentState(NOPE::os_removed);
// need to remove ref from parent class container (if any)
{
CScenarioPtr parent(CScenario::loadFromCache(_ScenarioId, true), __FILE__, __LINE__);
if (parent != NULL && parent->_SessionLogs != NULL)
{
std::vector < CSessionLogPtr >::iterator it = std::find(parent->_SessionLogs->begin(), parent->_SessionLogs->end(), this);
if (it != parent->_SessionLogs->end())
{
parent->_SessionLogs->erase(it);
}
}
}
// need to remove ref from parent (if any)
return true;
}
}
return false;
}
bool CSessionLog::removeById(MSW::CConnection &connection, uint32 id)
{
CSessionLog *object = loadFromCache(id, true);
if (object != NULL)
{
return object->remove(connection);
}
// not in cache, run a SQL query
std::string qs;
qs = "DELETE FROM session_log ";
qs += " WHERE id = '"+NLMISC::toString(id)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
// ok, the row is removed
return true;
}
}
return false;
}
// Try to load the specified object from the memory cache, return NULL if the object is not in the cache
CSessionLog *CSessionLog::loadFromCache(uint32 objectId, bool unrelease)
{
// look in the cache
TObjectCache::iterator it(_ObjectCache.find(objectId));
if (it == _ObjectCache.end())
{
// not found, return null
return NULL;
}
else
{
CSessionLog *object = it->second;
if (object->_ObjectState == NOPE::os_released)
{
if (unrelease)
{
// we need to remove this object from the released object set.
object->removeFromReleased();
object->_ObjectState = NOPE::os_clean;
}
}
return it->second;
}
}
// Receive and execute command from the cache manager.
uint32 CSessionLog::cacheCmd(NOPE::TCacheCmd cmd)
{
if (cmd == NOPE::cc_update)
{
updateCache();
}
else if (cmd == NOPE::cc_clear)
{
clearCache();
}
else if (cmd == NOPE::cc_dump)
{
dump();
}
else if (cmd == NOPE::cc_instance_count)
{
return (uint32)_ObjectCache.size();
}
// default return value
return 0;
}
void CSessionLog::dump()
{
nlinfo(" Cache info for class CSessionLog :");
nlinfo(" There are %u object instances in cache", _ObjectCache.size());
// count the number of object in the released object set
uint32 nbReleased = 0;
TReleasedObject::iterator first(_ReleasedObject.begin()), last(_ReleasedObject.end());
for (; first != last; ++first)
{
nbReleased += (uint32)first->second.size();
}
nlinfo(" There are %u object instances in cache not referenced (waiting deletion or re-use))", nbReleased);
}
void CSessionLog::updateCache()
{
if (_ReleasedObject.empty())
return;
// 30 s hold in cache
const time_t MAX_CACHE_OLD_TIME = 30;
time_t now = NLMISC::CTime::getSecondsSince1970();
// look for object set older than MAX_CACHE_OLD_TIME and delete them
while (!_ReleasedObject.empty() && _ReleasedObject.begin()->first < now-MAX_CACHE_OLD_TIME)
{
TObjectSet &delSet = _ReleasedObject.begin()->second;
// unload this objects
while (!delSet.empty())
{
CSessionLog *object = *delSet.begin();
delete object;
}
_ReleasedObject.erase(_ReleasedObject.begin());
}
}
void CSessionLog::clearCache()
{
// remove any unreferenced object from the cache
while (!_ReleasedObject.empty())
{
TObjectSet &delSet = _ReleasedObject.begin()->second;
// unload this objects
while (!delSet.empty())
{
CSessionLog *object = *delSet.begin();
delete object;
}
_ReleasedObject.erase(_ReleasedObject.begin());
}
}
void CSessionLog::registerUpdatable()
{
static bool registered = false;
if (!registered)
{
NOPE::CPersistentCache::getInstance().registerCache(&CSessionLog::cacheCmd);
registered = true;
}
}
// set the pointer on the first pointer of the pointer list (set to null when there is no more pointer)
void CSessionLog::setFirstPtr(CSessionLogPtr *ptr)
{
_PtrList = ptr;
if (ptr == NULL)
{
// this is the last pointer !
if (_ObjectState == NOPE::os_transient
|| _ObjectState == NOPE::os_removed)
{
// not a persistent object, or removed object, just delet it
delete this;
}
else if (_ObjectState != NOPE::os_removed)
{
setPersistentState(NOPE::os_released);
}
}
}
// Set the persistent state of the object and do some house keeping
void CSessionLog::setPersistentState(NOPE::TObjectState state)
{
nlassert(NOPE::AllowedTransition[_ObjectState][state] == true);
if(_ObjectState == NOPE::os_released && state == NOPE::os_removed)
{
// a release object gets removed (e.g. by remove by id)
// delete the object
delete this;
// no more to do
return;
}
if (_ObjectState == NOPE::os_transient && state != NOPE::os_transient)
{
nldebug("NOPE: inserting CSessionLog @%p in cache with id %u", this, static_cast(_Id));
nlverify(_ObjectCache.insert(std::make_pair(_Id, this)).second);
}
if (_ObjectState != NOPE::os_transient)
nlassert(_ObjectCache.find(_Id) != _ObjectCache.end());
_ObjectState = state;
if (state == NOPE::os_released)
{
_ReleaseDate = NLMISC::CTime::getSecondsSince1970();
nlverify(_ReleasedObject[_ReleaseDate].insert(this).second);
}
else if (state == NOPE::os_removed)
{
nldebug("NOPE: erasing CSessionLog @%p in cache with id %u", this, static_cast(_Id));
nlverify(_ObjectCache.erase(_Id) == 1);
}
}
CSessionLogPtr CSessionLog::load(MSW::CConnection &connection, uint32 id, const char *filename, uint32 lineNum)
{
CSessionLog *inCache = loadFromCache(id, true);
if (inCache != NULL)
{
return CSessionLogPtr(inCache, filename, lineNum);
}
std::string qs;
qs = "SELECT ";
qs += "id, scenario_id, rrp_scored, scenario_point_scored, time_taken, participants, launch_date, owner, guild_name";
qs += " FROM session_log";
qs += " WHERE id = '"+NLMISC::toString(id)+"'";
CSessionLogPtr ret;
if (!connection.query(qs))
{
return ret;
}
MSW::CStoreResult *result = connection.storeResult().release();
nlassert(result->getNumRows() <= 1);
if (result->getNumRows() == 1)
{
ret.assign(new CSessionLog, filename, lineNum);
// ok, we have an object
result->fetchRow();
result->getField(0, ret->_Id);
result->getField(1, ret->_ScenarioId);
result->getField(2, ret->_RRPScored);
result->getField(3, ret->_ScenarioPointScored);
result->getField(4, ret->_TimeTaken);
result->getField(5, ret->_Participants);
result->getDateField(6, ret->_LaunchDate);
result->getField(7, ret->_Owner);
result->getField(8, ret->_GuildName);
ret->setPersistentState(NOPE::os_clean);
}
delete result;
return ret;
}
bool CSessionLog::loadChildrenOfCScenario(MSW::CConnection &connection, uint32 parentId, std::vector < CSessionLogPtr > & container, const char *filename, uint32 lineNum)
{
std::string qs;
qs = "SELECT ";
qs += "id, scenario_id, rrp_scored, scenario_point_scored, time_taken, participants, launch_date, owner, guild_name";
qs += " FROM session_log";
qs += " WHERE scenario_id = '"+NLMISC::toString(parentId)+"'";
if (!connection.query(qs))
{
return false;
}
std::unique_ptr result(connection.storeResult());
for (uint i=0; igetNumRows(); ++i)
{
CSessionLog *ret = new CSessionLog();
// ok, we have an object
result->fetchRow();
result->getField(0, ret->_Id);
result->getField(1, ret->_ScenarioId);
result->getField(2, ret->_RRPScored);
result->getField(3, ret->_ScenarioPointScored);
result->getField(4, ret->_TimeTaken);
result->getField(5, ret->_Participants);
result->getDateField(6, ret->_LaunchDate);
result->getField(7, ret->_Owner);
result->getField(8, ret->_GuildName);
CSessionLog *inCache = loadFromCache(ret->_Id, true);
if (inCache != NULL)
{
container.push_back(CSessionLogPtr(inCache, filename, lineNum));
// no more needed
delete ret;
}
else
{
ret->setPersistentState(NOPE::os_clean);
container.push_back(CSessionLogPtr(ret, filename, lineNum));
}
}
return true;
}
void CGmStatusPtr::linkPtr()
{
nlassert(_NextPtr == NULL);
nlassert(_PrevPtr == NULL);
if (_Ptr != NULL)
{
_NextPtr = _Ptr->getFirstPtr();
if (_NextPtr != NULL)
{
_PrevPtr = _NextPtr->_PrevPtr;
_PrevPtr->_NextPtr = this;
_NextPtr->_PrevPtr = this;
}
else
{
_NextPtr = this;
_PrevPtr = this;
_Ptr->setFirstPtr(this);
}
}
}
void CGmStatusPtr::unlinkPtr()
{
if (_NextPtr == NULL)
{
nlassert(_PrevPtr == NULL);
return;
}
if (_Ptr != NULL)
{
if (_NextPtr == this)
{
nlassert(_PrevPtr == this);
// last pointer !
_Ptr->setFirstPtr(NULL);
}
else
{
if (_Ptr->getFirstPtr() == this)
{
// the first ptr is the current one, we need to switch to next one
_Ptr->setFirstPtr(_NextPtr);
}
}
}
if (_NextPtr != this)
{
nlassert(_PrevPtr != this);
_NextPtr->_PrevPtr = _PrevPtr;
_PrevPtr->_NextPtr = _NextPtr;
}
_NextPtr = NULL;
_PrevPtr = NULL;
}
CGmStatus::TObjectCache CGmStatus::_ObjectCache;
CGmStatus::TReleasedObject CGmStatus::_ReleasedObject;
// Destructor, delete any children
CGmStatus::~CGmStatus()
{
// release childs reference
if (_PtrList != NULL)
{
nlwarning("ERROR : someone try to delete this object, but there are still ptr on it !");
CGmStatusPtr *ptr = _PtrList;
do
{
nlwarning(" Pointer created from '%s', line %u", ptr->_FileName, ptr->_LineNum);
ptr = _PtrList->getNextPtr();
} while(ptr != _PtrList);
nlstop;
}
// remove object from cache map
if (_UserId != NOPE::INVALID_OBJECT_ID
&& _ObjectState != NOPE::os_removed
&& _ObjectState != NOPE::os_transient)
{
nldebug("NOPE: clearing CGmStatus @%p from cache with id %u", this, static_cast(_UserId));
nlverify(_ObjectCache.erase(_UserId) == 1);
}
else if (_ObjectState != NOPE::os_transient)
{
nlassert(_ObjectCache.find(_UserId) == _ObjectCache.end());
}
if (_ObjectState == NOPE::os_released)
{
removeFromReleased();
}
else
{
TReleasedObject::iterator it(_ReleasedObject.find(_ReleaseDate));
if (it != _ReleasedObject.end())
{
nlassert(it->second.find(this) == it->second.end());
}
}
}
void CGmStatus::removeFromReleased()
{
TReleasedObject::iterator it(_ReleasedObject.find(_ReleaseDate));
nlassert(it != _ReleasedObject.end());
TObjectSet &os = it->second;
nlverify(os.erase(this) == 1);
// nb : _ReleasedObject time entry are removed by the cache update
}
bool CGmStatus::create(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_transient);
nlassert(_UserId != 0);
std::string qs;
qs = "INSERT INTO gm_status (";
qs += "user_id, available";
qs += ") VALUES (";
qs += "'"+MSW::escapeString(NLMISC::toString(_UserId), connection)+"'";
qs += ", ";
qs += "'"+MSW::escapeString(NLMISC::toString(_Available), connection)+"'";
qs += ")";
if (connection.query(qs))
{
setPersistentState(NOPE::os_clean);
// update the parent class instance in cache if any
if (_UserId != 0)
{
// need to update the parent class child if it is in the cache
CRingUser *parent = CRingUser::loadFromCache(_UserId, false);
if (parent && parent->_GMStatusLoaded)
{
nlassert(parent->_GMStatus == NULL);
parent->_GMStatus = CGmStatusPtr(this, __FILE__, __LINE__);
}
}
return true;
}
return false;
}
bool CGmStatus::update(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_dirty || getPersistentState() == NOPE::os_clean);
if (getPersistentState() == NOPE::os_clean)
// the object is clean, just ignore the save
return true;
std::string qs;
qs = "UPDATE gm_status SET ";
qs += "user_id = '"+MSW::escapeString(NLMISC::toString(_UserId), connection)+"'";
qs += ", ";
qs += "available = '"+MSW::escapeString(NLMISC::toString(_Available), connection)+"'";
qs += " WHERE user_id = '"+NLMISC::toString(_UserId)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
setPersistentState(NOPE::os_clean);
return true;
}
}
return false;
}
bool CGmStatus::remove(MSW::CConnection &connection)
{
nlassert(getPersistentState() == NOPE::os_dirty || getPersistentState() == NOPE::os_clean);
std::string qs;
qs = "DELETE FROM gm_status ";
qs += " WHERE user_id = '"+NLMISC::toString(_UserId)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
// change the persistant state to 'removed'.
setPersistentState(NOPE::os_removed);
// need to remove ref from parent class container (if any)
// need to remove ref from parent (if any)
{
CRingUserPtr parent(CRingUser::loadFromCache(_UserId, true), __FILE__, __LINE__);
if (parent != NULL && parent->_GMStatusLoaded)
{
// assign a new NULL pointer
parent->_GMStatus.assign(CGmStatusPtr(), __FILE__, __LINE__);
}
}
return true;
}
}
return false;
}
bool CGmStatus::removeById(MSW::CConnection &connection, uint32 id)
{
CGmStatus *object = loadFromCache(id, true);
if (object != NULL)
{
return object->remove(connection);
}
// not in cache, run a SQL query
std::string qs;
qs = "DELETE FROM gm_status ";
qs += " WHERE user_id = '"+NLMISC::toString(id)+"'";
if (connection.query(qs))
{
if (connection.getAffectedRows() == 1)
{
// ok, the row is removed
return true;
}
}
return false;
}
// Try to load the specified object from the memory cache, return NULL if the object is not in the cache
CGmStatus *CGmStatus::loadFromCache(uint32 objectId, bool unrelease)
{
// look in the cache
TObjectCache::iterator it(_ObjectCache.find(objectId));
if (it == _ObjectCache.end())
{
// not found, return null
return NULL;
}
else
{
CGmStatus *object = it->second;
if (object->_ObjectState == NOPE::os_released)
{
if (unrelease)
{
// we need to remove this object from the released object set.
object->removeFromReleased();
object->_ObjectState = NOPE::os_clean;
}
}
return it->second;
}
}
// Receive and execute command from the cache manager.
uint32 CGmStatus::cacheCmd(NOPE::TCacheCmd cmd)
{
if (cmd == NOPE::cc_update)
{
updateCache();
}
else if (cmd == NOPE::cc_clear)
{
clearCache();
}
else if (cmd == NOPE::cc_dump)
{
dump();
}
else if (cmd == NOPE::cc_instance_count)
{
return (uint32)_ObjectCache.size();
}
// default return value
return 0;
}
void CGmStatus::dump()
{
nlinfo(" Cache info for class CGmStatus :");
nlinfo(" There are %u object instances in cache", _ObjectCache.size());
// count the number of object in the released object set
uint32 nbReleased = 0;
TReleasedObject::iterator first(_ReleasedObject.begin()), last(_ReleasedObject.end());
for (; first != last; ++first)
{
nbReleased += (uint32)first->second.size();
}
nlinfo(" There are %u object instances in cache not referenced (waiting deletion or re-use))", nbReleased);
}
void CGmStatus::updateCache()
{
if (_ReleasedObject.empty())
return;
// 30 s hold in cache
const time_t MAX_CACHE_OLD_TIME = 30;
time_t now = NLMISC::CTime::getSecondsSince1970();
// look for object set older than MAX_CACHE_OLD_TIME and delete them
while (!_ReleasedObject.empty() && _ReleasedObject.begin()->first < now-MAX_CACHE_OLD_TIME)
{
TObjectSet &delSet = _ReleasedObject.begin()->second;
// unload this objects
while (!delSet.empty())
{
CGmStatus *object = *delSet.begin();
delete object;
}
_ReleasedObject.erase(_ReleasedObject.begin());
}
}
void CGmStatus::clearCache()
{
// remove any unreferenced object from the cache
while (!_ReleasedObject.empty())
{
TObjectSet &delSet = _ReleasedObject.begin()->second;
// unload this objects
while (!delSet.empty())
{
CGmStatus *object = *delSet.begin();
delete object;
}
_ReleasedObject.erase(_ReleasedObject.begin());
}
}
void CGmStatus::registerUpdatable()
{
static bool registered = false;
if (!registered)
{
NOPE::CPersistentCache::getInstance().registerCache(&CGmStatus::cacheCmd);
registered = true;
}
}
// set the pointer on the first pointer of the pointer list (set to null when there is no more pointer)
void CGmStatus::setFirstPtr(CGmStatusPtr *ptr)
{
_PtrList = ptr;
if (ptr == NULL)
{
// this is the last pointer !
if (_ObjectState == NOPE::os_transient
|| _ObjectState == NOPE::os_removed)
{
// not a persistent object, or removed object, just delet it
delete this;
}
else if (_ObjectState != NOPE::os_removed)
{
setPersistentState(NOPE::os_released);
}
}
}
// Set the persistent state of the object and do some house keeping
void CGmStatus::setPersistentState(NOPE::TObjectState state)
{
nlassert(NOPE::AllowedTransition[_ObjectState][state] == true);
if(_ObjectState == NOPE::os_released && state == NOPE::os_removed)
{
// a release object gets removed (e.g. by remove by id)
// delete the object
delete this;
// no more to do
return;
}
if (_ObjectState == NOPE::os_transient && state != NOPE::os_transient)
{
nldebug("NOPE: inserting CGmStatus @%p in cache with id %u", this, static_cast(_UserId));
nlverify(_ObjectCache.insert(std::make_pair(_UserId, this)).second);
}
if (_ObjectState != NOPE::os_transient)
nlassert(_ObjectCache.find(_UserId) != _ObjectCache.end());
_ObjectState = state;
if (state == NOPE::os_released)
{
_ReleaseDate = NLMISC::CTime::getSecondsSince1970();
nlverify(_ReleasedObject[_ReleaseDate].insert(this).second);
}
else if (state == NOPE::os_removed)
{
nldebug("NOPE: erasing CGmStatus @%p in cache with id %u", this, static_cast(_UserId));
nlverify(_ObjectCache.erase(_UserId) == 1);
}
}
CGmStatusPtr CGmStatus::load(MSW::CConnection &connection, uint32 id, const char *filename, uint32 lineNum)
{
CGmStatus *inCache = loadFromCache(id, true);
if (inCache != NULL)
{
return CGmStatusPtr(inCache, filename, lineNum);
}
std::string qs;
qs = "SELECT ";
qs += "user_id, available";
qs += " FROM gm_status";
qs += " WHERE user_id = '"+NLMISC::toString(id)+"'";
CGmStatusPtr ret;
if (!connection.query(qs))
{
return ret;
}
MSW::CStoreResult *result = connection.storeResult().release();
nlassert(result->getNumRows() <= 1);
if (result->getNumRows() == 1)
{
ret.assign(new CGmStatus, filename, lineNum);
// ok, we have an object
result->fetchRow();
result->getField(0, ret->_UserId);
result->getField(1, ret->_Available);
ret->setPersistentState(NOPE::os_clean);
}
delete result;
return ret;
}
bool CGmStatus::loadChildOfCRingUser(MSW::CConnection &connection, uint32 parentId, CGmStatusPtr &childPtr, const char *filename, uint32 lineNum)
{
std::string qs;
qs = "SELECT ";
qs += "user_id, available";
qs += " FROM gm_status";
qs += " WHERE user_id = '"+NLMISC::toString(parentId)+"'";
CGmStatusPtr ret;
if (!connection.query(qs))
{
childPtr = CGmStatusPtr();
return false;
}
std::unique_ptr result(connection.storeResult());
// check that the data description is consistent with database content
nlassert(result->getNumRows() <= 1);
if (result->getNumRows() == 1)
{
CGmStatus *object = new CGmStatus;
// ok, we have an object
result->fetchRow();
result->getField(0, object->_UserId);
result->getField(1, object->_Available);
CGmStatus *inCache = loadFromCache(object->_UserId, true);
if (inCache != NULL)
{
ret.assign(inCache, filename, lineNum);
// no more needed
delete object;
}
else
{
object->setPersistentState(NOPE::os_clean);
ret.assign(object, filename, lineNum);
}
childPtr = ret;
return true;
}
// no result, but no error
childPtr = CGmStatusPtr();
return true;
}
}