// 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/module_builder_parts.h" #include "nel/net/callback_server.h" #include "game_share/ryzom_entity_id.h" #include "server_share/chat_unifier_itf.h" #include "game_share/utils.h" #include "server_share/mysql_wrapper.h" #include "database_mapping.h" #include "character_sync.h" #include "entity_locator.h" #include "character_sync.h" using namespace std; using namespace NLMISC; using namespace NLNET; using namespace RSMGR; using namespace CHATUNI; using namespace ENTITYLOC; using namespace CHARSYNC; namespace CHATUNI { class CChatUnifierServer: public CEmptyModuleServiceBehav > >, public CChatUnifierSkel { // mysql ring database connection MSW::CConnection _RingDb; typedef uint32 TShardId; typedef CTwinMap TChatClients; // the chat clients TChatClients _ChatClients; // A vector of chat clients, ready for broadcast vector _ChatClientsForBroadcast; public: CChatUnifierServer() { CChatUnifierSkel::init(this); } static const std::string &getInitStringHelp() { static string help("ring_db(host= user= password= base=)"); return help; } bool initModule(const TParsedCommandLine &initInfo) { nldebug("CHATUNI : initModule with '%s'", initInfo.toString().c_str()); bool ret = CModuleBase::initModule(initInfo); // init ring db const TParsedCommandLine *initRingDb = initInfo.getParam("ring_db"); if (initRingDb == NULL) { nlwarning("RSM : missing ring db connection information"); return false; } // connect to the database if (!_RingDb.connect(*initRingDb)) { nlwarning("Failed to connect to database using %s", initRingDb->toString().c_str()); return false; } return ret; } void onModuleUp(IModuleProxy *proxy) { // if (proxy->getModuleClassName() == "EntityLocator" && proxy->getModuleDistance() == 0) // _EntityLocator = proxy->getLocalModule(); // look in the manifest to check for clients const std::string &manifest = proxy->getModuleManifest(); // parse the manifest. We are looking for "charUnifierClient(shardId=) TParsedCommandLine pcl; if (!pcl.parseParamList(manifest)) { // failed to parse the manifest, this module certainly is not one of our client return; } const TParsedCommandLine *shardIdParam = pcl.getParam("chatUnifierClient.shardId"); if (shardIdParam == NULL) { // not one of our client return; } // ok, we have a valid client, add it to the list uint32 shardId = atoi(shardIdParam->ParamValue.c_str()); // check that we don't have conflict const TModuleProxyPtr *pproxy = _ChatClients.getA(shardId); if (pproxy != NULL) { nlwarning("CChatUnifierServer:onModuleUp : module '%s' claims to be client for shard %u, but already have module '%s' for it, second ignored.", proxy->getModuleName().c_str(), shardId, (*pproxy)->getModuleName().c_str()); return; } nldebug("CChatUnifierServer:onModuleUp : added module '%s' as chat client for shard %u", proxy->getModuleName().c_str(), shardId); // ok, insert the new client _ChatClients.add(proxy, shardId); rebuildBroadcastList(); } void onModuleDown(IModuleProxy *proxy) { // if (proxy->getModuleDistance() == 0 && proxy->getLocalModule() == _EntityLocator) // _EntityLocator = NULL; // check if it's one of our clients const TShardId *pshardId = _ChatClients.getB(proxy); if (pshardId != NULL) { nldebug("CChatUnifierServer:onModuleDown : removing module '%s' as chat client for shard %u", proxy->getModuleName().c_str(), *pshardId); _ChatClients.removeWithA(proxy); rebuildBroadcastList(); } } // bool onProcessModuleMessage(IModuleProxy *sender, const CMessage &message) // { // if (CChatUnifierSkel::onDispatchMessage(sender, message)) // return true; // // nlwarning("CRingSessionManager : Unknown message '%s' received", message.getName().c_str()); // // return false; // } void rebuildBroadcastList() { _ChatClientsForBroadcast.clear(); _ChatClientsForBroadcast.reserve(_ChatClients.getAToBMap().size()); TChatClients::TAToBMap::const_iterator first(_ChatClients.getAToBMap().begin()), last(_ChatClients.getAToBMap().end()); for (; first != last; ++first) { _ChatClientsForBroadcast.push_back(first->first); } } ///////////////////////////////////////////////////////////// //// CChatUnifierSkel interface impl ///////////////////////////////////////////////////////////// // IOS forward a tell message to the unifier // If IOS can't find the player locally, it forward // the tell to the unifier void sendFarTell(NLNET::IModuleProxy *sender, const NLMISC::CEntityId &senderCharId, bool havePrivilege, const ucstring &destName, const ucstring &text) { nldebug("CU : sendFarTell : module '%s' send far tell '%s' from character %s to '%s'", sender->getModuleName().c_str(), text.toUtf8().c_str(), senderCharId.toString().c_str(), destName.toUtf8().c_str()); CChatUnifierClientProxy cucSender(sender); // ask the entity locator to find the addressee charId from it's name IEntityLocator *el = IEntityLocator::getInstance(); if (el == NULL) { // no entity locator, can't forward messages cucSender.recvFarTellFail(this, senderCharId, destName, TFailInfo::fi_no_entity_locator); nldebug("sendFarTell : no entity locator"); return; } uint32 hostShardId = el->getShardIdForChar(destName); if (hostShardId == 0) { // the character is not online cucSender.recvFarTellFail(this, senderCharId, destName, TFailInfo::fi_char_offline); nldebug("sendFarTell : no valid host shard id for addressee '%s'", destName.toUtf8().c_str()); return; } // This player is online, we can forward the tell to him // lookup in the IOS proxies const TModuleProxyPtr *pproxy = _ChatClients.getA(hostShardId); if (pproxy == NULL) { // no IOS for the hosting shard ! cucSender.recvFarTellFail(this, senderCharId, destName, TFailInfo::fi_no_ios_module); nldebug("sendFatTell : no module proxy for shard %u", hostShardId); return; } ICharacterSync *charSync = ICharacterSync::getInstance(); if (charSync == NULL) { // no character synchronizer to retrieve sender name ! cucSender.recvFarTellFail(this, senderCharId, destName, TFailInfo::fi_no_char_sync); nldebug("sendFarTell : can't finc character sync singleton"); return; } ucstring senderName = charSync->getCharacterName(uint32(senderCharId.getShortId())); if (senderName.empty()) { // no character synchronizer to retrieve sender name ! cucSender.recvFarTellFail(this, senderCharId, destName, TFailInfo::fi_sender_char_unknown); nldebug("sendFarTell : can't get character name from sender char id %s", senderCharId.toString().c_str()); return; } // ok, we can send the far tell CChatUnifierClientProxy cucDest(*pproxy); cucDest.recvFarTell(this, senderCharId, senderName, havePrivilege, destName, text); } // IOS forward a guild chat message to the unifier // void sendFarGuildChat(NLNET::IModuleProxy *sender, const NLMISC::CEntityId &senderCharId, uint32 guildId, const ucstring &text) // { // nldebug("CU : sendFarGuildChat : module '%s' send far guild chat '%s' from character %s to guild %u chat", // sender->getModuleName().c_str(), // text.toUtf8().c_str(), // senderCharId.toString().c_str(), // guildId); // // // // retrieve the sender name // ICharacterSync *charSync = ICharacterSync::getInstance(); // // if (charSync == NULL) // { // // no character synchronizer to retrieve sender name ! // return; // } // // ucstring senderName = charSync->getCharacterName(uint32(senderCharId.getShortId())); // // if (senderName.empty()) // { // // no character synchronizer to retrieve sender name ! // return; // } // // // ok, we can send the guild message // // broadcast the message to all the IOS client we known. // CChatUnifierClientProxy::broadcast_recvFarGuildChat(_ChatClientsForBroadcast.begin(), _ChatClientsForBroadcast.end(), this, senderName, guildId, text); // } // IOS forward a guild chat message to the unifier // This version use a translated string name as chat content // void sendFarGuildChat2(NLNET::IModuleProxy *sender, const NLMISC::CEntityId &senderCharId, uint32 guildId, const ucstring &phraseName) // { // nldebug("CU : sendFarGuildChat2 : module '%s' send far guild chat phrase '%s' from character %s to guild %u chat", // sender->getModuleName().c_str(), // phraseName.toUtf8().c_str(), // senderCharId.toString().c_str(), // guildId); // // // // retrieve the sender name // ICharacterSync *charSync = ICharacterSync::getInstance(); // // if (charSync == NULL) // { // // no character synchronizer to retrieve sender name ! // return; // } // // ucstring senderName = charSync->getCharacterName(uint32(senderCharId.getShortId())); // // if (senderName.empty()) // { // // no character synchronizer to retrieve sender name ! // return; // } // // // ok, we can send the guild message // // broadcast the message to all the IOS client we known. // CChatUnifierClientProxy::broadcast_recvFarGuildChat2(_ChatClientsForBroadcast.begin(), _ChatClientsForBroadcast.end(), this, senderName, guildId, phraseName); // } // IOS forward a guild chat message to the unifier // This version use a translated string ID as chat content // void sendFarGuildChat2Ex(NLNET::IModuleProxy *sender, const NLMISC::CEntityId &senderCharId, uint32 guildId, uint32 phraseId) // { // nldebug("CU : sendFarGuildChat2Ex : module '%s' send far guild chat phrase Id '%s' from character %s to guild %u chat", // sender->getModuleName().c_str(), // phraseId, // senderCharId.toString().c_str(), // guildId); // // // // retrieve the sender name // ICharacterSync *charSync = ICharacterSync::getInstance(); // // if (charSync == NULL) // { // // no character synchronizer to retrieve sender name ! // return; // } // // ucstring senderName = charSync->getCharacterName(uint32(senderCharId.getShortId())); // // if (senderName.empty()) // { // // no character synchronizer to retrieve sender name ! // return; // } // // // ok, we can send the guild message // // broadcast the message to all the IOS client we known. // CChatUnifierClientProxy::broadcast_recvFarGuildChat2Ex(_ChatClientsForBroadcast.begin(), _ChatClientsForBroadcast.end(), this, senderName, guildId, phraseId); // } ///////////////////////////////////////////////////////////// //// commands ///////////////////////////////////////////////////////////// NLMISC_COMMAND_HANDLER_TABLE_EXTEND_BEGIN(CChatUnifierServer, CModuleBase) NLMISC_COMMAND_HANDLER_ADD(CChatUnifierServer, dump, "dump the module internal state", "no param"); NLMISC_COMMAND_HANDLER_TABLE_END NLMISC_CLASS_COMMAND_DECL(dump) { // call the base class dump NLMISC_CLASS_COMMAND_CALL_BASE(CModuleBase, dump); log.displayNL("-----------------------------------"); log.displayNL("Dumping chat unifier server state :"); log.displayNL("-----------------------------------"); log.displayNL("List of %u chat unifier client :", _ChatClients.getAToBMap().size()); TChatClients::TAToBMap::const_iterator first(_ChatClients.getAToBMap().begin()), last(_ChatClients.getAToBMap().end()); for (; first != last; ++first) { log.displayNL(" Module '%s' for shard %u", first->first->getModuleName().c_str(), first->second); } log.displayNL("-----------------------------------"); return true; } }; NLNET_REGISTER_MODULE_FACTORY(CChatUnifierServer, "ChatUnifierServer"); } // namespace RSMGR // force module linking void forceChatUnifierLink() { }