// 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 "string_manager.h" #include "input_output_service.h" #include "nel/misc/i18n.h" #include "nel/misc/path.h" #include "nel/misc/file.h" #include "nel/net/unified_network.h" #include "nel/net/service.h" #include "nel/misc/bit_mem_stream.h" #include "nel/misc/diff_tool.h" #include "game_share/ryzom_mirror_properties.h" #include "game_share/backup_service_interface.h" #include "nel/georges/u_form_elm.h" #include "nel/georges/load_form.h" #include "server_share/r2_variables.h" #include //--------------------------------------------------------------------------------------- // Stuff used for management of log messages //static bool VerboseLog=false; //bool DebugReplacementParameter = false; //NLMISC_VARIABLE(bool, DebugReplacementParameter, "Debug missing replacement parameter."); NLMISC::CVariable DebugReplacementParameter("ios","DebugReplacementParameter", "Insert error debugging information in generated text", false, 0, true); NLMISC::CVariable VerboseStringManager("ios","VerboseStringManager", "Turn on or off or check the state of verbose string manager logging", false, 0, true); NLMISC::CVariable VerboseStringManagerParser("ios","VerboseStringManagerParser", "Turn on or off or check the state of verbose string manager logging when parsing files", false, 0, true); NLMISC::CVariable StringManagerCacheDirectory("ios","StringManagerCacheDirectory", "Directory to read/write string cache file (default (empty) is service SaveFilesDirectory)", "", 0, true); #define LOG if (!VerboseStringManager) {} else nlinfo #define LOGPARSE if (!VerboseStringManagerParser) {} else nlinfo // Instantiate the string manager. CStringManager Instance; CStringManager *SM = &Instance; CIosLocalSender IosLocalSender; using namespace STRING_MANAGER; using namespace NLMISC; using namespace NLNET; using namespace std; const ucstring nl("\r\n"); std::string CStringManager::_LanguageCode[NB_LANGUAGES] = { "wk", // Work "en", // english "de", "fr", "ru", "es", /* ace: currently, we only want english, i remove other language to remove warning during IOS launch "fr", // french "zh", // traditionnal chinese "zh-CN" // simplified chinese */ }; /* ucstring CStringManager::getEntityDisplayName(const NLMISC::CEntityId &eid) { ucstring ret; NLMISC::CSheetId sid = SM->getSheetId(eid); if (sid != NLMISC::CSheetId::Unknown) { // TSheetInfoContainer::iterator it(_SheetInfo.find(sid)); if (it != _SheetInfo.end()) ret = it->second.DisplayName; } // not found. if (ret.empty()) ret = ucstring(eid.toString()); return ret; } */ uint32 CStringManager::CEntityWords::getStringId(const std::string &rowName, const std::string columnName) const { std::map::const_iterator colIt(_ColumnInfo.find(columnName)); if (colIt != _ColumnInfo.end()) { std::map::const_iterator rowIt(_RowInfo.find(rowName)); if (rowIt != _RowInfo.end()) { return _Data[rowIt->second*_NbColums + colIt->second]; } } // not found, return rowName.columnName. if (DebugReplacementParameter) return SM->storeString(ucstring(std::string("<")+rowName+"."+columnName+">")); else { ucstring s; s += ucchar(8); return SM->storeString(s); } } CStringManager::CStringManager() { _TestOnly = false; _CacheLoaded = false; _Mapper = NLMISC::CStringMapper::createLocalMapper(); _DefaultSetPhraseLanguage = NB_LANGUAGES; // init the game share string manager pointer. // GameShareSM = this; } void CStringManager::loadCache() { if (_CacheLoaded) { return; } nlassert(_StringIdx.empty()); nlassert(_StringBase.empty()); // std::string filename("data_shard/ios.string_cache"); // _CacheFilename = NLMISC::CPath::lookup(filename, false, false); // if (!_CacheFilename.empty()) std::string cacheDirectory = ((StringManagerCacheDirectory.get() == "") ? Bsi.getLocalPath() : StringManagerCacheDirectory.get()); _CacheFilename = CPath::standardizePath(cacheDirectory) + "ios.string_cache"; if (CFile::fileExists(_CacheFilename)) { nlinfo("Reading string cache..."); // ok, load the cache data NLMISC::CIFile file(_CacheFilename); file.serial(_CacheTimestamp); uint32 start = CTime::getSecondsSince1970(); uint32 t1 = start; while (!file.eof()) { uint32 id; ucstring str; file.serial(id); file.serial(str); // nldebug("Loaded from cache [%u][%s]", id, str.toString().c_str()); // create a new entry std::pair ret; ret = _StringIdx.insert(std::make_pair(str, id)); // nlassert(ret.second); if (!ret.second) { TMappedUStringContainer::iterator it = _StringIdx.find(str); nlassert(it != _StringIdx.end()); nlwarning("String cache : string [%s][%u] already in the string map with id [%u] !", str.c_str(), id, it->second); if (id != it->second) { nlwarning(" !!! ID diff : string cache invalide."); } } // if (_StringBase.size() <= id) // { // _StringBase.reserve(_StringBase.size()*2); // } // _StringBase.resize(id+1); // _StringBase[id] = str; while (_StringBase.size() <= id) _StringBase.push_back(string()); _StringBase[id] = str; // some logging uint32 now = CTime::getSecondsSince1970(); if ((now - t1) > 10) { // more than 10 s... log where we are double progress = 100*(file.getPos()/double(file.getFileSize())); nlinfo(" %6.2f%% read", progress); t1 = now; } } uint32 now = CTime::getSecondsSince1970(); nlinfo("Done, %u strings read in %u seconds.", _StringBase.size(), now-start); } else { // create a new cache if (!CFile::isExists(cacheDirectory)) CFile::createDirectoryTree(cacheDirectory); _CacheTimestamp = CTime::getSecondsSince1970(); NLMISC::COFile file(_CacheFilename); file.serial(_CacheTimestamp); } _CacheLoaded = true; } void CStringManager::clearCache(NLMISC::CLog *log) { if (_CacheFilename.empty()) { log->displayNL("Clear cache requested but no cache file name available!"); return; } log->displayNL("Clearing cache file and reloading string files..."); CFile::deleteFile(_CacheFilename); _StringIdx.clear(); _StringBase.clear(); _CacheLoaded = false; // this will clear all loaded string, then reinit the string manager reload(log); // warn all the client that they must dropout there cache file } const CStringManager::CEntityWords &CStringManager::getEntityWords(TLanguages lang, STRING_MANAGER::TParamType type) const { nlassert(lang < NB_LANGUAGES); nlassert(type < STRING_MANAGER::NB_PARAM_TYPES ||type == STRING_MANAGER::self); return _AllEntityWords[lang][type]; } bool CStringManager::CClause::eval(CStringManager::TLanguages lang, const CCharacterInfos *charInfo, const CPhrase *phrase) { bool ret = false; // if no condition, it's true ! if (Conditions.empty()) return true; for (uint i=0; !ret && i &andedCond = Conditions[i]; bool temp = true; for (uint j=0; temp && jParams[andedCond[j].ParamIndex]; temp &= param->eval(lang,charInfo ,andedCond[j]); } ret |= temp; } return ret; } NLMISC::CSheetId CStringManager::getSheetId(const NLMISC::CEntityId &entityId) { TDataSetRow entityIndex = TheDataset.getDataSetRow( entityId ); if (!entityIndex.isValid()) return NLMISC::CSheetId::Unknown; CMirrorPropValueBase sheetId( TheDataset, entityIndex, DSPropertySHEET ); return NLMISC::CSheetId( sheetId ); } NLMISC::CSheetId CStringManager::getSheetServerId(const NLMISC::CEntityId &entityId) { TDataSetRow entityIndex = TheDataset.getDataSetRow( entityId ); if (!entityIndex.isValid()) return NLMISC::CSheetId::Unknown; CMirrorPropValueBase sheetServerId( TheDataset, entityIndex, DSPropertySHEET_SERVER ); return NLMISC::CSheetId( sheetServerId ); } const CStringManager::TSheetInfo &CStringManager::getSheetInfo(const NLMISC::CSheetId &sheetId) { TSheetInfoContainer::iterator it(_SheetInfo.find(sheetId)); if (it != _SheetInfo.end()) return it->second; static TSheetInfo unknown; // nlwarning("Unknown sheetId : %s", sheetId.toString().c_str()); return unknown; } void CStringManager::buildMissingPhraseStream(CCharacterInfos * charInfo, uint32 seqNum, NLMISC::CBitMemStream & bmsOut, const std::string &phraseName) { // store a string for this error message. uint32 id = storeString(ucstring(""); // now, build the message for the client. GenericXmlMsgHeaderMngr.pushNameToStream( "STRING_MANAGER:PHRASE_SEND", bmsOut); bmsOut.serial(seqNum); bmsOut.serial(id); LOG("Sending phrase [%s] content", phraseName.c_str()); } bool CStringManager::buildPhraseStream( CCharacterInfos * charInfo, uint32 seqNum, NLNET::CMessage & message, bool debug, CStringManager::TLanguages lang, NLMISC::CBitMemStream & bmsOut) { // uint32 seqNum; std::string phraseName; message.serial(phraseName); LOG("Receiving phrase %u as '%s'", seqNum, phraseName.c_str() ); if (phraseName.empty()) { nlwarning("buildPhraseStream: phrase name is empty !"); return false; } // ok, try to find this phrase bool found = false; TPhrasesContainer::iterator it(_AllPhrases[lang].find(phraseName)); if (it != _AllPhrases[lang].end()) found = true; // if not found try to get the working text if (!found && lang != english) { it = _AllPhrases[english].find(phraseName); if (it != _AllPhrases[english].end()) found = true; } // if still not found try to get the work text if (!found && lang != work) { it = _AllPhrases[work].find(phraseName); if (it != _AllPhrases[work].end()) { found = true; // every phrases should be at least translated in english nlwarning("CStringManager::buildPhraseStream the phrase [%s] has not been translated in english!", phraseName.c_str()); } } if (!found) { nlwarning("CStringManager::buildPhraseStream the phrase [%s] is unknown in %s, in english and in work", phraseName.c_str(), _LanguageCode[lang].c_str()); buildMissingPhraseStream(charInfo, seqNum, bmsOut, phraseName); return true; } // ok, we have the phrase, we can parse the parameter from the message CPhrase &phrase = it->second; // std::vector params; // params.resize(phrase.Params.size()); uint i; try { bool result = true; for (i=1; iextractFromMessage(message, debug); } if (!result) nlwarning("Format error extracting parameters in phrase %s, result string could be erroneous !",phrase.Name.c_str() ); } catch(...) { nlwarning("Exception while extracting parameters in phrase %s, result string could be erroneous !",phrase.Name.c_str() ); // init the rest with default values for (; isetDefaultValue(); } // return; } // update the self parameter with dest eid. if ( charInfo ) phrase.Params[0]->EId = charInfo->EntityId; else phrase.Params[0]->EId = CEntityId::Unknown; // ok, we have the phrase and a list of typed param, we can search for the good clause. found = false; for (i=0; ifillBitMemStream(charInfo,lang, rep, bmsOut); } LOG("Sending phrase [%s] content", phraseName.c_str()); return true; } void CStringManager::receiveUserPhrase(NLNET::CMessage &message, bool debug) { // extract the parameters uint32 userId; uint32 seqNum; message.serial( userId ); message.serial(seqNum); LOG("Receiving a phrase for user id %d", userId); // get the user language TUserLanguagesContainer::const_iterator itUser = _UsersLanguages.find( userId ); if ( itUser == _UsersLanguages.end() ) { // just extract the phrase name for more info in the warning std::string phraseName; message.serial(phraseName); nlwarning("CStringManager::receiveUserPhrase '%s', unknown user %u", phraseName.c_str(), userId); return; } // built the stream to be sent to the client NLMISC::CBitMemStream bmsOut; if ( buildPhraseStream( NULL, seqNum, message, debug, (*itUser).second.Language, bmsOut ) ) { CMessage msgout( "IMPULSION_UID" ); msgout.serial( userId ); msgout.serialBufferWithSize((uint8*)bmsOut.buffer(), bmsOut.length()); NLNET::CUnifiedNetwork::getInstance()->send( (*itUser).second.FrontEndId, msgout ); nldebug( "IOSSM: Sent IMPULSION_UID to %hu (PHRASE_SEND)", (*itUser).second.FrontEndId.get() ); } } void CStringManager::receivePhrase(NLNET::CMessage &message, bool debug, const std::string &serviceName) { // extract the parameters // NLMISC::CEntityId dest; TDataSetRow dest; uint32 seqNum; message.serial( dest ); LOG("Receiving a phrase for char %s:%x", TheDataset.getEntityId(dest).toString().c_str(), dest.getIndex() ); message.serial(seqNum); CEntityId destId = TheDataset.getEntityId(dest); // retrieve the dest player infos. CCharacterInfos *charInfo = IOS->getCharInfos(destId); if (charInfo == 0) { nlwarning("CStringManager::receivePhrase unknown eid %s:%x", TheDataset.getEntityId(dest).toString().c_str(), dest.getIndex()); return; } if (destId.getType() == RYZOMID::npc || destId.getType() == RYZOMID::creature) { // read the phrase name std::string phraseName; message.serial(phraseName); nlwarning("Service '%s' is trying to send phrase '%s' to a AIS entity !", serviceName.c_str(), phraseName.c_str()); return; } // built the stream to be sent to the client NLMISC::CBitMemStream bmsOut; if ( buildPhraseStream( charInfo, seqNum, message, debug, charInfo->Language, bmsOut ) ) { NLNET::CMessage msgout( "IMPULS_CH_ID" ); NLMISC::CEntityId destId = charInfo->EntityId; uint8 channel = 1; msgout.serial( destId ); msgout.serial( channel ); msgout.serialBufferWithSize((uint8*)bmsOut.buffer(), bmsOut.length()); NLNET::CUnifiedNetwork::getInstance()->send(TServiceId(charInfo->EntityId.getDynamicId()), msgout); } } void CStringManager::broadcastSystemMessage(NLNET::CMessage &message, bool debug) { TDataSetRow client; vector temp; set excluded; CChatGroup::TGroupType audience = CChatGroup::nbChatMode; uint32 seqNum; message.serial(client); message.serialCont(temp); excluded.insert(temp.begin(), temp.end()); message.serialEnum(audience); message.serial(seqNum); if (!IOS->getChatManager().checkClient(client)) { nlwarning("broadcastSystemMessage : can't find client %s:%x in chat client", TheDataset.getEntityId(client).toString().c_str(), client.getIndex()); return; } try { // can thow CChatManager::EChatClient exception CChatClient &chatClient = IOS->getChatManager().getClient(client); TGroupId groupId; switch (audience) { case CChatGroup::say: // force an update of the say audience chatClient.getSayAudience(true); groupId = chatClient.getSayAudienceId(); break; case CChatGroup::team: groupId = chatClient.getTeamChatGroup(); break; case CChatGroup::guild: groupId = chatClient.getGuildChatGroup(); break; case CChatGroup::shout: // force an update of the shout audience chatClient.getShoutAudience(true); groupId = chatClient.getShoutAudienceId(); break; default: nlwarning("broadcastSystemMessage : unsupported chat mode '%s' for broadcast", CChatGroup::groupTypeToString(audience).c_str()); return; } // can throw EChatGroup CChatGroup &chatGroup = IOS->getChatManager().getGroup(groupId); // generate a unique string seq number seqNum = STRING_MANAGER::pickStringSerialNumber(); // and send to all the client in audience. CChatGroup::TMemberCont::iterator first(chatGroup.Members.begin()), last(chatGroup.Members.end()); for (; first != last; ++first) { TDataSetRow dsr = *first; // send to all client except the sender if (dsr != client) { CEntityId eid = TheDataset.getEntityId(dsr); if (excluded.find(eid) != excluded.end()) continue; CCharacterInfos *charInfo = IOS->getCharInfos(eid); if (charInfo == NULL) continue; // copy the message CMessage msg; msg.assignFromSubMessage(message); // built the stream to be sent to the client NLMISC::CBitMemStream bmsOut; if ( buildPhraseStream( charInfo, seqNum, msg, debug, charInfo->Language, bmsOut ) ) { NLNET::CMessage msgout( "IMPULS_CH_ID" ); NLMISC::CEntityId destId = charInfo->EntityId; uint8 channel = 1; msgout.serial( destId ); msgout.serial( channel ); msgout.serialBufferWithSize((uint8*)bmsOut.buffer(), bmsOut.length()); NLNET::CUnifiedNetwork::getInstance()->send(TServiceId(charInfo->EntityId.getDynamicId()), msgout); // inform the client to display the dyn string in system info { CMessage msgout( "IMPULSION_ID" ); msgout.serial( const_cast (eid) ); CBitMemStream bms; if ( ! GenericXmlMsgHeaderMngr.pushNameToStream( "STRING:DYN_STRING", bms) ) { nlwarning(" Msg name CHAT:DYN_STRING not found"); } else { bms.serial( seqNum ); msgout.serialBufferWithSize((uint8*)bms.buffer(), bms.length()); CUnifiedNetwork::getInstance()->send( TServiceId(eid.getDynamicId()), msgout ); } } } } } } catch(const CChatManager::EChatClient &e) { nlwarning("%s", e.what()); } catch(...) { } } //void CStringManager::requestString(const NLMISC::CEntityId &client, uint32 stringId) //{ // ucstring str = getString(stringId); // // CCharacterInfos *charInfo = IOS->getCharInfos(client); // if (charInfo == 0) // { // if (IsRingShard.get()) // { // // In ring shard, it is possible that the client autologin and // // autochoose the character rapidly, and if a string need to be resolved // // to display the char summary, it is possible client receive the // // dynamic string from IOS after the EGS has passed the frontend in // // entityId mode. // // So, the IOS receive a stringId request with a Eid not registered yet. // // Look in the user table, if we have the user, use the UID version // uint32 userId = client.getShortId()>>4; // TUserLanguagesContainer::const_iterator itUser = _UsersLanguages.find( userId ); // if ( itUser != _UsersLanguages.end() ) // { // requestString(userId, stringId); // return; // } // } // nlwarning("requestString : Client with eid %s is unknown (requesting string %u as [%s])", // client.toString().c_str(), // stringId, // str.toString().c_str()); // return; // } // // // LOG("Sending string %u as [%s] to client %s", stringId, str.toString().c_str(), client.toString().c_str()); // // // build the response message // NLMISC::CBitMemStream bmsOut; // GenericXmlMsgHeaderMngr.pushNameToStream( "STRING_MANAGER:STRING_RESP", bmsOut); // bmsOut.serial(stringId); // // Send in utf8 format to save bandwith // string strUtf8= str.toUtf8(); // bmsOut.serial(strUtf8); // // // send the message to Front End // NLNET::CMessage msgout( "IMPULS_CH_ID" ); // NLMISC::CEntityId destId = client; // uint8 channel = 1; // msgout.serial( destId ); // msgout.serial( channel ); // // msgout.serialBufferWithSize((uint8*)bmsOut.buffer(), bmsOut.length()); // NLNET::CUnifiedNetwork::getInstance()->send(TServiceId(charInfo->EntityId.getDynamicId()), msgout); //} void CStringManager::requestString(uint32 userId, uint32 stringId) { TServiceId frontendId; TUserLanguagesContainer::const_iterator itUser = _UsersLanguages.find( userId ); if ( itUser == _UsersLanguages.end() ) { // no user language, try to find a character breakable { const CInputOutputService::TIdToInfos &chars = IOS->getCharInfosCont(); CInputOutputService::TIdToInfos::const_iterator it(chars.lower_bound(CEntityId(RYZOMID::player, userId<<4))); if (it != chars.end()) { CEntityId eid = it->first; if (eid.getShortId()>>4 == userId) { // we found a character for this user, use it frontendId = TServiceId(eid.getDynamicId()); break; } } // if we are here, we did not found a character for this user nlwarning(" Invalid user id %u",userId); return; } } else { // we know the user frontendId = itUser->second.FrontEndId; } ucstring str = getString(stringId); LOG("Sending string %u as [%s] to user %u", stringId, str.toString().c_str(), userId); // build the response message NLMISC::CBitMemStream bmsOut; GenericXmlMsgHeaderMngr.pushNameToStream( "STRING_MANAGER:STRING_RESP", bmsOut); bmsOut.serial(stringId); // Send in utf8 format to save bandwidth string strUtf8= str.toUtf8(); bmsOut.serial(strUtf8); // send the message to Front End CMessage msgout( "IMPULSION_UID" ); msgout.serial( userId ); msgout.serialBufferWithSize((uint8*)bmsOut.buffer(), bmsOut.length()); NLNET::CUnifiedNetwork::getInstance()->send( frontendId, msgout ); nldebug( "IOSSM: Sent IMPULSION_UID to %hu (STRING_RESP)", frontendId.get() ); } void CStringManager::reload(NLMISC::CLog *log) { uint i; for(i=0; isecond; while (!phrase.Params.empty()) { delete phrase.Params.back(); phrase.Params.pop_back(); } _AllPhrases[i].erase(_AllPhrases[i].begin()); } _AllPhrases[i].clear(); for (j=0; jsecond; } else { // create a new entry std::pair ret; ret = _StringIdx.insert(std::make_pair(str, (uint32)_StringBase.size())); nlassert(ret.second); _StringBase.push_back(str); if (!_TestOnly) { // add the string in the cache file NLMISC::COFile file(_CacheFilename, true); LOGPARSE("Writing to cache [%u][%s]", ret.first->second, ret.first->first.toString().c_str()); file.serial(ret.first->second); ucstring temp = ret.first->first; file.serial(temp); } return ret.first->second; } } const ucstring &CStringManager::getString(uint32 stringId) { if (stringId < _StringBase.size()) return _StringBase[stringId]; else return _StringBase.front(); } uint32 CStringManager::translateShortName(uint32 shortNameIndex) { // No bot name translation on ring shards if (IsRingShard) return shortNameIndex; std::map::iterator it(_BotNameTranslation.find(shortNameIndex)); if (it != _BotNameTranslation.end()) { if (it->second != 0) // yeaa, we found a translation with a non empty short name return it->second; else // the translated name is empty, ignore the translated name return shortNameIndex; } else // no translation, return the same index. return shortNameIndex; return 0; } uint32 CStringManager::translateShortName(const ucstring &shortName) { // return translateShortName(storeString(shortName)); } uint32 CStringManager::translateTitle(const std::string &title, TLanguages language) { const std::string colName("name"); const CStringManager::CEntityWords &ew = getEntityWords(language, STRING_MANAGER::title); std::string rowName = NLMISC::toLower(title); uint32 stringId; stringId = ew.getStringId(rowName, colName); return stringId; } uint32 CStringManager::translateEventFaction(uint32 eventFactionId) { if (VerboseStringManager) nlinfo("Event faction translation asked for : '%s' (%u)", getString(eventFactionId).toString().c_str(), eventFactionId); if (eventFactionId == 0) return 0; std::map::iterator it = _EventFactionTranslation.find(eventFactionId); if (it != _EventFactionTranslation.end()) { if (VerboseStringManager) nlinfo("Found event faction translation : '%s' (%u)", getString(it->second).toString().c_str(), it->second); return it->second; } return 0; } uint32 CStringManager::translateEventFaction(const ucstring &eventFaction) { if (eventFaction.empty()) return 0; return translateEventFaction(storeString(eventFaction)); } /* * Send the requested string */ void CStringManager::sendString( uint32 nameIndex, TServiceId serviceId ) { CMessage msgout( "RECV_STRING" ); msgout.serial( nameIndex ); const ucstring& ucs = getString( nameIndex ); msgout.serial( const_cast(ucs) ); CUnifiedNetwork::getInstance()->send( serviceId, msgout ); // reply => not via mirror } /* * Send the names of all online entities */ void CStringManager::retrieveEntityNames( TServiceId serviceId ) { vector< pair > names; TEntityIdToEntityIndexMap::const_iterator itEntityIndex; for( itEntityIndex = TheDataset.entityBegin(); itEntityIndex != TheDataset.entityEnd(); ++itEntityIndex ) { TDataSetRow entityIndex = TheDataset.getCurrentDataSetRow( GET_ENTITY_INDEX(itEntityIndex) ); if ( entityIndex.isValid() ) { CMirrorPropValueRO nameIndex( TheDataset, entityIndex, DSPropertyNAME_STRING_ID ); if ( nameIndex() != 0 ) { names.push_back( make_pair( entityIndex, getString(nameIndex).toString() ) ); } } } NLNET::CMessage msgout( "ENTITY_NAMES" ); uint32 len = (uint32)names.size(); msgout.serial( len ); vector< pair >::const_iterator itn; for ( itn=names.begin(); itn!=names.end(); ++itn ) { msgout.serial( const_cast((*itn).first) ); msgout.serial( const_cast((*itn).second) ); } NLNET::CUnifiedNetwork::getInstance()->send( serviceId, msgout ); nldebug( "IOSSM: Sent %u names to service %hu", names.size(), serviceId.get() ); } void CStringManager::updateUserLanguage( uint32 userId, TServiceId frontEndId, const std::string & lang ) { CStringManager::TLanguages language = checkLanguageCode( lang ); SUserLanguageEntry entry( frontEndId, language ); TUserLanguagesContainer::iterator it = _UsersLanguages.find(userId); if (it == _UsersLanguages.end()) { _UsersLanguages.insert(make_pair(userId, entry)); } else { it->second.FrontEndId = frontEndId; it->second.Language = language; } // TODO : send cache time stamp to client. nldebug ("IOSSM: updateUserLanguage : set userId %u to front end %u using language code '%s'", userId, frontEndId.get(), SM->getLanguageCodeString(language).c_str()); // send back the cache time stamp info uint32 timestamp = SM->getCacheTimestamp(); // now, build the message for the client. NLMISC::CBitMemStream bmsOut; GenericXmlMsgHeaderMngr.pushNameToStream( "STRING_MANAGER:RELOAD_CACHE", bmsOut); bmsOut.serial(timestamp); // send the message to Front End NLNET::CMessage msgout( "IMPULSION_UID" ); msgout.serial(userId); msgout.serialBufferWithSize((uint8*)bmsOut.buffer(), bmsOut.length()); try { CUnifiedNetwork::getInstance()->send( frontEndId, msgout); } catch(const Exception& e) { nlwarning( "CStringManager::updateUserLanguage : Error : %s", e.what() ); } } /* * Replace a phrase in default language(s) (message handler) */ void CStringManager::setPhrase(NLNET::CMessage &message) { std::string phraseName; ucstring phraseContent; try { message.serial(phraseName); message.serial(phraseContent); } catch(const Exception& e) { nlwarning(" %s",e.what()); return; } setPhrase(phraseName, phraseContent); } /* * Replace a phrase in specified language (message handler) */ void CStringManager::setPhraseLang(NLNET::CMessage &message) { std::string phraseName; ucstring phraseContent; std::string langString; try { message.serial(phraseName); message.serial(phraseContent); message.serial(langString); } catch( Exception& e ) { nlwarning(" %s",e.what()); return; } TLanguages lang = checkLanguageCode(langString); setPhrase(phraseName, phraseContent, lang); } /* * Replace a phrase in default language(s) */ void CStringManager::setPhrase(std::string const& phraseName, ucstring const& phraseContent) { if (_DefaultSetPhraseLanguage==NB_LANGUAGES) for (int i=0; i &itemInfos) { // first, parse all the container to remove any previously user item with this aiInstance TRingUserItemInfos::iterator first(_RingUserItemInfos.begin()), last(_RingUserItemInfos.end()); // for each item having one or more translation... for (; first != last; ++first) { std::vector &items = first->second; // for each translation of this item... for (uint i=0; i1) return false; if(args.size()==1) { if(args[0]==string("on")||args[0]==string("ON")||args[0]==string("true")||args[0]==string("TRUE")||args[0]==string("1")) VerboseLog=true; if(args[0]==string("off")||args[0]==string("OFF")||args[0]==string("false")||args[0]==string("FALSE")||args[0]==string("0")) VerboseLog=false; } nlinfo("VerboseLogging is %s",VerboseLog?"ON":"OFF"); return true; } */ NLMISC_CATEGORISED_COMMAND(stringmanager, loadPhraseFile, "Merge a phrase file into string manager"," [/file]") { if (args.size() != 2) return false; std::string lang = args[0]; std::string file = args[1]; CStringManager::TLanguages language = SM->checkLanguageCode(lang); if (SM->getLanguageCodeString(language) != lang) { log.displayNL("Failed, language '%s' is not a valid language", lang.c_str()); return false; } if (!CFile::fileExists(file)) { if (!CFile::isDirectory(file)) { log.displayNL("Failed, path '%s' is not a valid file nor directory", file.c_str()); return false; } file = CPath::standardizePath(file)+"phrase_"+lang+".txt"; if (!CFile::fileExists(file)) { log.displayNL("Failed to locate default phrase file '%s'", file.c_str()); return false; } } SM->loadPhraseFile(file, language, "", &log); return true; } NLMISC_CATEGORISED_COMMAND(stringmanager, mergeWordFile, "Merge a word file into string manager"," [/file]") { if (args.size() != 3) return false; std::string lang = args[0]; std::string word = toLower(args[1]); std::string file = args[2]; // get language CStringManager::TLanguages language = SM->checkLanguageCode(lang); if (SM->getLanguageCodeString(language) != lang) { log.displayNL("Failed, language '%s' is not a valid language", lang.c_str()); return false; } // get word type CStringManager::TParameterTraitList typeNames = CStringManager::CParameterTraits::getParameterTraitsNames(); STRING_MANAGER::TParamType wordType; uint i; for (i=0; iReadTranslationWork; // We don't want diff with work file now SM->ReadTranslationWork = false; SM->mergeEntityWordsFile(file, language, wordType); SM->ReadTranslationWork = oldMode; return true; } NLMISC_CATEGORISED_COMMAND(stringmanager, displayEntityWords, "display entity words for a language and type"," [wordwildcard]") { if (args.size() < 2 || args.size() > 3) return false; std::string lang = args[0]; std::string word = toLower(args[1]); std::string wc; if (args.size() == 3) wc = args[2]; // get language CStringManager::TLanguages language = SM->checkLanguageCode(lang); if (SM->getLanguageCodeString(language) != lang) { log.displayNL("Failed, language '%s' is not a valid language", lang.c_str()); return false; } // get word type CStringManager::TParameterTraitList typeNames = CStringManager::CParameterTraits::getParameterTraitsNames(); STRING_MANAGER::TParamType wordType; uint i; for (i=0; idisplayEntityWords(language, wordType, wc, &log); return true; } NLMISC_CATEGORISED_COMMAND(stringmanager, setEntityWord, "set a word value","... ") { if (args.size() < 2 || args.size() > 5) return false; std::string path = args[0]; uint wi = 1; while (wi < args.size()-1) path += "."+args[wi++]; ucstring word(args[wi]); // get language SM->setEntityWord(path, word); return true; } NLMISC_CATEGORISED_COMMAND(stringmanager, loadBotNames, "load a bot names file","[bot names file] [reset bot names (0|1)]") { if (args.size() > 2) return false; std::string filename = "bot_names.txt"; bool resetBotnames = false; if (args.size() > 0) filename = args[0]; if (args.size() > 1) NLMISC::fromString(args[1], resetBotnames); SM->loadBotNames(filename, resetBotnames, &log); return true; } NLMISC_CATEGORISED_COMMAND(stringmanager, setBotName, "set a bot name"," ") { if (args.size() != 2) return false; ucstring botname, translation; botname.fromUtf8(args[0]); translation.fromUtf8(args[1]); SM->setBotName(botname, translation); return true; } NLMISC_CATEGORISED_COMMAND(stringmanager, readStringManagerRepository, "parse a whole repository with phrases and words (language is optional, none will load rep for all languages)"," [language code]") { if (args.size() < 1 || args.size() > 2) return false; string path = args[0]; if (args.size() == 2) { string lang = args[1]; CStringManager::TLanguages language = SM->checkLanguageCode(lang); if (SM->getLanguageCodeString(language) != lang) { log.displayNL("Failed, language '%s' is not a valid language", lang.c_str()); return false; } log.displayNL("Reading text repository '%s' for language '%s'", path.c_str(), lang.c_str()); SM->readRepository(path, language, &log); } else { log.displayNL("Reading text repository '%s' for all language", path.c_str()); uint i; for (i=1; ireadRepository(path, (CStringManager::TLanguages)i, &log); } } return true; } NLMISC_CATEGORISED_COMMAND(stringmanager, defaultSetPhraseLanguage, "Selects the language overriden by AIS messages","") { if (args.size() < 0 || args.size() > 1) return false; if (args.size()!=0) { CStringManager::TLanguages language = CStringManager::english; if (args[0]=="all") { language = CStringManager::NB_LANGUAGES; } else { language = SM->checkLanguageCode(args[0]); } SM->setDefaultSetPhraseLanguage(language); } CStringManager::TLanguages language = SM->getDefaultSetPhraseLanguage(); std::string languageCode; if (language==CStringManager::NB_LANGUAGES) languageCode = "all"; else languageCode = SM->getLanguageCodeString(language); log.displayNL("Language overriden by AIS messages is %s", languageCode.c_str()); return true; }