// 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 . #include "stdpch.h" #include "nel/net/service.h" #include "nel/net/message.h" #include "nel/misc/file.h" #include "nel/misc/path.h" #include "nel/misc/o_xml.h" #include "nel/misc/i_xml.h" #include "nel/misc/algo.h" #include "nel/misc/sstring.h" #include "nel/misc/random.h" #include "game_share/backup_service_interface.h" #include "game_share/utils.h" #include "name_manager.h" #include "database_mapping.h" using namespace std; using namespace NLMISC; using namespace NLNET; using namespace MSW; using namespace CHARSYNC; CVariable NameManagerCheckOnStatup("name_mgr", "NameManagerCheckOnStatup", "If true name manager will do a complete check on character name and rename any invalid one", true, 0, true ); NLMISC::CRandom RandomGenerator; const char *GUILD_NAME_FILE = "guild_names.txt"; //----------------------------------------------------------------------------- bool CNameManager::assignName(uint32 charId, const ucstring & ucName, uint32 homeSessionId, bool skipTest) { // if (eId == NLMISC::CEntityId::Unknown) // return false; const TCharSlot charSlot( charId ); const uint32 playerId = charSlot.UserId; const uint8 charIndex = charSlot.CharIndex; // check that name is usable if (!skipTest && isNameUsable( ucName, playerId, charIndex, homeSessionId ) != TCharacterNameResult::cnr_ok) return false; // assign name and save const string name = toLower( ucName.toUtf8() ); // remove any temporary reserve for this name _TemporaryReservedNames.erase(name); TFullName fullname(name, homeSessionId); const TCharSlot *owner = _Names.getB(fullname); if (owner != NULL && *owner == charSlot) // association already exist, nothing more to do return true; else if (owner != NULL) { // we need to remove this assoc _Names.removeWithA(fullname); _ReleasedNames.insert((owner->UserId<<4)+owner->CharIndex); } if (_Names.getBToAMap().find(charSlot) != _Names.getBToAMap().end()) { // the character is associated to another name _Names.removeWithB(charSlot); } // insert the new association _Names.add(fullname, charSlot); _ChangedNames.insert(charId); // saveCharacterNames(); nlinfo("NAMEMGR: assigned name '%s' to char %u", name.c_str(), charId ); return true; } //----------------------------------------------------------------------------- void CNameManager::liberateName(uint32 charId, const ucstring & ucName) { const TCharSlot charSlot( charId ); const string name = toLower( ucName.toUtf8() ); // remove the name from the reservation if it belong to the specified user { TTempReservedNames::iterator it(_TemporaryReservedNames.find(name)); if (it != _TemporaryReservedNames.end() && it->second.UserId == (charId >> 4)) { _TemporaryReservedNames.erase(it); } } const TFullName *fullname = _Names.getA(charId); if (fullname == NULL || fullname->Name != ucName.toUtf8()) { nlwarning("NAMEMGR: char %u is trying to liberate a name he does not own: '%s'", charId, name.c_str() ); return; } _Names.removeWithB( charSlot ); _ReleasedNames.insert(charId); // saveCharacterNames(); nlinfo("NAMEMGR: char %u liberated his name '%s'", charId, name.c_str() ); } //----------------------------------------------------------------------------- // liberate any name associated to a character void CNameManager::liberateName(uint32 charId) { const TCharSlot charSlot( charId ); TNamesIndex::TBToAMap::const_iterator it = _Names.getBToAMap().find( charSlot ); if ( it == _Names.getBToAMap().end() ) return; nlinfo("NAMEMGR: char %u liberated his name '%s'", charId, it->second.Name.c_str() ); // ok, association found, remove it _Names.removeWithB(charSlot); _ReleasedNames.insert(charId); // saveCharacterNames(); } //----------------------------------------------------------------------------- //void CNameManager::checkCharacterSlot(uint32 charId, const ucstring & ucName) //{ //// if (eId == CEntityId::Unknown) //// return; // // const TCharSlot charSlot( charId ); // const TName name = toLower( ucName.toUtf8() ); // // // verify that name is assigned to the character // { // TNamesIndex::TAToBMap::const_iterator it = _Names.getAToBMap().find( name ); // if ( it != _Names.getAToBMap().end() ) // { // if( it->second != charSlot ) // { // nlwarning("NAMEMGR: char %u has name '%s', but this name is assigned to another character", charId, name.c_str() ); // renameCharacter(charId); // } // } // else // { // nlwarning("NAMEMGR: char %u has name '%s', but this name is not assigned", charId, name.c_str() ); // assignName(charId, ucName); // } // } // // // if this slot is not duplicated, just return // TDuplicatedSlots::iterator it = _DuplicatedSlots.find( charSlot ); // if ( it == _DuplicatedSlots.end() ) // return; // // const std::set & dupNames = (*it).second; // // // erase other names associated to the same slot // std::set::const_iterator itDupName; // for (itDupName = dupNames.begin(); itDupName != dupNames.end(); ++itDupName) // { // const TName & dupName = (*itDupName); // if (dupName != name) // { // const TCharSlot *cs = _Names.getB(dupName); // if (cs != NULL) // { // _ReleasedNames.insert((cs->UserId<<4)+cs->CharIndex); // } // _Names.removeWithA( dupName ); // } // } // // // save changes // saveCharacterNames(); // // // slot has been fixed // _DuplicatedSlots.erase( it ); // // nlinfo("NAMEMGR: fixed character slot: userId=%u, slot=%u, name='%s'", charSlot.UserId, charSlot.CharIndex, name.c_str() ); // // if ( !_DuplicatedSlots.empty() ) // nlinfo("NAMEMGR: there still are %u duplicated character slots", _DuplicatedSlots.size() ); //} //----------------------------------------------------------------------------- ucstring CNameManager::renameCharacter(uint32 charId, uint32 homeSessionId) { const ucstring ucDefaultName = generateDefaultName(charId, homeSessionId); nlverify(assignName(charId, ucDefaultName, homeSessionId, true)); return ucDefaultName; } //----------------------------------------------------------------------------- ucstring CNameManager::generateDefaultName(uint32 charId, uint32 homeSessionId) { const TCharSlot charSlot(charId); string defaultName; string randomLetters(5, 'a'); uint count = 0; while (1) { for (uint i = 0; i < randomLetters.size(); i++) { randomLetters[i] = 'a' + (char)RandomGenerator.rand(uint16('z'-'a')); } defaultName = "Default" + randomLetters; if ( isNameUsable(defaultName, charSlot.UserId, charSlot.CharIndex, homeSessionId) == TCharacterNameResult::cnr_ok) break; // anti freeze if (++count > 100) break; } return defaultName; } //----------------------------------------------------------------------------- ucstring CNameManager::generateDefaultGuildName(uint32 guildId) { string defaultName; string randomLetters(5, 'a'); uint count = 0; while (1) { for (uint i = 0; i < randomLetters.size(); i++) { randomLetters[i] = 'a' + (char)RandomGenerator.rand(uint16('z'-'a')); } defaultName = "DefaultGuild" + randomLetters; if ( isGuildNameUsable(defaultName, guildId) == TCharacterNameResult::cnr_ok) break; // anti freeze if (++count > 100) break; } return defaultName; } //----------------------------------------------------------------------------- /** This methods implemented by CCommandHandler is used by the * command registry to retrieve the name of the object instance. */ const std::string &CNameManager::getCommandHandlerName() const { const static string name = "nameManager"; return name; } //----------------------------------------------------------------------------- CHARSYNC::TCharacterNameResult CNameManager::isNameUsable(const ucstring & ucNameIn, uint32 userId, uint8 charIndex, uint32 homeSessionId) { // WARNING: if you change validity checks here, // please also change them in CEntityIdTranslator::isValidEntityName() (nel/misc/eid_translator.cpp) // if the name contains a shard specification, remove it first ucstring ucName; ucstring::size_type pos = ucNameIn.find('('); if (pos != ucstring::npos) { //only keep the simple name for the test ucName = ucNameIn.substr(0, pos); } else { // test the whole input name ucName = ucNameIn; } // perform a first validity check on the name length if (ucName.size() < 3) { nldebug("VALID_NAME::CNameManager::isNameUsable name '%s' rejected because is too short", ucName.toString().c_str()); return TCharacterNameResult::cnr_invalid_name; } // perform a first validity check on the name length if (ucName.size() > 15) { nldebug("VALID_NAME::CNameManager::isNameUsable name '%s' rejected because is too long", ucName.toString().c_str()); return TCharacterNameResult::cnr_invalid_name; } // make sure the name is only composed of valid characters for (uint32 i = 0; i < ucName.size(); ++i) { if ( (ucName[i] < (uint16)'A' || ucName[i] > (uint16)'Z') && (ucName[i] < (uint16)'a' || ucName[i] > (uint16)'z') ) { nldebug("VALID_NAME::CNameManager::isNameUsable name '%s' rejected because it contains invalid character", ucName.toString().c_str()); return TCharacterNameResult::cnr_invalid_name; } } // it's now safe to convert the name to 8 bit lower case const string name = toLower( ucName.toUtf8() ); // make sure the name isn't forbidden for (uint32 i = 0; i < _ForbiddenNames.size(); ++i) { if ( NLMISC::testWildCard( name, _ForbiddenNames[i] ) ) { nldebug("VALID_NAME::CNameManager::isNameUsable name '%s' rejected because it contains the forbidden string '%s'", ucName.toString().c_str(), _ForbiddenNames[i].c_str()); return TCharacterNameResult::cnr_invalid_name; } } // make sure the name isn't reserved TReservedNames::iterator rit = _ReservedNames.find( name ); if ( rit != _ReservedNames.end() ) { if ( (*rit).second != userId ) { nldebug("VALID_NAME::CNameManager::isNameUsable name '%s' rejected because it's reserved", ucName.toString().c_str()); return TCharacterNameResult::cnr_already_exist; } } // make sure the name isn't temporary reserved { TTempReservedNames::iterator rit = _TemporaryReservedNames.find( name ); if ( rit != _TemporaryReservedNames.end() ) { if ( rit->second.UserId != userId && rit->second.UserId == homeSessionId) { nldebug("VALID_NAME::CNameManager::isNameUsable name '%s' rejected because it's temporary reserved", ucName.toString().c_str()); return TCharacterNameResult::cnr_already_exist; } } } TFullName fullname(name, homeSessionId); // make sure the name isn't used by another character TNamesIndex::TAToBMap::const_iterator it = _Names.getAToBMap().find( fullname ); if ( it != _Names.getAToBMap().end()) { if( it->second.UserId != userId || it->second.CharIndex != charIndex ) { nldebug("VALID_NAME::CNameManager::isNameUsable name '%s' rejected because it's already used", ucName.toString().c_str()); return TCharacterNameResult::cnr_already_exist; } } // make sure the name is not used by a guild { TGuildNames::iterator it = _GuildNames.find(name); if (it != _GuildNames.end()) { nldebug("VALID_NAME::CNameManager::isNameUsable name '%s' rejected because it's a guild name", ucName.toString().c_str()); return TCharacterNameResult::cnr_already_exist; } } // the name is valid, insert it in the temporary reserve TTemporaryReservedNameInfo trni; trni.UserId = userId; trni.ReserveDate = CTime::getSecondsSince1970(); trni.HomeSessionId = homeSessionId; _TemporaryReservedNames.insert(make_pair(name, trni)); nldebug("VALID_NAME::CNameManager::isNameUsable name '%s' accepted", ucName.toString().c_str()); return TCharacterNameResult::cnr_ok; } //----------------------------------------------------------------------------- TCharacterNameResult CNameManager::isGuildNameUsable(const ucstring & ucName, uint32 guildId) { // perform a first validity check on the name length if (ucName.size() < 3) return TCharacterNameResult::cnr_invalid_name; // perform a first validity check on the name length if (ucName.size() > 50) return TCharacterNameResult::cnr_invalid_name; // make sure the name is only composed of valid characters bool prevBlank = false; for (uint i = 0; i < ucName.size(); i++) { if ( ucName[i] == ucchar(' ') ) { if ( prevBlank ) { return TCharacterNameResult::cnr_invalid_name; } prevBlank = true; } else { prevBlank = false; if (!isalpha (ucName[i])) { return TCharacterNameResult::cnr_invalid_name; } } } // it's now safe to convert the name to 8 bit lower case const string name = toLower( ucName.toUtf8() ); // make sure the name isn't forbidden for (uint32 i = 0; i < _ForbiddenNames.size(); ++i) { if ( NLMISC::testWildCard( name, _ForbiddenNames[i] ) ) return TCharacterNameResult::cnr_invalid_name; } // make sure the name isn't reserved TReservedNames::iterator rit = _ReservedNames.find( name ); if ( rit != _ReservedNames.end() ) { return TCharacterNameResult::cnr_already_exist; } // make sure the name isn't temporary reserved { TTempReservedNames::iterator rit = _TemporaryReservedNames.find( name ); if ( rit != _TemporaryReservedNames.end() ) { return TCharacterNameResult::cnr_already_exist; } } // make sure the name isn't used by a character TNamesIndex::TAToBMap::const_iterator it = _Names.getAToBMap().lower_bound( TFullName(name, 0) ); if ( it != _Names.getAToBMap().end() && it->first.Name == name) { return TCharacterNameResult::cnr_already_exist; } // make sure the name is not used by another guild { TGuildNames::iterator it = _GuildNames.find(name); if (it != _GuildNames.end() && it->second.GuildId != guildId) return TCharacterNameResult::cnr_already_exist; } // the name is valid return TCharacterNameResult::cnr_ok; } //----------------------------------------------------------------------------- void CNameManager::registerLoadedGuildNames(uint32 shardId, const std::map &guilds, vector &renamedGuildIds) { bool saveFile = false; std::map::const_iterator first(guilds.begin()), last(guilds.end()); for (; first != last; ++first) { uint32 guildId = first->first; const ucstring &guildName = first->second; // check the name if (isGuildNameUsable(guildName, guildId) == TCharacterNameResult::cnr_ok) { // convert the name into the standard utf8 low case version string name = toLower(guildName.toUtf8()); // ok, the name is correct, check if it already exist TGuildNames::iterator it(_GuildNames.find(name)); if (it == _GuildNames.end()) { // this is a new name _GuildNames.insert(make_pair(name, TGuildSlot(shardId, guildId))); _GuildIndex.insert(make_pair(guildId, name)); saveFile = true; } else { BOMB_IF(it->second.GuildId != guildId, "Guild "<second.GuildId, continue); BOMB_IF(it->second.ShardId != shardId, "Guild "<second.ShardId, continue); } } else { // we need to rename the guild ucstring newName = generateDefaultGuildName(guildId); nlinfo("NM:registerLoadedGuildNames : Guild %u has a conflicting name '%s', renamed to '%s'", guildId, guildName.toUtf8().c_str(), newName.toUtf8().c_str()); // save it in the container std::string name = toLower(newName.toUtf8()); _GuildNames.insert(make_pair(name, TGuildSlot(shardId, guildId))); _GuildIndex.insert(make_pair(guildId, name)); saveFile = true; // put it in the renamed guild list return vector renamedGuildIds.push_back(guildId); } } if (saveFile) { saveGuildNames(); } } //----------------------------------------------------------------------------- bool CNameManager::assignGuildName(uint32 shardId, uint32 guildId, const ucstring &guildName) { bool ret; if (isGuildNameUsable(guildName, guildId) != TCharacterNameResult::cnr_ok) { // oups, bad name, we need to generate a new valid name ucstring newName = generateDefaultGuildName(guildId); string name = toLower(newName.toUtf8()); _GuildNames.insert(make_pair(name, TGuildSlot(shardId, guildId))); _GuildIndex.insert(make_pair(guildId, name)); ret = false; } else { // ok, the name is correct, do a simple insertion string name = toLower(guildName.toUtf8()); _GuildNames.insert(make_pair(name, TGuildSlot(shardId, guildId))); _GuildIndex.insert(make_pair(guildId, name)); ret = true; } // save the file saveGuildNames(); return ret; } //----------------------------------------------------------------------------- void CNameManager::releaseGuildName(uint32 shardId, uint32 guildId) { TGuildIndex::iterator it(_GuildIndex.find(guildId)); if (it == _GuildIndex.end()) { nlwarning("CNameManager::releaseGuildName : no guild %s registered in name manager", guildId); return; } // erase the name entry _GuildNames.erase(it->second); // erase the index entry _GuildIndex.erase(it); // save the file saveGuildNames(); } //----------------------------------------------------------------------------- //void CNameManager::saveCharacterNames() //{ // // save the character names // { // string fileName = "character_names.txt"; // // CSString s; // nlinfo("NAMEMGR::save: building file content: %s",fileName.c_str()); // for (TNamesIndex::TAToBMap::const_iterator it=_Names.getAToBMap().begin(); it!=_Names.getAToBMap().end(); ++it) // { // s << it->first.Name << " " << it->second.UserId << " " << it->second.CharIndex << " " << it->first.HomeSessionId << "\n"; // } // // if( s.empty() ) // return; // // nlinfo("NAMEMGR::save: send message to BS"); // // CBackupMsgSaveFile msg( fileName, CBackupMsgSaveFile::SaveFile, BsiGlobal ); // msg.DataMsg.serialBuffer((uint8*)s.c_str(), s.size()); // BsiGlobal.sendFile(msg); // } // // // save account names // { // string fileName = "account_names.txt"; // // string s; // nlinfo("NAMEMGR::save: building file content: %s",fileName.c_str()); // for (TAccountNames::iterator it=_AccountNames.begin();it!=_AccountNames.end();++it) // { // s+=NLMISC::toString("%s %i\n",(*it).second.c_str(),(*it).first); // } // // if( s.empty() ) // return; // // nlinfo("NAMEMGR::save: send message to BS"); // // CBackupMsgSaveFile msg( fileName, CBackupMsgSaveFile::SaveFile, BsiGlobal ); // msg.DataMsg.serialBuffer((uint8*)s.c_str(), s.size()); // BsiGlobal.sendFile(msg); // } //} //----------------------------------------------------------------------------- void CNameManager::saveGuildNames() { // save the guild names { string fileName = GUILD_NAME_FILE; string s; nlinfo("NAMEMGR::save: building file content: %s",fileName.c_str()); for (TGuildNames::const_iterator it=_GuildNames.begin(); it!=_GuildNames.end(); ++it) { s+=NLMISC::toString("%s %i %i\n", it->first.c_str(), it->second.ShardId, it->second.GuildId); } if( s.empty() ) return; nlinfo("NAMEMGR::save: send message to BS"); CBackupMsgSaveFile msg( fileName, CBackupMsgSaveFile::SaveFile, Bsi ); msg.DataMsg.serialBuffer((uint8*)s.c_str(), (uint)s.size()); Bsi.sendFile(msg); } } //----------------------------------------------------------------------------- void CNameManager::loadAllNames() { bool result; // load account names result=loadAccountNamesFromDatabase(); if (result==false) result=loadAccountNamesFromTxt(); if (result==false) nlinfo("NAMEMGR::load: Failed to load account names from txt file"); // load character names result=loadCharacterNamesFromDatabase(); if (result==false) result=loadCharacterNamesFromTxt(); // if (result==false) // result=loadCharacterNamesFromXML(); if (result==false) nlinfo("NAMEMGR::load: Failed to load character names from txt file"); // load character names result=loadGuildsNamesFromTxt(); if (result==false) nlinfo("NAMEMGR::load: Failed to load guilds names from txt file"); // load forbidden names result=loadForbiddenNames(); if (result==false) nlwarning("NAMEMGR::load: Failed to load forbidden name file"); // load reserved names result=loadReservedNames("reserved_names.xml"); if (result==false) nlwarning("NAMEMGR::load: Failed to load reserved player name file"); // load dev and gm names result=loadReservedNames("dev_gm_names.xml"); if (result==false) nlwarning("NAMEMGR::load: Failed to load reserved dev and gm name file"); nlinfo("NameManager : checking %u loaded names...", _Names.getAToBMap().size()); // revalidate all character names TNamesIndex::TAToBMap::const_iterator first(_Names.getAToBMap().begin()), last(_Names.getAToBMap().end()); for (uint i=0; first != last; ++i) { TNamesIndex::TAToBMap::const_iterator next(first); ++next; const TFullName &fullName = first->first; const TCharSlot &charSlot = first->second; if (isNameUsable(fullName.Name, charSlot.UserId, charSlot.CharIndex, fullName.HomeSessionId) != CHARSYNC::TCharacterNameResult::cnr_ok) { nlinfo("NameManager : renaming invalid character name '%s' for char %u", fullName.Name.c_str(), charSlot.getCharId()); // get the shard id uint32 charId = charSlot.getCharId(); // the name is invalid ! ucstring newName = renameCharacter(charSlot.getCharId(), fullName.HomeSessionId); // do not access the fullName and charSlot var from now RSMGR::CCharacterPtr character = RSMGR::CCharacter::load(*_Database, charId, __FILE__, __LINE__); if (character != NULL) { character->setCharName(newName.toUtf8()); character->update(*_Database); } } first = next; if (i%1000 == 0) { nldebug("NameManager : checking name advance %u/%u", i, _Names.getAToBMap().size()); } } } //----------------------------------------------------------------------------- bool CNameManager::loadAccountNamesFromTxt() { FILE* f; string fileName; // open the file fileName = BsiGlobal.getLocalPath() + "account_names.txt"; f=fopen(fileName.c_str(),"rb"); if (f == NULL) { nlwarning("Failed to open file for reading: %s", fileName.c_str() ); return false; } CSString input; // read the file content into a buffer uint32 size=NLMISC::CFile::getFileSize(f); input.resize(size); uint32 readSize= (uint32)fread(&input[0],1,size,f); fclose(f); BOMB_IF(readSize!=size,"Failed to read file content for file: "+fileName,return false); // scan the content while (!input.empty()) { CSString line=input.firstLine(true); if (line.strip().empty()) continue; DROP_IF (line.countWords()!=2,"Invalid line found in account names file: "+line,continue); DROP_IF (line.word(1).atoi()==0,"Invalid user id in account names file line: "+line,continue); _AccountNames[line.word(1).atoi()]=line.word(0); } return true; } //----------------------------------------------------------------------------- bool CNameManager::loadAccountNamesFromDatabase() { if (_Database == NULL) return false; // request the database to extract the complete list of account names CSString query; query << "SELECT user_id, user_name FROM ring_users"; BOMB_IF(!_Database->query(query), "Failed to load account names from the database", return false); auto_ptr result = _Database->useResult(); while (result->fetchRow()) { uint32 userId; string accountName; result->getField(0, userId); result->getField(1, accountName); _AccountNames[userId]=accountName; } return true; } //----------------------------------------------------------------------------- //bool CNameManager::loadAccountNamesFromXML() //{ // CIFile f; // string fileName; // // fileName = BsiGlobal.getLocalPath() + "account_names.xml"; // if( !f.open( fileName ) ) // return false; // // nlinfo("NAMEMGR: Loading file: %s",fileName.c_str()); // // CIXml input; // if(!input.init( f )) // { // nlwarning("NAMEMGR:loadAccountNamesFromXML Can't init xml input for file %s", fileName.c_str()); // return false; // } // input.serialCont( _AccountNames ); // f.close(); // // return true; //} //----------------------------------------------------------------------------- //bool CNameManager::loadAccountNamesFromEIDTranslator() //{ // nlinfo("NAMEMGR: Build account names from EIDTranslator"); // // try // { // CEntityIdTranslator::getInstance()->load(BsiGlobal.getLocalPath() + "eid_translation.data", IService::getInstance()->ConfigFile.getVar("InvalidEntityNamesFilename").asString()); // } // catch(const Exception &) // { // // if we can't load the file, we force a check coherency // nlwarning("Can't load the eid_translation.data"); // return false; // } // // for( map::const_iterator it = CEntityIdTranslator::getInstance()->getRegisteredEntities().begin(); it != CEntityIdTranslator::getInstance()->getRegisteredEntities().end(); ++it ) // { // _AccountNames[(*it).second.UId]= (*it).second.UserName; // } // saveCharacterNames(); // // return true; //} //----------------------------------------------------------------------------- bool CNameManager::loadCharacterNamesFromTxt() { FILE* f; string fileName; // open the file fileName = BsiGlobal.getLocalPath() + "character_names.txt"; f=fopen(fileName.c_str(),"rb"); if (f == NULL) { nlwarning("Failed to open file for reading: %s", fileName.c_str() ); return false; } CSString input; // read the file content into a buffer uint32 size=NLMISC::CFile::getFileSize(f); input.resize(size); uint32 readSize= (uint32)fread(&input[0],1,size,f); fclose(f); BOMB_IF(readSize!=size,"Failed to read file content for file: "+fileName,return false); _Names.clear(); _DuplicatedSlots.clear(); typedef map TCharSlotToName; TCharSlotToName charSlotToName; vector lines; NLMISC::explode(string(input), string("\n"), lines, true); // scan the content // while (!input.empty()) // bool mustSaveFile = false; for (uint i=0; i 0 && (i%100 == 0)) nldebug("Loading character names : %u/%u (%.2f%%)", i, lines.size(), float(i)/float(lines.size())*100.0f); // CSString line=input.firstLine(true); CSString line = lines[i]; vector words; NLMISC::explode(string(line), string(" "), words, true); if (words.empty()) continue; // if (line.strip().empty()) // continue; // BOMB_IF (line.countWords()!=3,"Invalid line found in character names file: "+line,continue); BOMB_IF (words.size()!=3 && words.size()!=4,"Invalid line found in character names file: "+line,continue); sint i1, i2; NLMISC::fromString(words[1], i1); NLMISC::fromString(words[2], i2); BOMB_IF (i1==0,"Invalid user id in character names file line: "+line,continue); BOMB_IF (i2>15 || (i2==0 && words[2] != "0"),"Invalid slot id in character names file line: "+line,continue); sint sessionId =0; if (words.size() > 3) NLMISC::fromString(words[3], sessionId); const TName name = words[0]; const TCharSlot charSlot = TCharSlot(i1, i2); TFullName fullname(name, sessionId); // Check the name is usable, otherwise skip (to resolve corrupted names) if ( isNameUsable( ucstring(name), charSlot.UserId, charSlot.CharIndex, sessionId ) != TCharacterNameResult::cnr_ok) { nlwarning( "Invalid character name '%s' for user %u char slot %u in %s, will be reset to default", name.c_str(), charSlot.UserId, (uint)charSlot.CharIndex, fileName.c_str() ); // mustSaveFile = true; continue; } if (_Names.getAToBMap().find(fullname) != _Names.getAToBMap().end()) _Names.removeWithA(fullname); if (_Names.getBToAMap().find(charSlot) != _Names.getBToAMap().end()) _Names.removeWithB(charSlot); _Names.add(fullname, charSlot); // TNames::const_iterator it = _Names.find( name ); // nlassert(it != _Names.end()); // check duplicated character slots TCharSlotToName::iterator itSlot = charSlotToName.find( charSlot ); if ( itSlot != charSlotToName.end() ) { _DuplicatedSlots[charSlot].insert( (*itSlot).second ); _DuplicatedSlots[charSlot].insert( name ); } else { charSlotToName[charSlot] = name; } // // update the EID translator // uint64 lid = ( (*it).second.UserId<<4 ) | (*it).second.CharId; // CEntityId id( RYZOMID::player, lid ); // id.setDynamicId( 0 ); // id.setCreatorId( 0 ); // // if (CEntityIdTranslator::getInstance()->isEntityRegistered(id)) // CEntityIdTranslator::getInstance()->unregisterEntity( id ); // CEntityIdTranslator::getInstance()->registerEntity( id, capitalize( (*it).first ), (*it).second.CharId, (*it).second.UserId, _AccountNames[(*it).second.UserId] ); } if ( !_DuplicatedSlots.empty() ) { nlwarning("NAMEMGR: file '%s' contains %u duplicated character slots", fileName.c_str(), _DuplicatedSlots.size() ); } // if ( mustSaveFile ) // { // saveCharacterNames(); // } return true; } bool CNameManager::loadCharacterNamesFromDatabase() { if (_Database == NULL) return false; _Names.clear(); // request the database to extract the complete list of character names CSString query; query << "SELECT char_id, char_name, home_mainland_session_id FROM characters"; BOMB_IF(!_Database->query(query), "Failed to load character names from the database", return false); auto_ptr result = _Database->useResult(); while (result->fetchRow()) { uint32 charId; string charName; uint32 sessionNum; result->getField(0, charId); result->getField(1, charName); result->getField(2, sessionNum); // uint32 userId = charId >> 4; // uint8 charSlot = uint8(charId & 0xf); _Names.add(TFullName(charName, sessionNum), TCharSlot(charId)); } return true; } //----------------------------------------------------------------------------- //bool CNameManager::loadCharacterNamesFromXML() //{ // CIFile f; // string fileName; // // // load the character names and insert them into the eid translator // fileName = BsiGlobal.getLocalPath() + "character_names.xml"; // if( !f.open( fileName ) ) // return false; // // nlinfo("NAMEMGR: Loading file: %s",fileName.c_str()); // CIXml input; // if(!input.init( f )) // { // nlwarning("Can't init xml input for file %s", fileName.c_str()); // return false; // } // TNames namesToCheckAndAdd; // input.serialCont( namesToCheckAndAdd ); // f.close(); // // _DuplicatedSlots.clear(); // map charSlotToName; // // // update the EID translator // bool mustSaveFile = false; // for( TNames::iterator it = namesToCheckAndAdd.begin(); it != namesToCheckAndAdd.end(); ++it ) // { // const TName & name = (*it).first; // const TCharSlot charSlot = TCharSlot( (*it).second.UserId, (*it).second.CharIndex ); // // // Check the name is usable, otherwise skip (to resolve corrupted names) // if ( isNameUsable( ucstring(name), charSlot.UserId, charSlot.CharIndex ) != TCharacterNameResult::cnr_ok) // { // nlwarning( "Invalid character name '%s' for user %u char slot %u in %s, will be reset to default", name.c_str(), charSlot.UserId, (uint)charSlot.CharIndex, fileName.c_str() ); // mustSaveFile = true; // continue; // } // // if (_Names.getAToBMap().find(it->first) != _Names.getAToBMap().end()) // _Names.removeWithA(it->first); // if (_Names.getBToAMap().find(it->second) != _Names.getBToAMap().end()) // _Names.removeWithB(it->second); // // _Names.add( it->first, it->second ); // // // check duplicated character slots // map::iterator itSlot = charSlotToName.find( charSlot ); // if ( itSlot != charSlotToName.end() ) // { // _DuplicatedSlots[charSlot].insert( (*itSlot).second ); // _DuplicatedSlots[charSlot].insert( name ); // } // else // { // charSlotToName[charSlot] = name; // } // //// uint64 lid = ( (*it).second.UserId<<4 ) | (*it).second.CharId; //// CEntityId id( RYZOMID::player, lid ); //// id.setDynamicId( 0 ); //// id.setCreatorId( 0 ); //// //// if (CEntityIdTranslator::getInstance()->isEntityRegistered(id)) //// CEntityIdTranslator::getInstance()->unregisterEntity( id ); //// CEntityIdTranslator::getInstance()->registerEntity( id, capitalize( name ), (*it).second.CharId, (*it).second.UserId, _AccountNames[(*it).second.UserId] ); // } // // if ( !_DuplicatedSlots.empty() ) // { // nlwarning("NAMEMGR: file '%s' contains %u duplicated character slots", fileName.c_str(), _DuplicatedSlots.size() ); // } // // if ( mustSaveFile ) // { // saveCharacterNames(); // } // // return true; //} //----------------------------------------------------------------------------- bool CNameManager::loadGuildsNamesFromTxt() { FILE* f; string fileName; // open the file fileName = Bsi.getLocalPath() + GUILD_NAME_FILE; f=fopen(fileName.c_str(),"rb"); if (f == NULL) { nlinfo("Failed to open file for reading: %s", fileName.c_str() ); return false; } CSString input; // read the file content into a buffer uint32 size=NLMISC::CFile::getFileSize(f); input.resize(size); uint32 readSize= (uint32)fread(&input[0],1,size,f); fclose(f); BOMB_IF(readSize!=size,"Failed to read file content for file: "+fileName,return false); _GuildNames.clear(); _GuildIndex.clear(); typedef map TGuildSlotToName; TGuildSlotToName guildSlotToName; vector lines; NLMISC::explode(string(input), string("\n"), lines, true); // scan the content // while (!input.empty()) // bool mustSaveFile = false; for (uint i=0; i 0 && (i%100 == 0)) nldebug("Loading guild names : %u/%u (%.2f%%)", i+1, lines.size(), float(i+1)/float(lines.size())*100.0f); CSString line = lines[i]; vector words; NLMISC::explode(string(line), string(" "), words, true); if (words.empty()) continue; // merge the first words until we have only 3 words while (words.size() > 3) { words[0] += words[1]; words.erase(words.begin()+1); } BOMB_IF (words.size()!=3,"Invalid line "<WriteFilesDirectory.toString() + "invalid_entity_names.txt"; FILE *fp = fopen (fileName.c_str(), "r"); if (fp == NULL) return false; nlinfo("NAMEMGR: Loading file: %s",fileName.c_str()); while (true) { char str[512]; fgets(str, 511, fp); if(feof(fp)) break; if (strlen(str) > 0) { str[strlen(str)-1] = '\0'; toLower( str ); _ForbiddenNames.push_back(string()); _ForbiddenNames.back() = str; } } fclose (fp); return true; } //----------------------------------------------------------------------------- bool CNameManager::loadReservedNames(const char* fileNameWithoutPath) { CIFile f; string fileName; // read the reserved name list fileName = IService::getInstance()->WriteFilesDirectory.toString() + fileNameWithoutPath; if( !f.open( fileName ) ) return false; nlinfo("NAMEMGR: Loading file: %s",fileName.c_str()); CIXml input; if(!input.init( f )) { nlwarning("Can't init xml input for file %s", fileName.c_str()); return false; } TReservedNames reservedName; input.serialCont( reservedName ); f.close(); for( TReservedNames::iterator it = reservedName.begin(); it != reservedName.end(); ++it ) { _ReservedNames[ toLower( (*it).first ) ] = (*it).second; } return true; } void CNameManager::update() { TTempReservedNames::iterator first(_TemporaryReservedNames.begin()), last(_TemporaryReservedNames.end()); uint32 now = CTime::getSecondsSince1970(); for (; first != last; ++first) { if (first->second.ReserveDate+TEMPORARY_RESERVED_NAME_EXPIRATION > now) { // this reservation has expired, release it _TemporaryReservedNames.erase(first); // next release at next update break; } } } const CNameManager::TName &CNameManager::getGuildName(uint32 guildId) const { TGuildIndex::const_iterator it(_GuildIndex.find(guildId)); if (it == _GuildIndex.end()) { static string emptyString; return emptyString; } else return it->second; } uint32 CNameManager::findCharId(const std::string &charName, uint32 homeSessionId) { TFullName fullname(charName, homeSessionId); TNamesIndex::TAToBMap::const_iterator it(_Names.getAToBMap().find(fullname)); if (it != _Names.getAToBMap().end()) { return it->second.getCharId(); } // not found return 0; } /// Try to find a shard id from a name without session id, return 0 if 0 or more than one match. uint32 CNameManager::findCharId(const std::string &charName) { TFullName fullname(charName, 0); TNamesIndex::TAToBMap::const_iterator it(_Names.getAToBMap().upper_bound(fullname)); TNamesIndex::TAToBMap::const_iterator found(_Names.getAToBMap().end()); while (it->first.Name == charName) { if (found != _Names.getAToBMap().end()) { // more than one match return 0; } // first found found = it; } if (it != _Names.getAToBMap().end()) { // we found one return it->second.getCharId(); } // not found 0 return 0; } NLMISC_CLASS_COMMAND_IMPL(CNameManager, dump) { if (args.size() == 1 && args[0] == "all") { // dump the complete name table (heavy !) const TNamesIndex::TBToAMap &ba = _Names.getBToAMap(); log.displayNL("Dumping %u character names :", ba.size()); TNamesIndex::TBToAMap::const_iterator first(ba.begin()), last(ba.end()); for (; first != last; ++first) { const TCharSlot &charSlot = first->first; const TFullName &fullName = first->second; log.displayNL(" User %u, Slot %u (char_id %u) : Name '%s' on session %u", charSlot.UserId, charSlot.CharIndex, charSlot.UserId*16+charSlot.CharIndex, fullName.Name.c_str(), fullName.HomeSessionId.asInt()); } return true; } if (args.size() != 0) return false; log.displayNL("Dumping NameManager internal state :"); log.displayNL(" - %u character names", _Names.getAToBMap().size()); log.displayNL(" - %u account names", _AccountNames.size()); log.displayNL(" - %u guild names", _GuildNames.size()); log.displayNL(" - %u guild index", _GuildIndex.size()); log.displayNL(" - %u reserved names", _ReservedNames.size()); log.displayNL(" - %u forbidden names", _ForbiddenNames.size()); log.displayNL("End of dump"); return true; } NLMISC_CLASS_COMMAND_IMPL(CNameManager, releaseGuildNamesForShard) { if (args.size() != 1) return false; uint32 shardId = atoui(args[1].c_str()); vector guildNameToRemove; TGuildNames::iterator first(_GuildNames.begin()), last(_GuildNames.end()); for (; first != last; ++first) { if (first->second.ShardId == shardId) guildNameToRemove.push_back(first->second.GuildId); } log.displayNL("Releasing %u guild name for shard %u", guildNameToRemove.size(), shardId); for (uint i=0; isecond); _GuildIndex.erase(it); } return true; }