// 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; } }