// 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_mgr_module.h" #include "nel/net/unified_network.h" #include "nel/net/module_message.h" #include "nel/net/module_socket.h" #include "nel/net/module_builder_parts.h" #include "nel/net/module_manager.h" #include "nel/misc/command.h" #include "nel/misc/path.h" #include "nel/misc/types_nl.h" #include "nel/misc/sstring.h" #include "game_share/synchronised_message.h" #include "game_share/send_chat.h" #include "game_share/ios_interface.h" using namespace NLNET; using namespace NLMISC; using namespace R2; using namespace std; NLNET_REGISTER_MODULE_FACTORY(CStringManagerModule, "StringManagerModule"); /** *Send a npc chat */ void sendChatGroup(TDataSetRow& sender,CChatGroup::TGroupType groupType,ucstring& sentence) { CMessage msg("NPC_CHAT_SENTENCE"); msg.serial(sender); msg.serialEnum(groupType); msg.serial(sentence); CUnifiedNetwork::getInstance()->send("IOS",msg); } void sendChatChannel(TDataSetRow& sender,TChanID& chanId,ucstring& ucsentence) { nldebug("NPC_CHAT_SENTENCE_CHANNEL"); CMessage msg("NPC_CHAT_SENTENCE_CHANNEL"); msg.serial(chanId); msg.serial(sender); msg.serial(ucsentence); CUnifiedNetwork::getInstance()->send("IOS",msg); } /** *Add a chat client to the EGS */ static void addClient(TDataSetRow id) { NLNET::CMessage msg("DYN_CHAT:ADD_CLIENT"); msg.serial(id); NLNET::CUnifiedNetwork::getInstance()->send("EGS",msg); } /** *Add a chat session for a channel in the EGS */ static void addSession(NLMISC::CEntityId id,TChanID chId) { static bool writeRight = true; NLNET::CMessage msg("DYN_CHAT:ADD_SESSION_ENTITY"); msg.serial(chId); msg.serial(id); msg.serial(writeRight); NLNET::CUnifiedNetwork::getInstance()->send("EGS",msg); } static void addSession(TDataSetRow id,TChanID chId) { static bool writeRight = true; NLNET::CMessage msg("DYN_CHAT:ADD_SESSION"); msg.serial(chId); msg.serial(id); msg.serial(writeRight); NLNET::CUnifiedNetwork::getInstance()->send("EGS",msg); } static void removeSession(TDataSetRow& id,TChanID& chId) { NLNET::CMessage msg("DYN_CHAT:REMOVE_SESSION"); msg.serial(chId); msg.serial(id); NLNET::CUnifiedNetwork::getInstance()->send("EGS",msg); } static void removeSession(NLMISC::CEntityId& id,TChanID& chId) { NLNET::CMessage msg("DYN_CHAT:REMOVE_SESSION_ENTITY"); msg.serial(chId); msg.serial(id); NLNET::CUnifiedNetwork::getInstance()->send("EGS",msg); } void CStringManagerModule::requestDsr( ucstring& name) { NLNET::CMessage msg("REQUEST_DSR"); msg.serial(name); NLNET::CUnifiedNetwork::getInstance()->send("IOS",msg); } CStringManagerModule::CStringManagerModule() { _AnimChan = false; } CStringManagerModule::~CStringManagerModule() { _ClientChannels.clear(); } void CStringManagerModule::init(NLNET::IModuleSocket* clientGW,CDynamicMapService* server) { this->plugModule(clientGW); } void CStringManagerModule::onModuleUp(NLNET::IModuleProxy *moduleProxy) { std::string moduleName = moduleProxy->getModuleClassName(); if( moduleName == "ClientEditionModule") { insertClient(moduleProxy,TChanID());; } } void CStringManagerModule::onModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {} /** *Insert a new client for the dss incarn chat *The param mId must be valid. The other ones could be changed later. */ void CStringManagerModule::insertClient(NLNET::IModuleProxy *moduleProxy,TChanID channelId) { ClientInfo* cInfo=new ClientInfo(); //cInfo.ChanId = channelId; cInfo->Proxy = moduleProxy; _ClientChannels.insert(std::pair(moduleProxy->getModuleProxyId(),cInfo)); } void CStringManagerModule::removeClient(NLNET::IModuleProxy *moduleProxy) { std::map::iterator found = _ClientChannels.find(moduleProxy->getModuleProxyId()); if (found != _ClientChannels.end()) { ClientInfo* c = found->second; delete c; } } void CStringManagerModule::onModuleDown(NLNET::IModuleProxy *moduleProxy) { std::string moduleName = moduleProxy->getModuleClassName(); if( moduleName == "ClientEditionModule") { //release the channels used by this client std::map::iterator found = _ClientChannels.find(moduleProxy->getModuleProxyId()); if( found!=_ClientChannels.end()) { delete found->second; _ClientChannels.erase(found); } } } bool CStringManagerModule::onProcessModuleMessage(NLNET::IModuleProxy *senderModuleProxy, const NLNET::CMessage &message) { nlassert(message.isReading()); std::string operationName = message.getName(); if(operationName == "registerTable") { registerTableRequested(message); } else if(operationName == "unregisterTable") { TSessionId scenarioId; nlRead(message, serial, scenarioId); unregisterTableRequested(scenarioId); } else if(operationName == "translateAndForward") { TDataSetRow senderId; CChatGroup::TGroupType groupType; std::string id; TSessionId scenarioId; nlRead(message, serial, senderId); nlRead(message, serialEnum, groupType); nlRead(message, serial, id); nlRead(message, serial, scenarioId); translateAndForward(senderId,groupType,id,scenarioId); } else if(operationName == "translateAndForwardArg") { TDataSetRow senderId; CChatGroup::TGroupType groupType; std::string id; uint32 i=0,size; float value; TSessionId scenarioId; nlRead(message, serial, senderId); nlRead(message, serialEnum, groupType); nlRead(message, serial, id); nlRead(message, serial, scenarioId); nlRead(message, serial, size); std::vector args; for(;i::const_iterator found = _ClientChannels.find(mId); if(found != _ClientChannels.end()) { sendTable(scenarioId,found->second->Proxy); } else { } } else if(operationName=="requestSetValue") { TSessionId scenarioId; std::string localId; std::string value; nlRead(message, serial, scenarioId); nlRead(message, serial, localId); nlRead(message, serial, value); setValue(scenarioId.asInt(),localId,value); } else if(operationName == "requestStringValue") { TSessionId scenarioId; std::string localId; TModuleId mId; nlRead(message, serial, scenarioId); nlRead(message, serial, mId); nlRead(message, serial, localId); map::const_iterator found = _ClientChannels.find(mId); if(found != _ClientChannels.end()) { sendStringValue(scenarioId,found->second->Proxy,localId); } } else if (operationName == "requestIdList") { TSessionId scenarioId; std::string eid; TModuleId mId; nlRead(message,serial,scenarioId); nlRead(message,serial,mId); map::const_iterator found = _ClientChannels.find(mId); if(found !=_ClientChannels.end()) { sendIdList(scenarioId,found->second->Proxy); } } else { nlwarning("No operation '%s' defined !",operationName.c_str()); nlassert(0); return false; } return true; } void CStringManagerModule::addAnimSession(TModuleId id,TSessionId scenarioId) { std::map::iterator found = _ClientChannels.find(id); if(found!=_ClientChannels.end()) { found->second->SessionId = scenarioId; uint32 charId; NLMISC::CEntityId clientEid; std::string userPriv; std::string extendedPriv; if (!getCharInfo(found->second->Proxy, charId, clientEid, userPriv, extendedPriv)) { nlwarning("R2Ed: module '%s' has Invalid Security Info", found->second->Proxy->getModuleName().c_str()); } else { addSession(clientEid,_AnimChans[scenarioId.asInt()]); } } } void CStringManagerModule::registerTableRequested(const CMessage& msgin) { TSessionId scenarioId; uint32 nbEntry; std::string id,text; nlRead(msgin,serial,scenarioId); { uint32 size; nlRead(msgin,serial,size); for(uint32 i=0;i >& entries) { std::string id,text; uint32 nbEntry = (uint32)entries.size(); releaseTable(sessionId.asInt()); for(uint i=0;i > entries; //verify that there is no table for this scenario CObject* textsTable = table->getAttr("Texts"); uint32 max = textsTable->getSize(); //fill the table //for each entry of the local table for(uint i=0;igetValue(i); CObject* tmp = text->getAttr("Id"); nlassert(tmp); std::string localId = tmp->toString(); tmp = text->getAttr("Text"); nlassert(tmp); std::string value = tmp->toString(); entries.push_back( std::make_pair(localId, value)); } registerTableRequested(sessionId, entries); } void CStringManagerModule::unregisterTableRequested(TSessionId sessionId) { releaseTable(sessionId.asInt()); if(_AnimChan ) { NLNET::CMessage msg("DYN_CHAT:REMOVE_SERVICE_CHAN"); msg.serial(_AnimChans[sessionId.asInt()]); NLNET::CUnifiedNetwork::getInstance()->send("EGS",msg); _AnimChans[sessionId.asInt()]=TChanID(); } } static std::string formatString(std::string str,std::vector args) { uint32 size = (uint32)args.size(); std::string ret=""; { std::string::size_type pos = 0; CSString cstring(str); for(uint i=0;i& args) { std::string text = getValue(sessionId.asInt(), id ); if (text!="") { std::string toSend = formatString(text,args); send(senderId,groupType,toSend); } } void CStringManagerModule::translateAndForward(TDataSetRow senderId,CChatGroup::TGroupType groupType,std::string id,TSessionId sessionId) { std::string toSend = getValue(sessionId.asInt(), id); if(toSend != "") send(senderId,groupType,toSend); } void CStringManagerModule::send(TDataSetRow& senderId,CChatGroup::TGroupType groupType,const std::string& toSend) { if(toSend != "") { ucstring uStr; uStr.fromUtf8(toSend); //for each client (animator) std::map::const_iterator first(_ClientChannels.begin()), last(_ClientChannels.end()); for (;first!=last;first++) { TChanID chanId = first->second->getIncarnation(senderId); if( !chanId.isUnknownId() ) { //ucstring tmp("{no_bubble}"+toSend); ucstring uStr2("{no_bubble}"); uStr2 += uStr; sendChatChannel(senderId,chanId,uStr2); return; } } //if nobody incarn the npc, send the chat normally sendChatGroup(senderId,groupType,uStr); } } void CStringManagerModule::talkAs(TModuleId& id, TDataSetRow& creaturRowId,std::string& name,TSessionId sessionId) { //looking for the client's channel std::map::iterator found= _ClientChannels.find(id); if (found == _ClientChannels.end()) { nlwarning("client unregistered! moduleId: %u",id); return; } found->second->SessionId = sessionId; //the player already incarn this npc if (found->second->getIncarnation(creaturRowId)!=TChanID())return; TChanID chanId = initChannel(name); found->second->addIncarnation(chanId,creaturRowId); } void CStringManagerModule::stopTalkAs(TModuleId& id,TDataSetRow& npcId) { std::map::iterator found= _ClientChannels.find(id); if(found != _ClientChannels.end()) { found->second->removeIncarnation(npcId); } } void CStringManagerModule::releaseChannels(TSessionId sessionId) { std::map::iterator first( _ClientChannels.begin()), last(_ClientChannels.end()); while(first != last) { if(first->second->SessionId==sessionId) { nldebug(" : found an animation module with sessionId %u",sessionId.asInt()); first->second->clear(); } ++first; } } void CStringManagerModule::onApplicationExit() { } TChanID CStringManagerModule::initChannel(std::string name,bool forwardInput) { TChanID channelId = CEntityId::getNewEntityId(RYZOMID::dynChatGroup); bool noBroadcast = false; static uint32 cpt=0; std::string chanName = "dssChan_" + toString(cpt); ++cpt; if(channelId == TChanID::Unknown) { nlwarning("error while creating id for dyn chat channel!"); return TChanID(); } { CMessage msg("DYN_CHAT:ADD_SERVICE_CHAN"); msg.serial(channelId); msg.serial(chanName); msg.serial(noBroadcast); msg.serial(forwardInput); CUnifiedNetwork::getInstance()->send("EGS",msg); } { bool hideBubble = true; CMessage msg("DYN_CHAT:SET_HIDE_BUBBLE"); msg.serial(channelId); msg.serial(hideBubble); CUnifiedNetwork::getInstance()->send("EGS",msg); } CIOSMsgSetPhrase(chanName,name).send(); return channelId; } //called when a DYN_CHAT:FORWARD message is received by the DSS void CStringManagerModule::forwardIncarnChat(TChanID id,TDataSetRow senderId,ucstring sentence) { nldebug("dyn chat '%s' in channel '%s'",sentence.c_str(),id.toString().c_str()); map::const_iterator first(_ClientChannels.begin()),last(_ClientChannels.end()); while(first != last) { //look for the npc registered in this channel TDataSetRow npcId = first->second->getIncarnation(id); //if the chat is not from this npc, it come from the player //and we must forward it if( (npcId.isValid()) && (npcId!=senderId) ) { static ucstring noBubble("{no_bubble}"); static ucstring::size_type noBubbleLen = noBubble.length(); CChatGroup::TGroupType groupType = CChatGroup::say; ucstring tmp = sentence; ucstring::size_type pos = tmp.find(noBubble); if (pos != ucstring::npos && pos == 0) { tmp = tmp.substr(pos + noBubbleLen); } sendChatGroup(npcId,groupType,tmp); return; } ++first; } nlwarning("unable to find npc incarnated !"); } void CStringManagerModule::sendIdList(TSessionId scenarioId,NLNET::IModuleProxy* senderModuleProxy) { TLocalTable* localTable = getLocalTable(scenarioId.asInt()); if(localTable != NULL) { TLocalTable::const_iterator first(localTable->begin()),last(localTable->end()); CMessage msg("idList"); uint32 nb = (uint32)localTable->size(); msg.serial(nb); for( ; first!=last; ++first) { std::string stringId = first->first ; msg.serial(stringId); } senderModuleProxy->sendModuleMessage(this,msg); } } void CStringManagerModule::sendStringValue(TSessionId scenarioId,NLNET::IModuleProxy* moduleProxy,std::string id) { std::string val = getValue(scenarioId.asInt(),id); CMessage msg("stringValue"); msg.serial(id); msg.serial(val); moduleProxy->sendModuleMessage(this,msg); } //send a local table to a client void CStringManagerModule::sendTable(TSessionId scenarioId,IModuleProxy *moduleProxy) { TLocalTable* localTable = getLocalTable(scenarioId.asInt()); if (localTable != NULL) { uint32 nb = (uint32)localTable->size(); if(nb==0)return; CMessage msg("stringTable"); //eid of the client requesting the string table //number of entries msg.serial(nb); std::map::const_iterator first(localTable->begin()), last(localTable->end()); while(first!=last) { std::string localId = first->first; std::string text = getValue(scenarioId.asInt(), localId); //local id msg.serial(localId); //string msg.serial(text); first++; } moduleProxy->sendModuleMessage(this,msg); } } ClientInfo::~ClientInfo() { clear(); } TDataSetRow ClientInfo::getIncarnation(TChanID chanId) { std::vector::iterator first(_Incarnations.begin()), last(_Incarnations.end()); while(first != last) { if((*first).ChanId == chanId) { return (*first).NpcId; } ++first; } return TDataSetRow(); } TChanID ClientInfo::getIncarnation(TDataSetRow npcId) { std::vector::iterator first(_Incarnations.begin()), last(_Incarnations.end()); while(first != last) { if((*first).NpcId == npcId) { return first->ChanId; } ++first; } return TChanID(); } bool ClientInfo::incarn(TDataSetRow npcId) { std::vector::iterator first(_Incarnations.begin()), last(_Incarnations.end()); while(first != last) { if((*first).NpcId == npcId) { return true; } ++first; } return false; } void ClientInfo::addIncarnation(TChanID chanId,TDataSetRow& npcId) { addClient(npcId); //add npc session to this channel addSession(npcId,chanId); uint32 charId; NLMISC::CEntityId eid; std::string userPriv; std::string extendedPriv; //add user session in this channel if(getCharInfo(Proxy, charId, eid, userPriv, extendedPriv)) { addSession(eid,chanId); } TIncarn incarn; incarn.ChanId = chanId; incarn.NpcId = npcId; _Incarnations.push_back(incarn); } //release all channels used by this client void ClientInfo::clear() { uint32 size = (uint32)_Incarnations.size(); for(uint32 i=0;i::iterator first(_Incarnations.begin()), last(_Incarnations.end()); for( ; first != last; ++first) { if((*first).NpcId == npcId) { first->release(); _Incarnations.erase(first); return; } } } void TIncarn::release() { NLNET::CMessage msg("DYN_CHAT:REMOVE_SERVICE_CHAN"); msg.serial(ChanId); NLNET::CUnifiedNetwork::getInstance()->send("EGS",msg); }