diff --git a/code/ryzom/common/src/game_share/game_share.vcproj b/code/ryzom/common/src/game_share/game_share.vcproj index 3e699d301..9c336674c 100644 --- a/code/ryzom/common/src/game_share/game_share.vcproj +++ b/code/ryzom/common/src/game_share/game_share.vcproj @@ -86,75 +86,6 @@ Name="VCPostBuildEventTool" /> - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + diff --git a/code/ryzom/common/src/game_share/ring_session_manager_itf.h b/code/ryzom/common/src/game_share/ring_session_manager_itf.h index b03471403..6530ce008 100644 --- a/code/ryzom/common/src/game_share/ring_session_manager_itf.h +++ b/code/ryzom/common/src/game_share/ring_session_manager_itf.h @@ -25,7 +25,7 @@ #include "nel/net/login_cookie.h" -#include "../../nelns/welcome_service/welcome_service_itf.h" +#include "game_share/welcome_service_itf.h" #include "game_share/character_sync_itf.h" diff --git a/code/ryzom/common/src/game_share/welcome_service_itf.cpp b/code/ryzom/common/src/game_share/welcome_service_itf.cpp new file mode 100644 index 000000000..a8fdaea87 --- /dev/null +++ b/code/ryzom/common/src/game_share/welcome_service_itf.cpp @@ -0,0 +1,458 @@ +// 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" + +///////////////////////////////////////////////////////////////// +// WARNING : this is a generated file, don't change it ! +///////////////////////////////////////////////////////////////// + +#include "welcome_service_itf.h" + +namespace WS +{ + + ///////////////////////////////////////////////////////////////// + // WARNING : this is a generated file, don't change it ! + ///////////////////////////////////////////////////////////////// + + + const CWelcomeServiceSkel::TMessageHandlerMap &CWelcomeServiceSkel::getMessageHandlers() const + { + static TMessageHandlerMap handlers; + static bool init = false; + + if (!init) + { + std::pair < TMessageHandlerMap::iterator, bool > res; + + res = handlers.insert(std::make_pair(std::string("WU"), &CWelcomeServiceSkel::welcomeUser_skel)); + // if this assert, you have a doubly message name in your interface definition ! + nlassert(res.second); + + res = handlers.insert(std::make_pair(std::string("DU"), &CWelcomeServiceSkel::disconnectUser_skel)); + // if this assert, you have a doubly message name in your interface definition ! + nlassert(res.second); + + init = true; + } + + return handlers; + } + bool CWelcomeServiceSkel::fwdOnProcessModuleMessage(NLNET::IModuleProxy *sender, const NLNET::CMessage &message) + { + const TMessageHandlerMap &mh = getMessageHandlers(); + + TMessageHandlerMap::const_iterator it(mh.find(message.getName())); + + if (it == mh.end()) + { + return false; + } + + TMessageHandler cmd = it->second; + (this->*cmd)(sender, message); + + return true; + } + + + void CWelcomeServiceSkel::welcomeUser_skel(NLNET::IModuleProxy *sender, const NLNET::CMessage &__message) + { + H_AUTO(CWelcomeServiceSkel_welcomeUser_WU); + uint32 charId; + nlRead(__message, serial, charId); + std::string userName; + nlRead(__message, serial, userName); + NLNET::CLoginCookie cookie; + nlRead(__message, serial, cookie); + std::string priviledge; + nlRead(__message, serial, priviledge); + std::string exPriviledge; + nlRead(__message, serial, exPriviledge); + WS::TUserRole mode; + nlRead(__message, serial, mode); + uint32 instanceId; + nlRead(__message, serial, instanceId); + welcomeUser(sender, charId, userName, cookie, priviledge, exPriviledge, mode, instanceId); + } + + void CWelcomeServiceSkel::disconnectUser_skel(NLNET::IModuleProxy *sender, const NLNET::CMessage &__message) + { + H_AUTO(CWelcomeServiceSkel_disconnectUser_DU); + uint32 userId; + nlRead(__message, serial, userId); + disconnectUser(sender, userId); + } + // ask the welcome service to welcome a character + void CWelcomeServiceProxy::welcomeUser(NLNET::IModule *sender, uint32 charId, const std::string &userName, const NLNET::CLoginCookie &cookie, const std::string &priviledge, const std::string &exPriviledge, WS::TUserRole mode, uint32 instanceId) + { + if (_LocalModuleSkel && _LocalModule->isImmediateDispatchingSupported()) + { + // immediate local synchronous dispatching + _LocalModuleSkel->welcomeUser(_ModuleProxy->getModuleGateway()->getPluggedModuleProxy(sender), charId, userName, cookie, priviledge, exPriviledge, mode, instanceId); + } + else + { + // send the message for remote dispatching and execution or local queing + NLNET::CMessage __message; + + buildMessageFor_welcomeUser(__message, charId, userName, cookie, priviledge, exPriviledge, mode, instanceId); + + _ModuleProxy->sendModuleMessage(sender, __message); + } + } + // ask the welcome service to disconnect a user + void CWelcomeServiceProxy::disconnectUser(NLNET::IModule *sender, uint32 userId) + { + if (_LocalModuleSkel && _LocalModule->isImmediateDispatchingSupported()) + { + // immediate local synchronous dispatching + _LocalModuleSkel->disconnectUser(_ModuleProxy->getModuleGateway()->getPluggedModuleProxy(sender), userId); + } + else + { + // send the message for remote dispatching and execution or local queing + NLNET::CMessage __message; + + buildMessageFor_disconnectUser(__message, userId); + + _ModuleProxy->sendModuleMessage(sender, __message); + } + } + + // Message serializer. Return the message received in reference for easier integration + const NLNET::CMessage &CWelcomeServiceProxy::buildMessageFor_welcomeUser(NLNET::CMessage &__message, uint32 charId, const std::string &userName, const NLNET::CLoginCookie &cookie, const std::string &priviledge, const std::string &exPriviledge, WS::TUserRole mode, uint32 instanceId) + { + __message.setType("WU"); + nlWrite(__message, serial, charId); + nlWrite(__message, serial, const_cast < std::string& > (userName)); + nlWrite(__message, serial, const_cast < NLNET::CLoginCookie& > (cookie)); + nlWrite(__message, serial, const_cast < std::string& > (priviledge)); + nlWrite(__message, serial, const_cast < std::string& > (exPriviledge)); + nlWrite(__message, serial, mode); + nlWrite(__message, serial, instanceId); + + + return __message; + } + + // Message serializer. Return the message received in reference for easier integration + const NLNET::CMessage &CWelcomeServiceProxy::buildMessageFor_disconnectUser(NLNET::CMessage &__message, uint32 userId) + { + __message.setType("DU"); + nlWrite(__message, serial, userId); + + + return __message; + } + + ///////////////////////////////////////////////////////////////// + // WARNING : this is a generated file, don't change it ! + ///////////////////////////////////////////////////////////////// + + + const CLoginServiceSkel::TMessageHandlerMap &CLoginServiceSkel::getMessageHandlers() const + { + static TMessageHandlerMap handlers; + static bool init = false; + + if (!init) + { + std::pair < TMessageHandlerMap::iterator, bool > res; + + res = handlers.insert(std::make_pair(std::string("PUL"), &CLoginServiceSkel::pendingUserLost_skel)); + // if this assert, you have a doubly message name in your interface definition ! + nlassert(res.second); + + init = true; + } + + return handlers; + } + bool CLoginServiceSkel::fwdOnProcessModuleMessage(NLNET::IModuleProxy *sender, const NLNET::CMessage &message) + { + const TMessageHandlerMap &mh = getMessageHandlers(); + + TMessageHandlerMap::const_iterator it(mh.find(message.getName())); + + if (it == mh.end()) + { + return false; + } + + TMessageHandler cmd = it->second; + (this->*cmd)(sender, message); + + return true; + } + + + void CLoginServiceSkel::pendingUserLost_skel(NLNET::IModuleProxy *sender, const NLNET::CMessage &__message) + { + H_AUTO(CLoginServiceSkel_pendingUserLost_PUL); + NLNET::CLoginCookie cookie; + nlRead(__message, serial, cookie); + pendingUserLost(sender, cookie); + } + // An awaited user did not connect before the allowed timeout expire + void CLoginServiceProxy::pendingUserLost(NLNET::IModule *sender, const NLNET::CLoginCookie &cookie) + { + if (_LocalModuleSkel && _LocalModule->isImmediateDispatchingSupported()) + { + // immediate local synchronous dispatching + _LocalModuleSkel->pendingUserLost(_ModuleProxy->getModuleGateway()->getPluggedModuleProxy(sender), cookie); + } + else + { + // send the message for remote dispatching and execution or local queing + NLNET::CMessage __message; + + buildMessageFor_pendingUserLost(__message, cookie); + + _ModuleProxy->sendModuleMessage(sender, __message); + } + } + + // Message serializer. Return the message received in reference for easier integration + const NLNET::CMessage &CLoginServiceProxy::buildMessageFor_pendingUserLost(NLNET::CMessage &__message, const NLNET::CLoginCookie &cookie) + { + __message.setType("PUL"); + nlWrite(__message, serial, const_cast < NLNET::CLoginCookie& > (cookie)); + + + return __message; + } + + ///////////////////////////////////////////////////////////////// + // WARNING : this is a generated file, don't change it ! + ///////////////////////////////////////////////////////////////// + + + const CWelcomeServiceClientSkel::TMessageHandlerMap &CWelcomeServiceClientSkel::getMessageHandlers() const + { + static TMessageHandlerMap handlers; + static bool init = false; + + if (!init) + { + std::pair < TMessageHandlerMap::iterator, bool > res; + + res = handlers.insert(std::make_pair(std::string("RWS"), &CWelcomeServiceClientSkel::registerWS_skel)); + // if this assert, you have a doubly message name in your interface definition ! + nlassert(res.second); + + res = handlers.insert(std::make_pair(std::string("RWSOS"), &CWelcomeServiceClientSkel::reportWSOpenState_skel)); + // if this assert, you have a doubly message name in your interface definition ! + nlassert(res.second); + + res = handlers.insert(std::make_pair(std::string("WUR"), &CWelcomeServiceClientSkel::welcomeUserResult_skel)); + // if this assert, you have a doubly message name in your interface definition ! + nlassert(res.second); + + res = handlers.insert(std::make_pair(std::string("UCP"), &CWelcomeServiceClientSkel::updateConnectedPlayerCount_skel)); + // if this assert, you have a doubly message name in your interface definition ! + nlassert(res.second); + + init = true; + } + + return handlers; + } + bool CWelcomeServiceClientSkel::fwdOnProcessModuleMessage(NLNET::IModuleProxy *sender, const NLNET::CMessage &message) + { + const TMessageHandlerMap &mh = getMessageHandlers(); + + TMessageHandlerMap::const_iterator it(mh.find(message.getName())); + + if (it == mh.end()) + { + return false; + } + + TMessageHandler cmd = it->second; + (this->*cmd)(sender, message); + + return true; + } + + + void CWelcomeServiceClientSkel::registerWS_skel(NLNET::IModuleProxy *sender, const NLNET::CMessage &__message) + { + H_AUTO(CWelcomeServiceClientSkel_registerWS_RWS); + uint32 shardId; + nlRead(__message, serial, shardId); + uint32 fixedSessionId; + nlRead(__message, serial, fixedSessionId); + bool isOnline; + nlRead(__message, serial, isOnline); + registerWS(sender, shardId, fixedSessionId, isOnline); + } + + void CWelcomeServiceClientSkel::reportWSOpenState_skel(NLNET::IModuleProxy *sender, const NLNET::CMessage &__message) + { + H_AUTO(CWelcomeServiceClientSkel_reportWSOpenState_RWSOS); + bool isOnline; + nlRead(__message, serial, isOnline); + reportWSOpenState(sender, isOnline); + } + + void CWelcomeServiceClientSkel::welcomeUserResult_skel(NLNET::IModuleProxy *sender, const NLNET::CMessage &__message) + { + H_AUTO(CWelcomeServiceClientSkel_welcomeUserResult_WUR); + uint32 userId; + nlRead(__message, serial, userId); + bool ok; + nlRead(__message, serial, ok); + std::string shardAddr; + nlRead(__message, serial, shardAddr); + std::string errorMsg; + nlRead(__message, serial, errorMsg); + welcomeUserResult(sender, userId, ok, shardAddr, errorMsg); + } + + void CWelcomeServiceClientSkel::updateConnectedPlayerCount_skel(NLNET::IModuleProxy *sender, const NLNET::CMessage &__message) + { + H_AUTO(CWelcomeServiceClientSkel_updateConnectedPlayerCount_UCP); + uint32 nbOnlinePlayers; + nlRead(__message, serial, nbOnlinePlayers); + uint32 nbPendingPlayers; + nlRead(__message, serial, nbPendingPlayers); + updateConnectedPlayerCount(sender, nbOnlinePlayers, nbPendingPlayers); + } + // Register the welcome service in the ring session manager + // The provided sessionId will be non-zero only for a shard with a fixed sessionId + void CWelcomeServiceClientProxy::registerWS(NLNET::IModule *sender, uint32 shardId, uint32 fixedSessionId, bool isOnline) + { + if (_LocalModuleSkel && _LocalModule->isImmediateDispatchingSupported()) + { + // immediate local synchronous dispatching + _LocalModuleSkel->registerWS(_ModuleProxy->getModuleGateway()->getPluggedModuleProxy(sender), shardId, fixedSessionId, isOnline); + } + else + { + // send the message for remote dispatching and execution or local queing + NLNET::CMessage __message; + + buildMessageFor_registerWS(__message, shardId, fixedSessionId, isOnline); + + _ModuleProxy->sendModuleMessage(sender, __message); + } + } + // WS report it's current open state + void CWelcomeServiceClientProxy::reportWSOpenState(NLNET::IModule *sender, bool isOnline) + { + if (_LocalModuleSkel && _LocalModule->isImmediateDispatchingSupported()) + { + // immediate local synchronous dispatching + _LocalModuleSkel->reportWSOpenState(_ModuleProxy->getModuleGateway()->getPluggedModuleProxy(sender), isOnline); + } + else + { + // send the message for remote dispatching and execution or local queing + NLNET::CMessage __message; + + buildMessageFor_reportWSOpenState(__message, isOnline); + + _ModuleProxy->sendModuleMessage(sender, __message); + } + } + // return for welcome user + void CWelcomeServiceClientProxy::welcomeUserResult(NLNET::IModule *sender, uint32 userId, bool ok, const std::string &shardAddr, const std::string &errorMsg) + { + if (_LocalModuleSkel && _LocalModule->isImmediateDispatchingSupported()) + { + // immediate local synchronous dispatching + _LocalModuleSkel->welcomeUserResult(_ModuleProxy->getModuleGateway()->getPluggedModuleProxy(sender), userId, ok, shardAddr, errorMsg); + } + else + { + // send the message for remote dispatching and execution or local queing + NLNET::CMessage __message; + + buildMessageFor_welcomeUserResult(__message, userId, ok, shardAddr, errorMsg); + + _ModuleProxy->sendModuleMessage(sender, __message); + } + } + // transmits the current player counts + void CWelcomeServiceClientProxy::updateConnectedPlayerCount(NLNET::IModule *sender, uint32 nbOnlinePlayers, uint32 nbPendingPlayers) + { + if (_LocalModuleSkel && _LocalModule->isImmediateDispatchingSupported()) + { + // immediate local synchronous dispatching + _LocalModuleSkel->updateConnectedPlayerCount(_ModuleProxy->getModuleGateway()->getPluggedModuleProxy(sender), nbOnlinePlayers, nbPendingPlayers); + } + else + { + // send the message for remote dispatching and execution or local queing + NLNET::CMessage __message; + + buildMessageFor_updateConnectedPlayerCount(__message, nbOnlinePlayers, nbPendingPlayers); + + _ModuleProxy->sendModuleMessage(sender, __message); + } + } + + // Message serializer. Return the message received in reference for easier integration + const NLNET::CMessage &CWelcomeServiceClientProxy::buildMessageFor_registerWS(NLNET::CMessage &__message, uint32 shardId, uint32 fixedSessionId, bool isOnline) + { + __message.setType("RWS"); + nlWrite(__message, serial, shardId); + nlWrite(__message, serial, fixedSessionId); + nlWrite(__message, serial, isOnline); + + + return __message; + } + + // Message serializer. Return the message received in reference for easier integration + const NLNET::CMessage &CWelcomeServiceClientProxy::buildMessageFor_reportWSOpenState(NLNET::CMessage &__message, bool isOnline) + { + __message.setType("RWSOS"); + nlWrite(__message, serial, isOnline); + + + return __message; + } + + // Message serializer. Return the message received in reference for easier integration + const NLNET::CMessage &CWelcomeServiceClientProxy::buildMessageFor_welcomeUserResult(NLNET::CMessage &__message, uint32 userId, bool ok, const std::string &shardAddr, const std::string &errorMsg) + { + __message.setType("WUR"); + nlWrite(__message, serial, userId); + nlWrite(__message, serial, ok); + nlWrite(__message, serial, const_cast < std::string& > (shardAddr)); + nlWrite(__message, serial, const_cast < std::string& > (errorMsg)); + + + return __message; + } + + // Message serializer. Return the message received in reference for easier integration + const NLNET::CMessage &CWelcomeServiceClientProxy::buildMessageFor_updateConnectedPlayerCount(NLNET::CMessage &__message, uint32 nbOnlinePlayers, uint32 nbPendingPlayers) + { + __message.setType("UCP"); + nlWrite(__message, serial, nbOnlinePlayers); + nlWrite(__message, serial, nbPendingPlayers); + + + return __message; + } + +} diff --git a/code/ryzom/common/src/game_share/welcome_service_itf.h b/code/ryzom/common/src/game_share/welcome_service_itf.h new file mode 100644 index 000000000..81a498a06 --- /dev/null +++ b/code/ryzom/common/src/game_share/welcome_service_itf.h @@ -0,0 +1,541 @@ + +///////////////////////////////////////////////////////////////// +// WARNING : this is a generated file, don't change it ! +///////////////////////////////////////////////////////////////// + +#ifndef WELCOME_SERVICE_ITF +#define WELCOME_SERVICE_ITF +#include "nel/misc/types_nl.h" +#ifdef NL_COMP_VC8 + #include +#endif +#include "nel/misc/hierarchical_timer.h" +#include "nel/misc/string_conversion.h" +#include "nel/net/message.h" +#include "nel/net/module.h" +#include "nel/net/module_builder_parts.h" +#include "nel/net/module_message.h" +#include "nel/net/module_gateway.h" + +#include "nel/net/login_cookie.h" + +namespace WS +{ + + + + struct TUserRole + { + enum TValues + { + ur_player, + ur_editor, + ur_animator, + /// the highest valid value in the enum + last_enum_item = ur_animator, + /// a value equal to the last enum item +1 + end_of_enum, + + invalid_val, + + /// Number of enumerated values + nb_enum_items = 3 + }; + + /// Index table to convert enum value to linear index table + const std::map &getIndexTable() const + { + static std::map indexTable; + static bool init = false; + if (!init) + { + // fill the index table + indexTable.insert(std::make_pair(ur_player, 0)); + indexTable.insert(std::make_pair(ur_editor, 1)); + indexTable.insert(std::make_pair(ur_animator, 2)); + + init = true; + } + + return indexTable; + } + + + static const NLMISC::CStringConversion &getConversionTable() + { + NL_BEGIN_STRING_CONVERSION_TABLE(TValues) + NL_STRING_CONVERSION_TABLE_ENTRY(ur_player) + NL_STRING_CONVERSION_TABLE_ENTRY(ur_editor) + NL_STRING_CONVERSION_TABLE_ENTRY(ur_animator) + NL_STRING_CONVERSION_TABLE_ENTRY(invalid_val) + }; + static NLMISC::CStringConversion + conversionTable(TValues_nl_string_conversion_table, sizeof(TValues_nl_string_conversion_table) + / sizeof(TValues_nl_string_conversion_table[0]), invalid_val); + + return conversionTable; + } + + TValues _Value; + + public: + TUserRole() + : _Value(invalid_val) + { + } + TUserRole(TValues value) + : _Value(value) + { + } + + TUserRole(const std::string &str) + { + _Value = getConversionTable().fromString(str); + } + + void serial(NLMISC::IStream &s) + { + s.serialEnum(_Value); + } + + bool operator == (const TUserRole &other) const + { + return _Value == other._Value; + } + bool operator != (const TUserRole &other) const + { + return ! (_Value == other._Value); + } + bool operator < (const TUserRole &other) const + { + return _Value < other._Value; + } + + bool operator <= (const TUserRole &other) const + { + return _Value <= other._Value; + } + + bool operator > (const TUserRole &other) const + { + return !(_Value <= other._Value); + } + bool operator >= (const TUserRole &other) const + { + return !(_Value < other._Value); + } + + const std::string &toString() const + { + return getConversionTable().toString(_Value); + } + static const std::string &toString(TValues value) + { + return getConversionTable().toString(value); + } + + TValues getValue() const + { + return _Value; + } + + // return true if the actual value of the enum is valid, otherwise false + bool isValid() + { + if (_Value == invalid_val) + return false; + + // not invalid, check other enum value + return getConversionTable().isValid(_Value); + } + + + uint32 asIndex() + { + std::map::const_iterator it(getIndexTable().find(_Value)); + nlassert(it != getIndexTable().end()); + return it->second; + } + + }; + + ///////////////////////////////////////////////////////////////// + // WARNING : this is a generated file, don't change it ! + ///////////////////////////////////////////////////////////////// + class CWelcomeServiceSkel + { + public: + /// the interceptor type + typedef NLNET::CInterceptorForwarder < CWelcomeServiceSkel> TInterceptor; + protected: + CWelcomeServiceSkel() + { + // do early run time check for message table + getMessageHandlers(); + } + virtual ~CWelcomeServiceSkel() + { + } + + void init(NLNET::IModule *module) + { + _Interceptor.init(this, module); + } + + // unused interceptors + std::string fwdBuildModuleManifest() const { return std::string(); } + void fwdOnModuleUp(NLNET::IModuleProxy * /* moduleProxy */) {} + void fwdOnModuleDown(NLNET::IModuleProxy * /* moduleProxy */) {} + void fwdOnModuleSecurityChange(NLNET::IModuleProxy * /* moduleProxy */) {} + + // process module message interceptor + bool fwdOnProcessModuleMessage(NLNET::IModuleProxy *sender, const NLNET::CMessage &message); + private: + + typedef void (CWelcomeServiceSkel::*TMessageHandler)(NLNET::IModuleProxy *sender, const NLNET::CMessage &message); + typedef std::map TMessageHandlerMap; + + const TMessageHandlerMap &getMessageHandlers() const; + + + void welcomeUser_skel(NLNET::IModuleProxy *sender, const NLNET::CMessage &__message); + + void disconnectUser_skel(NLNET::IModuleProxy *sender, const NLNET::CMessage &__message); + + // declare one interceptor member of the skeleton + TInterceptor _Interceptor; + + // declare the interceptor forwarder as friend of this class + friend class NLNET::CInterceptorForwarder < CWelcomeServiceSkel>; + public: + ///////////////////////////////////////////////////////////////// + // WARNING : this is a generated file, don't change it ! + ///////////////////////////////////////////////////////////////// + + // ask the welcome service to welcome a character + virtual void welcomeUser(NLNET::IModuleProxy *sender, uint32 charId, const std::string &userName, const NLNET::CLoginCookie &cookie, const std::string &priviledge, const std::string &exPriviledge, WS::TUserRole mode, uint32 instanceId) =0; + // ask the welcome service to disconnect a user + virtual void disconnectUser(NLNET::IModuleProxy *sender, uint32 userId) =0; + + + }; + + ///////////////////////////////////////////////////////////////// + // WARNING : this is a generated file, don't change it ! + ///////////////////////////////////////////////////////////////// + class CWelcomeServiceProxy + { + /// Smart pointer on the module proxy + NLNET::TModuleProxyPtr _ModuleProxy; + + // Pointer on the local module that implement the interface (if the proxy is for a local module) + NLNET::TModulePtr _LocalModule; + // Direct pointer on the server implementation interface for collocated module + CWelcomeServiceSkel *_LocalModuleSkel; + + + public: + CWelcomeServiceProxy(NLNET::IModuleProxy *proxy) + { + nlassert(proxy->getModuleClassName() == "WelcomeService"); + _ModuleProxy = proxy; + + // initialize collocated servant interface + if (proxy->getModuleDistance() == 0) + { + _LocalModule = proxy->getLocalModule(); + nlassert(_LocalModule != NULL); + CWelcomeServiceSkel::TInterceptor *interceptor = NULL; + interceptor = static_cast < NLNET::CModuleBase* >(_LocalModule.getPtr())->getInterceptor(interceptor); + nlassert(interceptor != NULL); + + _LocalModuleSkel = interceptor->getParent(); + nlassert(_LocalModuleSkel != NULL); + } + else + _LocalModuleSkel = 0; + + } + virtual ~CWelcomeServiceProxy() + { + } + + NLNET::IModuleProxy *getModuleProxy() + { + return _ModuleProxy; + } + + // ask the welcome service to welcome a character + void welcomeUser(NLNET::IModule *sender, uint32 charId, const std::string &userName, const NLNET::CLoginCookie &cookie, const std::string &priviledge, const std::string &exPriviledge, WS::TUserRole mode, uint32 instanceId); + // ask the welcome service to disconnect a user + void disconnectUser(NLNET::IModule *sender, uint32 userId); + + // Message serializer. Return the message received in reference for easier integration + static const NLNET::CMessage &buildMessageFor_welcomeUser(NLNET::CMessage &__message, uint32 charId, const std::string &userName, const NLNET::CLoginCookie &cookie, const std::string &priviledge, const std::string &exPriviledge, WS::TUserRole mode, uint32 instanceId); + + // Message serializer. Return the message received in reference for easier integration + static const NLNET::CMessage &buildMessageFor_disconnectUser(NLNET::CMessage &__message, uint32 userId); + + + + + }; + + ///////////////////////////////////////////////////////////////// + // WARNING : this is a generated file, don't change it ! + ///////////////////////////////////////////////////////////////// + class CLoginServiceSkel + { + public: + /// the interceptor type + typedef NLNET::CInterceptorForwarder < CLoginServiceSkel> TInterceptor; + protected: + CLoginServiceSkel() + { + // do early run time check for message table + getMessageHandlers(); + } + virtual ~CLoginServiceSkel() + { + } + + void init(NLNET::IModule *module) + { + _Interceptor.init(this, module); + } + + // unused interceptors + std::string fwdBuildModuleManifest() const { return std::string(); } + void fwdOnModuleUp(NLNET::IModuleProxy * /* moduleProxy */) {} + void fwdOnModuleDown(NLNET::IModuleProxy * /* moduleProxy */) {} + void fwdOnModuleSecurityChange(NLNET::IModuleProxy * /* moduleProxy */) {} + + // process module message interceptor + bool fwdOnProcessModuleMessage(NLNET::IModuleProxy *sender, const NLNET::CMessage &message); + private: + + typedef void (CLoginServiceSkel::*TMessageHandler)(NLNET::IModuleProxy *sender, const NLNET::CMessage &message); + typedef std::map TMessageHandlerMap; + + const TMessageHandlerMap &getMessageHandlers() const; + + + void pendingUserLost_skel(NLNET::IModuleProxy *sender, const NLNET::CMessage &__message); + + // declare one interceptor member of the skeleton + TInterceptor _Interceptor; + + // declare the interceptor forwarder as friend of this class + friend class NLNET::CInterceptorForwarder < CLoginServiceSkel>; + public: + ///////////////////////////////////////////////////////////////// + // WARNING : this is a generated file, don't change it ! + ///////////////////////////////////////////////////////////////// + + // An awaited user did not connect before the allowed timeout expire + virtual void pendingUserLost(NLNET::IModuleProxy *sender, const NLNET::CLoginCookie &cookie) =0; + + + }; + + ///////////////////////////////////////////////////////////////// + // WARNING : this is a generated file, don't change it ! + ///////////////////////////////////////////////////////////////// + class CLoginServiceProxy + { + /// Smart pointer on the module proxy + NLNET::TModuleProxyPtr _ModuleProxy; + + // Pointer on the local module that implement the interface (if the proxy is for a local module) + NLNET::TModulePtr _LocalModule; + // Direct pointer on the server implementation interface for collocated module + CLoginServiceSkel *_LocalModuleSkel; + + + public: + CLoginServiceProxy(NLNET::IModuleProxy *proxy) + { + + _ModuleProxy = proxy; + + // initialize collocated servant interface + if (proxy->getModuleDistance() == 0) + { + _LocalModule = proxy->getLocalModule(); + nlassert(_LocalModule != NULL); + CLoginServiceSkel::TInterceptor *interceptor = NULL; + interceptor = static_cast < NLNET::CModuleBase* >(_LocalModule.getPtr())->getInterceptor(interceptor); + nlassert(interceptor != NULL); + + _LocalModuleSkel = interceptor->getParent(); + nlassert(_LocalModuleSkel != NULL); + } + else + _LocalModuleSkel = 0; + + } + virtual ~CLoginServiceProxy() + { + } + + NLNET::IModuleProxy *getModuleProxy() + { + return _ModuleProxy; + } + + // An awaited user did not connect before the allowed timeout expire + void pendingUserLost(NLNET::IModule *sender, const NLNET::CLoginCookie &cookie); + + // Message serializer. Return the message received in reference for easier integration + static const NLNET::CMessage &buildMessageFor_pendingUserLost(NLNET::CMessage &__message, const NLNET::CLoginCookie &cookie); + + + + + }; + + ///////////////////////////////////////////////////////////////// + // WARNING : this is a generated file, don't change it ! + ///////////////////////////////////////////////////////////////// + class CWelcomeServiceClientSkel + { + public: + /// the interceptor type + typedef NLNET::CInterceptorForwarder < CWelcomeServiceClientSkel> TInterceptor; + protected: + CWelcomeServiceClientSkel() + { + // do early run time check for message table + getMessageHandlers(); + } + virtual ~CWelcomeServiceClientSkel() + { + } + + void init(NLNET::IModule *module) + { + _Interceptor.init(this, module); + } + + // unused interceptors + std::string fwdBuildModuleManifest() const { return std::string(); } + void fwdOnModuleUp(NLNET::IModuleProxy * /* moduleProxy */) {} + void fwdOnModuleDown(NLNET::IModuleProxy * /* moduleProxy */) {} + void fwdOnModuleSecurityChange(NLNET::IModuleProxy * /* moduleProxy */) {} + + // process module message interceptor + bool fwdOnProcessModuleMessage(NLNET::IModuleProxy *sender, const NLNET::CMessage &message); + private: + + typedef void (CWelcomeServiceClientSkel::*TMessageHandler)(NLNET::IModuleProxy *sender, const NLNET::CMessage &message); + typedef std::map TMessageHandlerMap; + + const TMessageHandlerMap &getMessageHandlers() const; + + + void registerWS_skel(NLNET::IModuleProxy *sender, const NLNET::CMessage &__message); + + void reportWSOpenState_skel(NLNET::IModuleProxy *sender, const NLNET::CMessage &__message); + + void welcomeUserResult_skel(NLNET::IModuleProxy *sender, const NLNET::CMessage &__message); + + void updateConnectedPlayerCount_skel(NLNET::IModuleProxy *sender, const NLNET::CMessage &__message); + + // declare one interceptor member of the skeleton + TInterceptor _Interceptor; + + // declare the interceptor forwarder as friend of this class + friend class NLNET::CInterceptorForwarder < CWelcomeServiceClientSkel>; + public: + ///////////////////////////////////////////////////////////////// + // WARNING : this is a generated file, don't change it ! + ///////////////////////////////////////////////////////////////// + + // Register the welcome service in the ring session manager + // The provided sessionId will be non-zero only for a shard with a fixed sessionId + virtual void registerWS(NLNET::IModuleProxy *sender, uint32 shardId, uint32 fixedSessionId, bool isOnline) =0; + // WS report it's current open state + virtual void reportWSOpenState(NLNET::IModuleProxy *sender, bool isOnline) =0; + // return for welcome user + virtual void welcomeUserResult(NLNET::IModuleProxy *sender, uint32 userId, bool ok, const std::string &shardAddr, const std::string &errorMsg) =0; + // transmits the current player counts + virtual void updateConnectedPlayerCount(NLNET::IModuleProxy *sender, uint32 nbOnlinePlayers, uint32 nbPendingPlayers) =0; + + + }; + + ///////////////////////////////////////////////////////////////// + // WARNING : this is a generated file, don't change it ! + ///////////////////////////////////////////////////////////////// + class CWelcomeServiceClientProxy + { + /// Smart pointer on the module proxy + NLNET::TModuleProxyPtr _ModuleProxy; + + // Pointer on the local module that implement the interface (if the proxy is for a local module) + NLNET::TModulePtr _LocalModule; + // Direct pointer on the server implementation interface for collocated module + CWelcomeServiceClientSkel *_LocalModuleSkel; + + + public: + CWelcomeServiceClientProxy(NLNET::IModuleProxy *proxy) + { + + _ModuleProxy = proxy; + + // initialize collocated servant interface + if (proxy->getModuleDistance() == 0) + { + _LocalModule = proxy->getLocalModule(); + nlassert(_LocalModule != NULL); + CWelcomeServiceClientSkel::TInterceptor *interceptor = NULL; + interceptor = static_cast < NLNET::CModuleBase* >(_LocalModule.getPtr())->getInterceptor(interceptor); + nlassert(interceptor != NULL); + + _LocalModuleSkel = interceptor->getParent(); + nlassert(_LocalModuleSkel != NULL); + } + else + _LocalModuleSkel = 0; + + } + virtual ~CWelcomeServiceClientProxy() + { + } + + NLNET::IModuleProxy *getModuleProxy() + { + return _ModuleProxy; + } + + // Register the welcome service in the ring session manager + // The provided sessionId will be non-zero only for a shard with a fixed sessionId + void registerWS(NLNET::IModule *sender, uint32 shardId, uint32 fixedSessionId, bool isOnline); + // WS report it's current open state + void reportWSOpenState(NLNET::IModule *sender, bool isOnline); + // return for welcome user + void welcomeUserResult(NLNET::IModule *sender, uint32 userId, bool ok, const std::string &shardAddr, const std::string &errorMsg); + // transmits the current player counts + void updateConnectedPlayerCount(NLNET::IModule *sender, uint32 nbOnlinePlayers, uint32 nbPendingPlayers); + + // Message serializer. Return the message received in reference for easier integration + static const NLNET::CMessage &buildMessageFor_registerWS(NLNET::CMessage &__message, uint32 shardId, uint32 fixedSessionId, bool isOnline); + + // Message serializer. Return the message received in reference for easier integration + static const NLNET::CMessage &buildMessageFor_reportWSOpenState(NLNET::CMessage &__message, bool isOnline); + + // Message serializer. Return the message received in reference for easier integration + static const NLNET::CMessage &buildMessageFor_welcomeUserResult(NLNET::CMessage &__message, uint32 userId, bool ok, const std::string &shardAddr, const std::string &errorMsg); + + // Message serializer. Return the message received in reference for easier integration + static const NLNET::CMessage &buildMessageFor_updateConnectedPlayerCount(NLNET::CMessage &__message, uint32 nbOnlinePlayers, uint32 nbPendingPlayers); + + + + + }; + +} + +#endif diff --git a/code/ryzom/server/src/frontend_service/client_host.h b/code/ryzom/server/src/frontend_service/client_host.h index c284567d9..a8e377a29 100644 --- a/code/ryzom/server/src/frontend_service/client_host.h +++ b/code/ryzom/server/src/frontend_service/client_host.h @@ -33,7 +33,7 @@ #include "entity_container.h" #include "game_share/ryzom_entity_id.h" #include "game_share/entity_types.h" -#include "../../nelns/welcome_service/welcome_service_itf.h" +#include "game_share/welcome_service_itf.h" #include #include diff --git a/code/ryzom/server/src/ryzom_naming_service/ryzom_naming_service.cpp b/code/ryzom/server/src/ryzom_naming_service/ryzom_naming_service.cpp index 857d6b847..42287fb87 100644 --- a/code/ryzom/server/src/ryzom_naming_service/ryzom_naming_service.cpp +++ b/code/ryzom/server/src/ryzom_naming_service/ryzom_naming_service.cpp @@ -1,12 +1,1147 @@ +// 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 . +// +// Includes +// + +#include "nel/misc/types_nl.h" + +#include +#include + +#include "nel/misc/debug.h" +#include "nel/misc/command.h" +#include "nel/misc/variable.h" +#include "nel/misc/displayer.h" + +#include "nel/net/callback_server.h" +#include "nel/net/service.h" +#include "nel/net/module_manager.h" + +// +// Namespaces +// + +using namespace std; + +using namespace NLMISC; +using namespace NLNET; + + +NLMISC_COMMAND(test, "none", "none") +{ + log.displayNL("Raw cmd line : '%s'", rawCommandString.c_str()); + log.displayNL("Dumping %u parameters :", args.size()); + for (uint i=0; i &a, const string &n, TServiceId s) : SockId(sock), Addr(a), Name(n), SId (s), WaitingUnregistration(false) { } + + TSockId SockId; // the connection between the service and the naming service + vector Addr; // address to send to the service who wants to lookup this service + // it s possible to have more than one addr, anyway, the naming service + // will send good address depending of the sub net address of the service + string Name; // name of the service + TServiceId SId; // id of the service + + bool WaitingUnregistration; // true if this service is in unregistration process (wait other service ACK) + TTime WaitingUnregistrationTime; // time of the beginning of the inregistration process + list WaitingUnregistrationServices; // list of service that we wait the answer +}; + + + +// Helper that emulates layer5's send() +//void sendToService( uint16 sid, CMessage& msgout ); + +// Helper that emulate layer5's getServiceName() +string getServiceName( TServiceId sid ); + +// Helper that returns the first address of a service +CInetAddress getHostAddress( TServiceId sid ); + +// Asks a service to stop and tell every one +void doUnregisterService (TServiceId sid); extern void admin_modules_forceLink(); - - void foo() { admin_modules_forceLink(); } +/** + * Manager for services instances + * (Moved from the TICKS to the NS) + * Implementable with layer 5, here implemented in NS (layer 3) + * \author Olivier Cado + * \author Nevrax France + * \date 2003 + */ +class CServiceInstanceManager +{ +public: + + /// Constructor + CServiceInstanceManager(); -#include "../../nelns/naming_service/naming_service.cpp" + /** Add the name of a service which must not be duplicated + * If uniqueOnShard is true, only one service is allowed. + * If uniqueOnShard is false, one service is allowed by physical machine. + */ + void addUniqueService( const std::string& serviceName, bool uniqueOnShard ) + { + _UniqueServices.insert( std::make_pair( serviceName, uniqueOnShard ) ); + } + + /// Check if a service is allowed to start (if so, add it) + bool queryStartService( const std::string& serviceName, TServiceId serviceId, const std::vector &addr, string& reason ); + + /// Release a service instance + void releaseService( NLNET::TServiceId serviceId ); + + /// Display information + void displayInfo( NLMISC::CLog *log = NLMISC::InfoLog ) const; + + /// Make all controlled services quit + void killAllServices(); + +private: + + /// List of restricted services + std::map< std::string, bool > _UniqueServices; + + /// List of granted (online) services + std::set< TServiceId > _OnlineServices; +}; + + +CServiceInstanceManager *SIMInstance = NULL; + + +/* + * Constructor + */ +CServiceInstanceManager::CServiceInstanceManager() +{ + nlassert( ! SIMInstance ); + SIMInstance = this; + + // Note: addCallbackArray() done in CRangeMirrorManager::init() +} + + +/* + * Check if a service is allowed to start. Answer with a GSTS (Grant Start Service) message + */ +bool CServiceInstanceManager::queryStartService( const std::string& serviceName, TServiceId serviceId, const vector &addr, string& reason ) +{ + bool grantStarting = true; + std::map< std::string, bool >::iterator ius = _UniqueServices.find( serviceName ); + if ( ius != _UniqueServices.end() ) + { + // Service is restricted + set< TServiceId >::iterator ios; + bool uniqueOnShard = (*ius).second; + for ( ios=_OnlineServices.begin(); ios!=_OnlineServices.end(); ++ios ) + { + string name = getServiceName( *ios ); + if ( name == serviceName ) + { + if ( uniqueOnShard ) + { + // Only one service by shard is allowed => deny + grantStarting = false; + reason = toString( "Service %s already found as %hu, must be unique on shard", serviceName.c_str(), ios->get() ); + nlinfo( reason.c_str() ); + break; + } + else + { + // Only one service by physical machine is allowed + + // Implementation for layer5 + //TSockId hostid1, hostid2; + /*CCallbackNetBase *cnb1 = CUnifiedNetwork::getInstance()->getNetBase( serviceId, hostid1 ); + CCallbackNetBase *cnb2 = CUnifiedNetwork::getInstance()->getNetBase( *ios, hostid2 ); + if ( cnb1->hostAddress( hostid1 ).internalIPAddress() == cnb2->hostAddress( hostid2 ).internalIPAddress() )*/ + + // Implementation for NS + if ( addr[0].internalIPAddress() == getHostAddress( *ios ).internalIPAddress() ) + { + grantStarting = false; + reason = toString( "Service %s already found as %hu on same machine", serviceName.c_str(), ios->get() ); + nlinfo( reason.c_str() ); + break; + } + } + } + } + } + + if ( grantStarting ) + { + _OnlineServices.insert( serviceId ); + } + return grantStarting; +} + + +/* + * Release a service instance + */ +void CServiceInstanceManager::releaseService( NLNET::TServiceId serviceId ) +{ + _OnlineServices.erase( serviceId ); // not a problem if not found +} + + +/* + * Display information + */ +void CServiceInstanceManager::displayInfo( NLMISC::CLog *log ) const +{ + log->displayNL( "Restricted services:" ); + std::map< std::string, bool >::const_iterator ius; + for ( ius=_UniqueServices.begin(); ius!=_UniqueServices.end(); ++ius ) + { + log->displayNL( "%s -> only one per %s", (*ius).first.c_str(), (*ius).second?"shard":"machine" ); + } + log->displayNL( "Online registered services:" ); + std::set< TServiceId >::const_iterator ios; + for ( ios=_OnlineServices.begin(); ios!=_OnlineServices.end(); ++ios ) + { + log->displayNL( "%s", CUnifiedNetwork::getInstance()->getServiceUnifiedName( *ios ).c_str() ); + } +} + + +/* + * Make all controlled services quit + */ +void CServiceInstanceManager::killAllServices() +{ + // Send to all known online services + std::set< TServiceId >::const_iterator ios; + for ( ios=_OnlineServices.begin(); ios!=_OnlineServices.end(); ++ios ) + { + doUnregisterService( (TServiceId)(*ios) ); + } +} + + + +// +// Variables +// + +list RegisteredServices; /// List of all registred services + +uint16 MinBasePort = 51000; /// Ports begin at 51000 +uint16 MaxBasePort = 52000; /// (note: in this implementation there can be no more than 1000 services) + +const TServiceId BaseSId(128); /// Allocated SIds begin at 128 (except for Agent Service) + +const TTime UnregisterTimeout = 10000; /// After 10s we remove an unregister service if every server didn't ACK the message + +CCallbackServer *CallbackServer = NULL; + +// +// Functions +// + +bool canAccess (const vector &addr, const CServiceEntry &entry, vector &accessibleAddr) +{ + accessibleAddr.clear (); + + if (entry.WaitingUnregistration) + return false; + + for (uint i = 0; i < addr.size(); i++) + { + uint32 net = addr[i].internalNetAddress(); + for (uint j = 0; j < entry.Addr.size(); j++) + { + if (net == entry.Addr[j].internalNetAddress()) + { + accessibleAddr.push_back (entry.Addr[j]); + } + } + } + + if (accessibleAddr.empty()) + { + nldebug ("service %s-%hu is not accessible by '%s'", entry.Name.c_str(), entry.SId.get(), vectorCInetAddressToString (addr).c_str ()); + } + else + { + nldebug ("service %s-%hu is accessible by '%s'", entry.Name.c_str(), entry.SId.get(), vectorCInetAddressToString (accessibleAddr).c_str ()); + } + + return !accessibleAddr.empty (); +} + +void displayRegisteredServices (CLog *log = InfoLog) +{ + log->displayNL ("Display the %d registered services :", RegisteredServices.size()); + for (list::iterator it = RegisteredServices.begin(); it != RegisteredServices.end (); it++) + { + TSockId id = (*it).SockId; + if (id == NULL) + { + log->displayNL ("> %s-%hu %s '%s' %s %d addr", (*it).Name.c_str(), it->SId.get(), "", "", (*it).WaitingUnregistration?"WaitUnreg":"", (*it).Addr.size()); + for(uint i = 0; i < (*it).Addr.size(); i++) + log->displayNL (" '%s'", (*it).Addr[i].asString().c_str()); + } + else + { + log->displayNL ("> %s-%hu %s '%s' %s %d addr", (*it).Name.c_str(), it->SId.get(), (*it).SockId->asString().c_str(), CallbackServer->hostAddress((*it).SockId).asString().c_str(), (*it).WaitingUnregistration?"WaitUnreg":"", (*it).Addr.size()); + for(uint i = 0; i < (*it).Addr.size(); i++) + log->displayNL (" '%s'", (*it).Addr[i].asString().c_str()); + } + } + log->displayNL ("End of the list"); +} + + +list::iterator effectivelyRemove (list::iterator &it) +{ + // remove the service from the registered service list + nlinfo ("Effectively remove the service %s-%hu", (*it).Name.c_str(), it->SId.get()); + return RegisteredServices.erase (it); +} + +/* + * Helper procedure for cbLookupAlternate and cbUnregister. + * Note: name is used for a LOGS. + */ +list::iterator doRemove (list::iterator it) +{ + nldebug ("Unregister the service %s-%hu '%s'", (*it).Name.c_str(), it->SId.get(), (*it).Addr[0].asString().c_str()); + + // tell to everybody that this service is unregistered + + CMessage msgout ("UNB"); + msgout.serial ((*it).Name); + msgout.serial ((*it).SId); + + vector accessibleAddress; + nlinfo ("Broadcast the Unregistration of %s-%hu to all registered services", (*it).Name.c_str(), it->SId.get()); + for (list::iterator it3 = RegisteredServices.begin(); it3 != RegisteredServices.end (); it3++) + { + if (canAccess((*it).Addr, (*it3), accessibleAddress)) + { + CallbackServer->send (msgout, (*it3).SockId); + //CNetManager::send ("NS", msgout, (*it3).SockId); + nldebug ("Broadcast to %s-%hu", (*it3).Name.c_str(), it3->SId.get()); + } + } + + // new system, after the unregistation broadcast, we wait ACK from all services before really remove + // the service, before, we tag the service as 'wait before unregister' + // if everybody didn't answer before the time out, we remove it + + (*it).SockId = NULL; + + (*it).WaitingUnregistration = true; + (*it).WaitingUnregistrationTime = CTime::getLocalTime(); + + // we remove all services awaiting his ACK because this service is down so it'll never ACK + for (list::iterator itr = RegisteredServices.begin(); itr != RegisteredServices.end (); itr++) + { + for (list::iterator itw = (*itr).WaitingUnregistrationServices.begin(); itw != (*itr).WaitingUnregistrationServices.end ();) + { + if ((*itw) == (*it).SId) + { + itw = (*itr).WaitingUnregistrationServices.erase (itw); + } + else + { + itw++; + } + } + } + + string res; + for (list::iterator it2 = RegisteredServices.begin(); it2 != RegisteredServices.end (); it2++) + { + if (!(*it2).WaitingUnregistration) + { + (*it).WaitingUnregistrationServices.push_back ((*it2).SId); + res += toString((*it2).SId.get()) + " "; + } + } + + nlinfo ("Before removing the service %s-%hu, we wait the ACK of '%s'", (*it).Name.c_str(), (*it).SId.get(), res.c_str()); + + if ((*it).WaitingUnregistrationServices.empty()) + { + return effectivelyRemove (it); + } + else + { + return ++it; + } + + // Release from the service instance manager + SIMInstance->releaseService( (*it).SId ); +} + +void doUnregisterService (TServiceId sid) +{ + list::iterator it; + for (it = RegisteredServices.begin(); it != RegisteredServices.end (); it++) + { + if ((*it).SId == sid) + { + // found it, remove it + doRemove (it); + return; + } + } + nlwarning ("Service %hu not found", sid.get()); +} + +void doUnregisterService (TSockId from) +{ + list::iterator it; + for (it = RegisteredServices.begin(); it != RegisteredServices.end ();) + { + if ((*it).SockId == from) + { + // it's possible that one "from" have more than one registred service, so we have to find in all the list + // found it, remove it + it = doRemove (it); + } + else + { + it++; + } + } +} + +/*void doUnregisterService (const CInetAddress &addr) +{ + list::iterator it; + for (it = RegisteredServices.begin(); it != RegisteredServices.end (); it++) + { + if ((*it).Addr == addr) + { + // found it, remove it + doRemove (it); + return; + } + } + nlwarning ("Service %s not found", addr.asString().c_str()); +}*/ + +/* + * Helper function for cbRegister. + * If alloc_sid is true, sid is ignored + * Returns false in case of failure of sid allocation or bad sid provided + * Note: the reply is included in this function, because it must be done before things such as syncUniTime() + */ +bool doRegister (const string &name, const vector &addr, TServiceId sid, TSockId from, CCallbackNetBase &netbase, bool reconnection = false) +{ + // Find if the service is not already registered + string reason; + uint8 ok = true; + bool needRegister = true; + /*for (list::iterator it = RegisteredServices.begin(); it != RegisteredServices.end (); it++) + { + if ((*it).Addr.asIPString() == addr.asIPString() ) + { + // we already have a service on this address, remplace it if it's the same name + if ((*it).Name == name) + { + // it's the same service, replace it + (*it).SockId = from; + sid = (*it).SId; + nlinfo ("Replace the service %s", name.c_str()); + } + else + { + nlwarning ("Try to register %s to %s but the service %s already on this address. ignore it!", name.c_str(), addr.asIPString().c_str(), (*it).Name.c_str()); + ok = false; + } + needRegister = false; + break; + } + }*/ + + if (needRegister) + { + if (sid.get() == 0) + { + // we have to find a sid + sid = BaseSId; + bool found = false; + while (!found) + { + list::iterator it; + for (it = RegisteredServices.begin(); it != RegisteredServices.end (); it++) + { + if ((*it).SId == sid) + { + break; + } + } + if (it == RegisteredServices.end ()) + { + // ok, we have an empty sid + found = true; + } + else + { + sid.set(sid.get()+1); + if (sid.get() == 0) // round the clock + { + nlwarning ("Service identifier allocation overflow"); + ok = false; + break; + } + } + } + + } + else + { + // we have to check that the user provided sid is available + list::iterator it; + for (it = RegisteredServices.begin(); it != RegisteredServices.end (); it++) + { + if ((*it).SId == sid) + { + nlwarning ("Sid %d already used by another service", sid.get()); + ok = false; + break; + } + } + if (it != RegisteredServices.end ()) + { + ok = true; + } + } + + // if ok, register the service and send a broadcast to other people + if (ok) + { + // Check if the instance is allowed to start, according to the restriction in the config file + if ( SIMInstance->queryStartService( name, sid, addr, reason ) ) + { + // add him in the registered list + RegisteredServices.push_back (CServiceEntry(from, addr, name, sid)); + + // tell to everybody but not him that this service is registered + if (!reconnection) + { + CMessage msgout ("RGB"); + TServiceId::size_type s = 1; + msgout.serial (s); + msgout.serial (const_cast(name)); + msgout.serial (sid); + // we need to send all addr to all services even if the service can't access because we use the address index + // to know which connection comes. + msgout.serialCont (const_cast &>(addr)); + nlinfo ("The service is %s-%d, broadcast the Registration to everybody", name.c_str(), sid.get()); + + vector accessibleAddress; + for (list::iterator it3 = RegisteredServices.begin(); it3 != RegisteredServices.end (); it3++) + { + // send only services that can be accessed and not itself + if ((*it3).SId != sid && canAccess(addr, (*it3), accessibleAddress)) + { + CallbackServer->send (msgout, (*it3).SockId); + //CNetManager::send ("NS", msgout, (*it3).SockId); + nldebug ("Broadcast to %s-%hu", (*it3).Name.c_str(), it3->SId.get()); + } + } + } + + // set the sid only if it s ok + from->setAppId (sid.get()); + } + else + { + // Reply "startup denied", and do not send registration to other services + ok = false; + } + } + + // send the message to the service to say if it s ok or not + if (!reconnection) + { + // send the answer to the client + CMessage msgout ("RG"); + msgout.serial (ok); + if (ok) + { + msgout.serial (sid); + + // send him all services available (also itself) + TServiceId::size_type nb = 0; + + vector accessibleAddress; + + for (list::iterator it2 = RegisteredServices.begin(); it2 != RegisteredServices.end (); it2++) + { + // send only services that are available + if (canAccess(addr, (*it2), accessibleAddress)) + nb++; + } + msgout.serial (nb); + + for (list::iterator it = RegisteredServices.begin(); it != RegisteredServices.end (); it++) + { + // send only services that are available + if (canAccess(addr, (*it), accessibleAddress)) + { + msgout.serial ((*it).Name); + msgout.serial ((*it).SId); + msgout.serialCont ((*it).Addr); + } + } + } + else + { + msgout.serial( reason ); + } + + netbase.send (msgout, from); + netbase.flush (from); + } + } + + //displayRegisteredServices (); + + return ok!=0; +} + +void checkWaitingUnregistrationServices () +{ + for (list::iterator it = RegisteredServices.begin(); it != RegisteredServices.end ();) + { + if ((*it).WaitingUnregistration && ((*it).WaitingUnregistrationServices.empty() || CTime::getLocalTime() > (*it).WaitingUnregistrationTime + UnregisterTimeout)) + { + if ((*it).WaitingUnregistrationServices.empty()) + { + nlinfo ("Removing the service %s-%hu because all services ACKd the removal", (*it).Name.c_str(), (*it).SId.get()); + } + else + { + string res; + for (list::iterator it2 = (*it).WaitingUnregistrationServices.begin(); it2 != (*it).WaitingUnregistrationServices.end (); it2++) + { + res += toString(it2->get()) + " "; + } + nlwarning ("Removing the service %s-%hu because time out occurs (service numbers %s didn't ACK)", (*it).Name.c_str(), (*it).SId.get(), res.c_str()); + } + it = effectivelyRemove (it); + } + else + { + it++; + } + } +} + + +/** + * Callback for service unregistration ACK. Mean that a service was ACK the unregistration broadcast + */ +static void cbACKUnregistration (CMessage& msgin, TSockId from, CCallbackNetBase &netbase) +{ + TServiceId sid; + msgin.serial (sid); + + for (list::iterator it = RegisteredServices.begin(); it != RegisteredServices.end (); it++) + { + if ((*it).SId == sid && (*it).WaitingUnregistration) + { + for (list::iterator it2 = (*it).WaitingUnregistrationServices.begin(); it2 != (*it).WaitingUnregistrationServices.end (); it2++) + { + if (*it2 == TServiceId(uint16(from->appId()))) + { + // remove the acked service + (*it).WaitingUnregistrationServices.erase (it2); + checkWaitingUnregistrationServices (); + return; + } + } + } + } +} + + +/** + * Callback for service registration when the naming service goes down and up (don't need to broadcast) + */ +static void cbResendRegisteration (CMessage& msgin, TSockId from, CCallbackNetBase &netbase) +{ + string name; + vector addr; + TServiceId sid; + msgin.serial (name); + msgin.serialCont (addr); + msgin.serial (sid); + + doRegister (name, addr, sid, from, netbase, true); +} + + + +/** + * Callback for service registration. + * + * Message expected : RG + * - Name of service to register (string) + * - Address of service (CInetAddress) + * + * Message emitted : RG + * - Allocated service identifier (TServiceId) or 0 if failed + */ +static void cbRegister (CMessage& msgin, TSockId from, CCallbackNetBase &netbase) +{ + string name; + vector addr; + TServiceId sid; + msgin.serial (name); + msgin.serialCont (addr); + msgin.serial (sid); + + doRegister (name, addr, sid, from, netbase); +} + + +/** + * Callback for service unregistration. + * + * Message expected : UNI + * - Service identifier (TServiceId) + */ +static void cbUnregisterSId (CMessage& msgin, TSockId from, CCallbackNetBase &netbase) +{ + TServiceId sid; + msgin.serial( sid ); + + doUnregisterService (sid); + //displayRegisteredServices (); +} + + +/* + * Helper function for cbQueryPort + * + * \warning QueryPort + Registration is not atomic so more than one service could ask a port before register + */ +uint16 doAllocatePort (const CInetAddress &addr) +{ + static uint16 nextAvailablePort = MinBasePort; + + // check if nextavailableport is free + + if (nextAvailablePort >= MaxBasePort) nextAvailablePort = MinBasePort; + + bool ok; + do + { + ok = true; + list::iterator it; + for (it = RegisteredServices.begin(); it != RegisteredServices.end (); it++) + { + if ((*it).Addr[0].port () == nextAvailablePort) + { + nextAvailablePort++; + ok = false; + break; + } + } + } + while (!ok); + + return nextAvailablePort++; +} + + +/** + * Callback for port allocation + * Note: if a service queries a port but does not register itself to the naming service, the + * port will remain allocated and unused. + * + * Message expected : QP + * - Name of service to register (string) + * - Address of service (CInetAddress) (its port can be 0) + * + * Message emitted : QP + * - Allocated port number (uint16) + */ +static void cbQueryPort (CMessage& msgin, TSockId from, CCallbackNetBase &netbase) +{ + // Allocate port + uint16 port = doAllocatePort (netbase.hostAddress (from)); + + // Send port back + CMessage msgout ("QP"); + msgout.serial (port); + netbase.send (msgout, from); + + nlinfo ("The service got port %hu", port); +} + + +/* + * Unregisters a service if it has not been done before. + * Note: this callback is called whenever someone disconnects from the NS. + * May be there are too many calls if many clients perform many transactional lookups. + */ +static void cbDisconnect /*(const string &serviceName, TSockId from, void *arg)*/ ( TSockId from, void *arg ) +{ + doUnregisterService (from); + //displayRegisteredServices (); +} + +/* + * a service is connected, send him all services infos + */ +static void cbConnect /*(const string &serviceName, TSockId from, void *arg)*/ ( TSockId from, void *arg ) +{ + // we have to wait the registred services message to send all services because it this points, we can't know which sub net + // the service can use + + //displayRegisteredServices (); + + // set the appid with a bad id (-1) + from->setAppId (~0); +} + +/*// returns the list of accessible services with a list of address +static void cbRegisteredServices(CMessage& msgin, TSockId from, CCallbackNetBase &netbase) +{ + vector addr; + msgin.serialCont (addr); + + nlinfo ("New service ask me the available services, sending him all services available"); + // send to the new service the list of all services that this service can access (depending of his sub net) + + CMessage msgout ("RGB"); + + uint8 nb = 0; + + vector accessibleAddress; + + for (list::iterator it2 = RegisteredServices.begin(); it2 != RegisteredServices.end (); it2++) + { + // send only services that are available + if (canAccess(addr, (*it2), accessibleAddress)) + nb++; + } + + msgout.serial (nb); + + for (list::iterator it = RegisteredServices.begin(); it != RegisteredServices.end (); it++) + { + // send only services that are available + if (canAccess(addr, (*it), accessibleAddress)) + { + msgout.serial ((*it).Name); + msgout.serial ((*it).SId); + msgout.serialCont (accessibleAddress); + } + } + + CNetManager::send ("NS", msgout, from); +}*/ + + +/* + * Helper that emulates layer5 send() + */ +/*void sendToService( uint16 sid, CMessage& msgout ) +{ + list::iterator it; + for (it = RegisteredServices.begin(); it != RegisteredServices.end (); it++) + { + if ((*it).SId == sid) + { + CallbackServer->send (msgout, (*it).SockId); + } + } +}*/ + + +/* + * Helper that emulate layer5's getServiceName() + */ +string getServiceName( TServiceId sid ) +{ + list::iterator it; + for (it = RegisteredServices.begin(); it != RegisteredServices.end (); it++) + { + if ((*it).SId == sid) + { + return (*it).Name; + } + } + return ""; // not found +} + + +/* + * Helper that returns the first address of a service + */ +CInetAddress getHostAddress( TServiceId sid ) +{ + list::iterator it; + for (it = RegisteredServices.begin(); it != RegisteredServices.end (); it++) + { + if ((*it).SId == sid) + { + return (*it).Addr[0]; + } + } + return CInetAddress(); +} + + +// +// Callback array +// + +TCallbackItem CallbackArray[] = +{ + { "RG", cbRegister }, + { "RRG", cbResendRegisteration }, + { "QP", cbQueryPort }, + { "UNI", cbUnregisterSId }, + { "ACK_UNI", cbACKUnregistration }, +// { "RS", cbRegisteredServices }, +}; + + +// +// Service +// + +class CNamingService : public NLNET::IService +{ +public: + + /** + * Init + */ + void init() + { + // if a baseport is available in the config file, get it + CConfigFile::CVar *var; + if ((var = ConfigFile.getVarPtr ("BasePort")) != NULL) + { + uint16 newBasePort = var->asInt (); + nlinfo ("Changing the MinBasePort number from %hu to %hu", MinBasePort, newBasePort); + sint32 delta = MaxBasePort - MinBasePort; + nlassert (delta > 0); + MinBasePort = newBasePort; + MaxBasePort = MinBasePort + uint16 (delta); + } + + // Parameters for the service instance manager + try + { + CConfigFile::CVar& uniqueServices = ConfigFile.getVar("UniqueOnShardServices"); + for ( uint i=0; i!=uniqueServices.size(); ++i ) + { + _ServiceInstances.addUniqueService( uniqueServices.asString(i), true ); + } + } + catch(Exception &) + {} + try + { + CConfigFile::CVar& uniqueServicesM = ConfigFile.getVar("UniqueByMachineServices"); + for ( uint i=0; i!=uniqueServicesM.size(); ++i ) + { + _ServiceInstances.addUniqueService( uniqueServicesM.asString(i), false ); + } + } + catch(Exception &) + {} + +/* + // we don't try to associate message from client + CNetManager::getNetBase ("NS")->ignoreAllUnknownId (true); + + // add the callback in case of disconnection + CNetManager::setConnectionCallback ("NS", cbConnect, NULL); + + // add the callback in case of disconnection + CNetManager::setDisconnectionCallback ("NS", cbDisconnect, NULL); +*/ + // DEBUG + // DebugLog->addDisplayer( new CStdDisplayer() ); + + vector v = CInetAddress::localAddresses(); + nlinfo ("%d detected local addresses:", v.size()); + for (uint i = 0; i < v.size(); i++) + { + nlinfo (" %d - '%s'",i, v[i].asString().c_str()); + } + + uint16 nsport = 50000; + if ((var = ConfigFile.getVarPtr ("NSPort")) != NULL) + { + nsport = var->asInt (); + } + + CallbackServer = new CCallbackServer; + CallbackServer->init(nsport); + CallbackServer->addCallbackArray(CallbackArray, sizeof(CallbackArray)/sizeof(CallbackArray[0])); + CallbackServer->setConnectionCallback(cbConnect, NULL); + CallbackServer->setDisconnectionCallback(cbDisconnect, NULL); + } + + /** + * Update + */ + bool update () + { + checkWaitingUnregistrationServices (); + + CallbackServer->update (); + + return true; + } + + void release() + { + if (CallbackServer != NULL) + delete CallbackServer; + CallbackServer = NULL; + } + +private: + + /// Service instance manager singleton + CServiceInstanceManager _ServiceInstances; +}; + + +static const char* getCompleteServiceName(const IService* theService) +{ + static std::string s; + s= "naming_service"; + + if (theService->haveLongArg("nsname")) + { + s+= "_"+theService->getLongArg("nsname"); + } + + if (theService->haveLongArg("fullnsname")) + { + s= theService->getLongArg("fullnsname"); + } + + return s.c_str(); +} + +static const char* getShortServiceName(const IService* theService) +{ + static std::string s; + s= "NS"; + + if (theService->haveLongArg("shortnsname")) + { + s= theService->getLongArg("shortnsname"); + } + + return s.c_str(); +} +// +/// Naming Service +// +NLNET_SERVICE_MAIN( CNamingService, getShortServiceName(scn), getCompleteServiceName(scn), 0, EmptyCallbackArray, "", "") + + +// +// Commands +// + + +NLMISC_COMMAND (nsServices, "displays the list of all registered services", "") +{ + if(args.size() != 0) return false; + + displayRegisteredServices (&log); + + return true; +} + +NLMISC_COMMAND (kill, "kill a service and send an unregister broadcast to other service", "|") +{ + if(args.size() != 1) return false; + + // try with number + + TServiceId sid(atoi(args[0].c_str())); + + if(sid.get() == 0) + { + // not a number, try a name + list::iterator it; + for (it = RegisteredServices.begin(); it != RegisteredServices.end (); it++) + { + if ((*it).Name == args[0]) + { + sid = (*it).SId; + break; + } + } + if (it == RegisteredServices.end()) + { + log.displayNL ("Bad service name or id '%s'", args[0].c_str()); + return false; + } + } + + doUnregisterService (sid); + return true; +} + +NLMISC_DYNVARIABLE(uint32, NbRegisteredServices, "display the number of service that are registered in naming service") +{ + if (get) *pointer = RegisteredServices.size(); +} + +NLMISC_COMMAND( displayServiceInstances, "SIM: Display info on service instances", "" ) +{ + SIMInstance->displayInfo( &log ); + return true; +} + +NLMISC_COMMAND( killAllServices, "SIM: Make all the controlled services quit", "" ) +{ + SIMInstance->killAllServices(); + return true; +} diff --git a/code/ryzom/server/src/ryzom_welcome_service/ryzom_welcome_service.cpp b/code/ryzom/server/src/ryzom_welcome_service/ryzom_welcome_service.cpp index 23166383a..bc2e366e2 100644 --- a/code/ryzom/server/src/ryzom_welcome_service/ryzom_welcome_service.cpp +++ b/code/ryzom/server/src/ryzom_welcome_service/ryzom_welcome_service.cpp @@ -1,4 +1,48 @@ +// NeLNS - 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 "nel/misc/types_nl.h" + +#include +#include +#include + +#include + +#include "nel/misc/debug.h" +#include "nel/misc/config_file.h" +#include "nel/misc/displayer.h" +#include "nel/misc/command.h" +#include "nel/misc/variable.h" +#include "nel/misc/log.h" +#include "nel/misc/file.h" +#include "nel/misc/path.h" + +#include "nel/net/service.h" +#include "nel/net/unified_network.h" +#include "nel/net/login_cookie.h" + +#include "ryzom_welcome_service.h" + +#include "game_share/welcome_service_itf.h" + +using namespace std; +using namespace NLMISC; +using namespace NLNET; +using namespace WS; extern void admin_modules_forceLink(); @@ -8,5 +52,1527 @@ void foo() } -#include "../../../../nelns/welcome_service/welcome_service.cpp" -#include "../../../../nelns/welcome_service/welcome_service_itf.cpp" +CVariable PlayerLimit( + "ws","PlayerLimit", "Rough max number of players accepted on this shard (-1 for Unlimited)", + 5000, + 0, true ); + +// Forward declaration of callback cbShardOpen (see ShardOpen variable) +void cbShardOpen(IVariable &var); + +// Forward declaration of callback cbShardOpenStateFile (see ShardOpenStateFile variable) +void cbShardOpenStateFile(IVariable &var); + +// Forward declaration of callback cbUsePatchMode +void cbUsePatchMode(IVariable &var); + +// Types of open state +enum TShardOpenState +{ + ClosedForAll = 0, + OpenOnlyForAllowed = 1, + OpenForAll = 2 +}; + +static bool AllowDispatchMsgToLS = false; + +/** + * ShardOpen + * true if shard is open to public + * 0 means closed for all but :DEV: + * 1 means open only for groups in config file (see OpenGroups variable) and :DEV: + * 2 means open for all + */ +CVariable ShardOpen("ws", "ShardOpen", "Indicates if shard is open to public (0 closed for all but :DEV:, 1 open only for groups in cfg, 2 open for all)", 2, 0, true, cbShardOpen); + +/** + * ShardOpenStateFile + * true if shard is open to public + */ +CVariable ShardOpenStateFile("ws", "ShardOpenStateFile", "Name of the file that contains ShardOpen state", "", 0, true, cbShardOpenStateFile); + +/** + * OpenGroups + */ +CVariable OpenGroups("ws", "OpenGroups", "list of groups allowed at ShardOpen Level 1", "", 0, true); + +/** + * OpenFrontEndThreshold + * The FS balance algorithm works like this: + * - select the least loaded frontend + * - if this frontend has more than the OpenFrontEndThreshold + * - try to open a new frontend + * - reselect least loaded frontend + */ +CVariable OpenFrontEndThreshold("ws", "OpenFrontEndThreshold", "Limit number of players on all FS to decide to open a new FS", 800, 0, true ); + + +/** + * Use Patch mode + */ +CVariable UsePatchMode("ws", "UsePatchMode", "Use Frontends as Patch servers (at FS startup)", true, 0, true, cbUsePatchMode ); + +/** + * Use Patch mode + */ +CVariable DontUseLS("ws", "DontUseLS", "Don't use the login service", false, 0, true); + + +// Shortcut to the module instance +//CWelcomeServiceMod *CWelcomeServiceMod::_Instance = NULL; + + +/** + * Using expected services and current running service instances, this class + * reports a main "online status". + */ +class COnlineServices +{ +public: + + /// Set expected instances. Ex: { "TICKS", "FS", "FS", "FS" } + void setExpectedInstances( CConfigFile::CVar& var ) + { + // Reset "expected" counters (but don't clear the map, keep the running instances) + CInstances::iterator ici; + for ( ici=_Instances.begin(); ici!=_Instances.end(); ++ici ) + { + (*ici).second.Expected = 0; + } + // Rebuild "expected" counters + for ( uint i=0; i!=var.size(); ++i ) + { + ++_Instances[var.asString(i)].Expected; + } + } + + /// Add a service instance + void addInstance( const std::string& serviceName ) + { + ++_Instances[serviceName].Running; + } + + /// Remove a service instance + void removeInstance( const std::string& serviceName ) + { + CInstances::iterator ici = _Instances.find( serviceName ); + if ( ici != _Instances.end() ) + { + --(*ici).second.Running; + + // Remove from the map only if not part of the expected list + if ( ((*ici).second.Expected == 0) && ((*ici).second.Running == 0) ) + { + _Instances.erase( ici ); + } + } + else + { + nlwarning( "Can't remove instance of %s", serviceName.c_str() ); + } + } + + /// Check if all expected instances are online + bool getOnlineStatus() const + { + CInstances::const_iterator ici; + for ( ici=_Instances.begin(); ici!=_Instances.end(); ++ici ) + { + if ( ! ici->second.isOnlineAsExpected() ) + return false; + } + return true; + } + + /// Display contents + void display( NLMISC::CLog& log = *NLMISC::DebugLog ) + { + CInstances::const_iterator ici; + for ( ici=_Instances.begin(); ici!=_Instances.end(); ++ici ) + { + log.displayNL( "%s: %s (%u expected, %u running)", + (*ici).first.c_str(), + (*ici).second.Expected ? ((*ici).second.isOnlineAsExpected() ? "ONLINE" : "MISSING") : "OPTIONAL", + (*ici).second.Expected, (*ici).second.Running ); + } + } + +private: + + struct TInstanceCounters + { + TInstanceCounters() : Expected(0), Running(0) {} + + // If not expected, count as online as well + bool isOnlineAsExpected() const { return Running >= Expected; } + + uint Expected; + uint Running; + }; + + typedef std::map< std::string, TInstanceCounters > CInstances; + + CInstances _Instances; +}; + +/// Online services +COnlineServices OnlineServices; + + +/// Main online status +bool OnlineStatus; + +/// Send changes of status to the LS +void reportOnlineStatus( bool newStatus ) +{ + if ( newStatus != OnlineStatus && AllowDispatchMsgToLS ) + { + if (!DontUseLS) + { + CMessage msgout( "OL_ST" ); + msgout.serial( newStatus ); + CUnifiedNetwork::getInstance()->send( "LS", msgout ); + } + + if (CWelcomeServiceMod::isInitialized()) + { + // send a status report to welcome service client + CWelcomeServiceMod::getInstance()->reportWSOpenState(newStatus); + } + + OnlineStatus = newStatus; + } +} + + + +/// Set the version of the shard. you have to increase it each time the client-server protocol changes. +/// You have to increment the client too (the server and client version must be the same to run correctly) +static const uint32 ServerVersion = 1; + +/// Contains the correspondance between userid and the FES connection where the userid is connected. +map UserIdSockAssociations; + +// ubi hack +string FrontEndAddress; + + + +enum TFESState +{ + PatchOnly, + AcceptClientOnly +}; + +struct CFES +{ + CFES (TServiceId sid) : SId(sid), NbPendingUsers(0), NbUser(0), State(PatchOnly) { } + + TServiceId SId; // Connection to the front end + uint32 NbPendingUsers; // Number of not yet connected users (but rooted to this frontend) + uint32 NbUser; // Number of user currently connected on this front end + + TFESState State; // State of frontend (patching/accepting clients) + std::string PatchAddress; // Address of frontend patching server + + uint32 getUsersCountHeuristic() const + { + return NbUser + NbPendingUsers; + } + + void setToAcceptClients() + { + if (State == AcceptClientOnly) + return; + + // tell FS to accept client + State = AcceptClientOnly; + CMessage msgOpenFES("FS_ACCEPT"); + CUnifiedNetwork::getInstance()->send(SId, msgOpenFES); + + // report state to LS + bool dummy; + reportStateToLS(dummy, true); + } + + void reportStateToLS(bool& reportPatching, bool alive = true) + { + // report to LS + + bool patching = (State == PatchOnly); + if (alive && patching) + reportPatching = true; + + if ( AllowDispatchMsgToLS ) + { + if (!DontUseLS) + { + CMessage msgout("REPORT_FS_STATE"); + msgout.serial(SId); + msgout.serial(alive); + msgout.serial(patching); + msgout.serial(PatchAddress); + CUnifiedNetwork::getInstance()->send("LS", msgout); + } + } + } +}; + +list FESList; + +/* + * Find the best front end service for a new connecting user (return NULL if there is no suitable FES). + * Additionally, calculate totalNbUsers. + */ +CFES *findBestFES ( uint& totalNbUsers ) +{ + totalNbUsers = 0; + + CFES* best = NULL; + + for (list::iterator it=FESList.begin(); it!=FESList.end(); ++it) + { + CFES &fes = *it; + if (fes.State == AcceptClientOnly) + { + if (best == NULL || best->getUsersCountHeuristic() > fes.getUsersCountHeuristic()) + best = &fes; + + totalNbUsers += fes.NbUser; + } + + } + + return best; +} + +/** + * Select a frontend in patch mode to open + * Returns true if a new FES was open, false if no FES could be open + */ +bool openNewFES() +{ + for (list::iterator it=FESList.begin(); it!=FESList.end(); ++it) + { + if ((*it).State == PatchOnly) + { + nlinfo("openNewFES: ask the FS %d to accept clients", it->SId.get()); + + // switch FES to AcceptClientOnly + (*it).setToAcceptClients(); + return true; + } + } + + return false; +} + + + +void displayFES () +{ + nlinfo ("There's %d FES in the list:", FESList.size()); + for (list::iterator it = FESList.begin(); it != FESList.end(); it++) + { + nlinfo(" > %u NbUser:%d NbPendingUser:%d", it->SId.get(), it->NbUser, it->NbPendingUsers); + } + nlinfo ("End of the list"); +} + + + + + + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// CONNECTION TO THE FRONT END SERVICE /////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void cbFESShardChooseShard (CMessage &msgin, const std::string &serviceName, TServiceId sid) +{ + // the WS answer a user authorize + string reason; + CLoginCookie cookie; + string addr; + + // + // S09: receive "SCS" message from FES and send the "SCS" message to the LS + // + + CMessage msgout ("SCS"); + + msgin.serial (reason); + msgout.serial (reason); + + msgin.serial (cookie); + msgout.serial (cookie); + + if (reason.empty()) + { + msgin.serial (addr); + + // if we set the FontEndAddress in the welcome_service.cfg we use this address + if (FrontEndAddress.empty()) + { + msgout.serial (addr); + } + else + { + msgout.serial (FrontEndAddress); + } + + uint32 nbPendingUser; + msgin.serial(nbPendingUser); + + // update the pending user count for this shard + for (list::iterator it = FESList.begin(); it != FESList.end(); it++) + { + if (it->SId == sid) + { + it->NbPendingUsers = nbPendingUser; + break; + } + } + + /* + // OBSOLETE: LS doesn't read patching URLs + // build patch server list + std::string PatchURLS; + for (list::iterator it=FESList.begin(); it!=FESList.end(); ++it) + { + if ((*it).State == PatchOnly && !(*it).PatchAddress.empty()) + { + if (!PatchURLS.empty()) + PatchURLS += '|'; + PatchURLS += (*it).PatchAddress; + } + } + + + msgout.serial(PatchURLS); + */ + } + + if (PendingFeResponse.find(cookie) != PendingFeResponse.end()) + { + nldebug( "ERLOG: SCS recvd from %s-%hu => sending %s to SU", serviceName.c_str(), sid.get(), cookie.toString().c_str()); + + // this response is not waited by LS + TPendingFEResponseInfo &pfri = PendingFeResponse.find(cookie)->second; + + pfri.WSMod->frontendResponse(pfri.WaiterModule, pfri.UserId, reason, cookie, addr); + // cleanup pending record + PendingFeResponse.erase(cookie); + } + else + { + nldebug( "ERLOG: SCS recvd from %s-%hu, but pending %s not found", serviceName.c_str(), sid.get(), cookie.toString().c_str()); + + // return the result to the LS + if (!DontUseLS) + { + CUnifiedNetwork::getInstance()->send ("LS", msgout); + } + } + +} + +// This function is call when a FES accepted a new client or lost a connection to a client +void cbFESClientConnected (CMessage &msgin, const std::string &serviceName, TServiceId sid) +{ + // + // S15: receive "CC" message from FES and send "CC" message to the "LS" + // + + CMessage msgout ("CC"); + + uint32 userid; + msgin.serial (userid); + msgout.serial (userid); + + uint8 con; + msgin.serial (con); + msgout.serial (con); + + if (!DontUseLS) + { + CUnifiedNetwork::getInstance()->send ("LS", msgout); + } + + // add or remove the user number really connected on this shard + uint32 totalNbOnlineUsers = 0, totalNbPendingUsers = 0; + for (list::iterator it = FESList.begin(); it != FESList.end(); it++) + { + if (it->SId == sid) + { + if (con) + { + (*it).NbUser++; + + // the client connected, it's no longer pending + if ((*it).NbPendingUsers > 0) + (*it).NbPendingUsers--; + } + else + { + if ( (*it).NbUser != 0 ) + (*it).NbUser--; + } + } + totalNbOnlineUsers += (*it).NbUser; + totalNbPendingUsers += (*it).NbPendingUsers; + } + + if (CWelcomeServiceMod::isInitialized()) + CWelcomeServiceMod::getInstance()->updateConnectedPlayerCount(totalNbOnlineUsers, totalNbPendingUsers); + + if (con) + { + // we know that this user is on this FES + UserIdSockAssociations.insert (make_pair (userid, sid)); + } + else + { + // remove the user + UserIdSockAssociations.erase (userid); + } + +} + +// This function is called when a FES rejected a client' cookie +void cbFESRemovedPendingCookie(CMessage &msgin, const std::string &serviceName, TServiceId sid) +{ + CLoginCookie cookie; + msgin.serial(cookie); + nldebug( "ERLOG: RPC recvd from %s-%hu => %s removed", serviceName.c_str(), sid.get(), cookie.toString().c_str(), cookie.toString().c_str()); + + + // client' cookie rejected, no longer pending + uint32 totalNbOnlineUsers = 0, totalNbPendingUsers = 0; + for (list::iterator it = FESList.begin(); it != FESList.end(); it++) + { + if ((*it).SId == sid) + { + if ((*it).NbPendingUsers > 0) + --(*it).NbPendingUsers; + } + totalNbOnlineUsers += (*it).NbUser; + totalNbPendingUsers += (*it).NbPendingUsers; + } + + if (CWelcomeServiceMod::isInitialized()) + { + CWelcomeServiceMod::getInstance()->pendingUserLost(cookie); + CWelcomeServiceMod::getInstance()->updateConnectedPlayerCount(totalNbOnlineUsers, totalNbPendingUsers); + } +} + +// This function is called by FES to setup its PatchAddress +void cbFESPatchAddress(CMessage &msgin, const std::string &serviceName, TServiceId sid) +{ + std::string address; + msgin.serial(address); + + bool acceptClients; + msgin.serial(acceptClients); + + nldebug("Received patch server address '%s' from service %s %d", address.c_str(), serviceName.c_str(), sid.get()); + + for (list::iterator it = FESList.begin(); it != FESList.end(); it++) + { + if ((*it).SId == sid) + { + nldebug("Affected patch server address '%s' to frontend %s %d", address.c_str(), serviceName.c_str(), sid.get()); + + if (!UsePatchMode.get() && !acceptClients) + { + // not in patch mode, force fs to accept clients + acceptClients = true; + (*it).setToAcceptClients(); + } + + (*it).PatchAddress = address; + (*it).State = (acceptClients ? AcceptClientOnly : PatchOnly); + if (acceptClients) + nldebug("Frontend %s %d reported to accept client, patching unavailable for that server", address.c_str(), serviceName.c_str(), sid.get()); + else + nldebug("Frontend %s %d reported to be in patching mode", address.c_str(), serviceName.c_str(), sid.get()); + + bool dummy; + (*it).reportStateToLS(dummy); + break; + } + } +} + +// This function is called by FES to setup the right number of players (if FES was already present before WS launching) +void cbFESNbPlayers(CMessage &msgin, const std::string &serviceName, TServiceId sid) +{ + // *********** WARNING ******************* + // This version of the callback is deprecated, the system + // now use cbFESNbPlayers2 that report the pending user count + // as well as the number of connected players. + // It is kept for backward compatibility only. + // *************************************** + + uint32 nbPlayers; + msgin.serial(nbPlayers); + + uint32 totalNbOnlineUsers = 0, totalNbPendingUsers = 0; + for (list::iterator it = FESList.begin(); it != FESList.end(); it++) + { + if ((*it).SId == sid) + { + nldebug("Frontend '%d' reported %d online users", sid.get(), nbPlayers); + (*it).NbUser = nbPlayers; + if (nbPlayers != 0 && (*it).State == PatchOnly) + { + nlwarning("Frontend %d is in state PatchOnly, yet reports to have online %d players, state AcceptClientOnly is forced (FS_ACCEPT message sent)"); + (*it).setToAcceptClients(); + } + } + totalNbOnlineUsers += (*it).NbUser; + totalNbPendingUsers += (*it).NbPendingUsers; + } + + if (CWelcomeServiceMod::isInitialized()) + CWelcomeServiceMod::getInstance()->updateConnectedPlayerCount(totalNbOnlineUsers, totalNbPendingUsers); +} + + +// This function is called by FES to setup the right number of players (if FES was already present before WS launching) +void cbFESNbPlayers2(CMessage &msgin, const std::string &serviceName, TServiceId sid) +{ + uint32 nbPlayers; + uint32 nbPendingPlayers; + msgin.serial(nbPlayers); + msgin.serial(nbPendingPlayers); + + uint32 totalNbOnlineUsers = 0, totalNbPendingUsers = 0; + for (list::iterator it = FESList.begin(); it != FESList.end(); it++) + { + CFES &fes = *it; + if (fes.SId == sid) + { + nldebug("Frontend '%d' reported %d online users", sid.get(), nbPlayers); + fes.NbUser = nbPlayers; + fes.NbPendingUsers = nbPendingPlayers; + if (nbPlayers != 0 && fes.State == PatchOnly) + { + nlwarning("Frontend %d is in state PatchOnly, yet reports to have online %d players, state AcceptClientOnly is forced (FS_ACCEPT message sent)"); + (*it).setToAcceptClients(); + } + } + totalNbOnlineUsers += fes.NbUser; + totalNbPendingUsers += fes.NbPendingUsers; + } + + if (CWelcomeServiceMod::isInitialized()) + CWelcomeServiceMod::getInstance()->updateConnectedPlayerCount(totalNbOnlineUsers, totalNbPendingUsers); +} + +/* + * Set Shard open state + */ +void setShardOpenState(TShardOpenState state, bool writeInVar = true) +{ + if (writeInVar) + ShardOpen = state; + + if ( AllowDispatchMsgToLS ) + { + if (!DontUseLS) + { + // send to LS current shard state + CMessage msgout ("SET_SHARD_OPEN"); + uint8 shardOpenState = (uint8)state; + + msgout.serial (shardOpenState); + CUnifiedNetwork::getInstance()->send ("LS", msgout); + } + } +} + + +/* + * Set Shard Open State + * uint8 Open State (0 closed for all, 1 open for groups in cfg, 2 open for all) + */ +void cbSetShardOpen(CMessage &msgin, const std::string &serviceName, TServiceId sid) +{ + uint8 shardOpenState; + msgin.serial (shardOpenState); + + if (shardOpenState > OpenForAll) + { + shardOpenState = OpenForAll; + } + + setShardOpenState((TShardOpenState)shardOpenState); +} + +// forward declaration to callback +void cbShardOpenStateFile(IVariable &var); + +/* + * Restore Shard Open state from config file or from file if found + */ +void cbRestoreShardOpen(CMessage &msgin, const std::string &serviceName, TServiceId sid) +{ + // first restore state from config file + CConfigFile::CVar* var = IService::getInstance()->ConfigFile.getVarPtr("ShardOpen"); + if (var != NULL) + { + setShardOpenState((TShardOpenState)var->asInt()); + } + + // then restore state from state file, if it exists + cbShardOpenStateFile(ShardOpenStateFile); +} + + + + + +// a new front end connecting to me, add it +void cbFESConnection (const std::string &serviceName, TServiceId sid, void *arg) +{ + FESList.push_back (CFES ((TServiceId)sid)); + nldebug("new FES connection: sid %u", sid.get()); + displayFES (); + + bool dummy; + FESList.back().reportStateToLS(dummy); + + if (!UsePatchMode.get()) + { + FESList.back().setToAcceptClients(); + } +} + + +// a front end closes the connection, deconnect him +void cbFESDisconnection (const std::string &serviceName, TServiceId sid, void *arg) +{ + nldebug("new FES disconnection: sid %u", sid.get()); + + for (list::iterator it = FESList.begin(); it != FESList.end(); it++) + { + if ((*it).SId == sid) + { + // send a message to the LS to say that all players from this FES are offline + map::iterator itc = UserIdSockAssociations.begin(); + map::iterator nitc = itc; + while (itc != UserIdSockAssociations.end()) + { + nitc++; + if ((*itc).second == sid) + { + // bye bye little player + uint32 userid = (*itc).first; + nlinfo ("Due to a frontend crash, removed the player %d", userid); + if (!DontUseLS) + { + CMessage msgout ("CC"); + msgout.serial (userid); + uint8 con = 0; + msgout.serial (con); + CUnifiedNetwork::getInstance()->send ("LS", msgout); + } + UserIdSockAssociations.erase (itc); + } + itc = nitc; + } + + bool dummy; + (*it).reportStateToLS(dummy, false); + + // remove the FES + FESList.erase (it); + + break; + } + } + + // Update the welcome service client with the new count of connection + + uint32 totalNbOnlineUsers =0, totalNbPendingUsers = 0; + for (list::iterator it = FESList.begin(); it != FESList.end(); it++) + { + const CFES &fes = *it; + totalNbOnlineUsers += fes.NbUser; + totalNbPendingUsers += fes.NbPendingUsers; + } + + if (CWelcomeServiceMod::isInitialized()) + CWelcomeServiceMod::getInstance()->updateConnectedPlayerCount(totalNbOnlineUsers, totalNbPendingUsers); + + displayFES (); +} + + +// +void cbServiceUp (const std::string &serviceName, TServiceId sid, void *arg) +{ + OnlineServices.addInstance( serviceName ); + bool online = OnlineServices.getOnlineStatus(); + reportOnlineStatus( online ); + + // send shard id to service + sint32 shardId; + if (IService::getInstance()->haveArg('S')) + { + // use the command line param if set + shardId = atoi(IService::getInstance()->getArg('S').c_str()); + } + else if (IService::getInstance()->ConfigFile.exists ("ShardId")) + { + // use the config file param if set + shardId = IService::getInstance()->ConfigFile.getVar ("ShardId").asInt(); + } + else + { + shardId = -1; + } + + if (shardId == -1) + { + nlerror ("ShardId variable must be valid (>0)"); + } + + CMessage msgout("R_SH_ID"); + msgout.serial(shardId); + CUnifiedNetwork::getInstance()->send (sid, msgout); +} + + +// +void cbServiceDown (const std::string &serviceName, TServiceId sid, void *arg) +{ + OnlineServices.removeInstance( serviceName ); + bool online = OnlineServices.getOnlineStatus(); + reportOnlineStatus( online ); +} + + +// Callback Array for message from FES +TUnifiedCallbackItem FESCallbackArray[] = +{ + { "SCS", cbFESShardChooseShard }, + { "CC", cbFESClientConnected }, + { "RPC", cbFESRemovedPendingCookie }, + { "FEPA", cbFESPatchAddress }, + { "NBPLAYERS", cbFESNbPlayers }, + { "NBPLAYERS2", cbFESNbPlayers2 }, + + { "SET_SHARD_OPEN", cbSetShardOpen }, + { "RESTORE_SHARD_OPEN", cbRestoreShardOpen }, +}; + + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// CONNECTION TO THE LOGIN SERVICE /////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void cbLSChooseShard (CMessage &msgin, const std::string &serviceName, TServiceId sid) +{ + // the LS warns me that a new client want to come in my shard + + nldebug( "ERLOG: CS recvd from %s-%hu", serviceName.c_str(), sid.get()); + + // + // S07: receive the "CS" message from LS and send the "CS" message to the selected FES + // + + CLoginCookie cookie; + msgin.serial (cookie); + string userName, userPriv, userExtended; + msgin.serial (userName); + + try + { + msgin.serial (userPriv); + } + catch (Exception &) + { + nlwarning ("LS didn't give me the user privilege for user '%s', set to empty", userName.c_str()); + } + + try + { + msgin.serial (userExtended); + } + catch (Exception &) + { + nlwarning ("LS didn't give me the extended data for user '%s', set to empty", userName.c_str()); + } + + + string ret = lsChooseShard(userName, cookie, userPriv, userExtended, WS::TUserRole::ur_player, 0xffffffff, ~0); + + if (!ret.empty()) + { + // send back an error message to LS + CMessage msgout ("SCS"); + msgout.serial (ret); + msgout.serial (cookie); + CUnifiedNetwork::getInstance()->send(sid, msgout); + } +} + +//void cbLSChooseShard (CMessage &msgin, const std::string &serviceName, uint16 sid) +std::string lsChooseShard (const std::string &userName, + const CLoginCookie &cookie, + const std::string &userPriv, + const std::string &userExtended, + WS::TUserRole userRole, + uint32 instanceId, + uint32 charSlot) +{ + // the LS warns me that a new client want to come in my shard + + // + // S07: receive the "CS" message from LS and send the "CS" message to the selected FES + // + +/* + uint totalNbUsers; + CFES *best = findBestFES( totalNbUsers ); + if (best == NULL) + { + // answer the LS that we can't accept the user + CMessage msgout ("SCS"); + string reason = "No front-end server available"; + msgout.serial (reason); + msgout.serial (cookie); + CUnifiedNetwork::getInstance()->send(sid, msgout); + return; + } +*/ + + uint totalNbUsers; + CFES* best = findBestFES( totalNbUsers ); + + // could not find a good FES or best FES has more players than balance limit + if (best == NULL || best->getUsersCountHeuristic() >= OpenFrontEndThreshold) + { + // open a new frontend + openNewFES(); + + // reselect best FES (will return newly open FES, or previous if no more FES available) + best = findBestFES(totalNbUsers); + + // check there is a FES available + if (best == NULL) + { + // answer the LS that we can't accept the user + return "No front-end server available"; + } + } + + + bool authorizeUser = false; + bool forceAuthorize = false; + + if (userPriv == ":DEV:") + { + // devs have all privileges + authorizeUser = true; + forceAuthorize = true; + } + else if (ShardOpen != ClosedForAll) + { + const std::string& allowedGroups = OpenGroups; + bool userInOpenGroups = (!userPriv.empty() && !allowedGroups.empty() && allowedGroups.find(userPriv) != std::string::npos); + + // open for all or user is privileged + authorizeUser = (ShardOpen == OpenForAll || userInOpenGroups); + // let authorized users to force access even if limit is reached + forceAuthorize = userInOpenGroups; + } + + bool shardLimitReached = ( (PlayerLimit.get() != -1) && (totalNbUsers >= (uint)PlayerLimit.get()) ); + + if (!forceAuthorize && (!authorizeUser || shardLimitReached)) + { + // answer the LS that we can't accept the user + CMessage msgout ("SCS"); + string reason; + if (shardLimitReached) + return "The shard is currently full, please try again in 5 minutes."; + else + return "The shard is closed."; + } + + + CMessage msgout ("CS"); + msgout.serial (const_cast(cookie)); + msgout.serial (const_cast(userName), const_cast(userPriv), const_cast(userExtended)); + msgout.serial (instanceId); + msgout.serial (charSlot); + + CUnifiedNetwork::getInstance()->send (best->SId, msgout); + best->NbPendingUsers++; + + // Update counts + uint32 totalNbOnlineUsers = 0, totalNbPendingUsers = 0; + for (list::iterator it=FESList.begin(); it!=FESList.end(); ++it) + { + totalNbOnlineUsers += (*it).NbUser; + totalNbPendingUsers += (*it).NbPendingUsers; + } + if (CWelcomeServiceMod::isInitialized()) + CWelcomeServiceMod::getInstance()->updateConnectedPlayerCount(totalNbOnlineUsers, totalNbPendingUsers); + + return ""; +} + +void cbFailed (CMessage &msgin, const std::string &serviceName, TServiceId sid) +{ + // I can't connect to the Login Service, just nlerror (); + string reason; + msgin.serial (reason); + nlerror (reason.c_str()); +} + + +bool disconnectClient(uint32 userId) +{ + map::iterator it = UserIdSockAssociations.find (userId); + if (it == UserIdSockAssociations.end ()) + { + nlinfo ("Login service ask to disconnect user %d, he is not connected here, so ignoring", userId); + return false; + } + else + { + CMessage msgout ("DC"); + msgout.serial (userId); + CUnifiedNetwork::getInstance()->send (it->second, msgout); + + return true; + } +} + +void cbLSDisconnectClient (CMessage &msgin, const std::string &serviceName, TServiceId sid) +{ + // the LS tells me that i have to disconnect a client + + uint32 userid; + msgin.serial (userid); + + disconnectClient(userid); + +} + +// connection to the LS, send the identification message +void cbLSConnection (const std::string &serviceName, TServiceId sid, void *arg) +{ + sint32 shardId; + + if (IService::getInstance()->haveArg('S')) + { + // use the command line param if set + shardId = atoi(IService::getInstance()->getArg('S').c_str()); + } + else if (IService::getInstance()->ConfigFile.exists ("ShardId")) + { + // use the config file param if set + shardId = IService::getInstance()->ConfigFile.getVar ("ShardId").asInt(); + } + else + { + shardId = -1; + } + + if (shardId == -1) + { + nlerror ("ShardId variable must be valid (>0)"); + } + + CMessage msgout ("WS_IDENT"); + msgout.serial (shardId); + CUnifiedNetwork::getInstance()->send (sid, msgout); + + nlinfo ("Connected to %s-%hu and sent identification with shardId '%d'", serviceName.c_str(), sid.get(), shardId); + + // send state to LS + setShardOpenState((TShardOpenState)(ShardOpen.get()), false); + + // + if (!DontUseLS) + { + CMessage msgrpn("REPORT_NO_PATCH"); + CUnifiedNetwork::getInstance()->send("LS", msgrpn); + } + + bool reportPatching = false; + list::iterator itfs; + for (itfs=FESList.begin(); itfs!=FESList.end(); ++itfs) + (*itfs).reportStateToLS(reportPatching); +} + + +// Callback for detection of config file change about "ExpectedServices" +void cbUpdateExpectedServices( CConfigFile::CVar& var ) +{ + OnlineServices.setExpectedInstances( var ); +} + + +/* + * ShardOpen update functions/callbacks etc. + */ + +/** + * updateShardOpenFromFile() + * Update ShardOpen from a file. + * Read a line of text in the file, converts it to int (atoi), then casts into bool for ShardOpen. + */ +void updateShardOpenFromFile(const std::string& filename) +{ + CIFile f; + + if (!f.open(filename)) + { + nlwarning("Failed to update ShardOpen from file '%s', couldn't open file", filename.c_str()); + return; + } + + try + { + char readBuffer[256]; + f.getline(readBuffer, 256); + setShardOpenState((TShardOpenState)atoi(readBuffer)); + + nlinfo("Updated ShardOpen state to '%u' from file '%s'", ShardOpen.get(), filename.c_str()); + } + catch (Exception& e) + { + nlwarning("Failed to update ShardOpen from file '%s', exception raised while getline() '%s'", filename.c_str(), e.what()); + } +} + +std::string ShardOpenStateFileName; + +/** + * cbShardOpen() + * Callback for ShardOpen + */ +void cbShardOpen(IVariable &var) +{ + setShardOpenState((TShardOpenState)(ShardOpen.get()), false); +} + + +/** + * cbShardOpenStateFile() + * Callback for ShardOpenStateFile + */ +void cbShardOpenStateFile(IVariable &var) +{ + // remove previous file change callback + if (!ShardOpenStateFileName.empty()) + { + CFile::removeFileChangeCallback(ShardOpenStateFileName); + nlinfo("Removed callback for ShardOpenStateFileName file '%s'", ShardOpenStateFileName.c_str()); + } + + ShardOpenStateFileName = var.toString(); + + if (!ShardOpenStateFileName.empty()) + { + // set new callback for the file + CFile::addFileChangeCallback(ShardOpenStateFileName, updateShardOpenFromFile); + nlinfo("Set callback for ShardOpenStateFileName file '%s'", ShardOpenStateFileName.c_str()); + + // and update state from file... + updateShardOpenFromFile(ShardOpenStateFileName); + } +} + +/** + * cbUsePatchMode() + * Callback for UsePatchMode + */ +void cbUsePatchMode(IVariable &var) +{ + // if patch mode not set, set all fs in patching mode to accept clients now + if (!UsePatchMode.get()) + { + nlinfo("UsePatchMode disabled, switch all patching servers to actual frontends"); + + list::iterator it; + + for (it=FESList.begin(); it!=FESList.end(); ++it) + { + if ((*it).State == PatchOnly) + { + (*it).setToAcceptClients(); + } + } + } +} + + +// Callback Array for message from LS +TUnifiedCallbackItem LSCallbackArray[] = +{ + { "CS", cbLSChooseShard }, + { "DC", cbLSDisconnectClient }, + { "FAILED", cbFailed }, +}; + +class CWelcomeService : public IService +{ + +public: + + /// Init the service, load the universal time. + void init () + { + string FrontendServiceName = ConfigFile.getVar ("FrontendServiceName").asString(); + + try { FrontEndAddress = ConfigFile.getVar ("FrontEndAddress").asString(); } catch(Exception &) { } + + nlinfo ("Waiting frontend services named '%s'", FrontendServiceName.c_str()); + + CUnifiedNetwork::getInstance()->setServiceUpCallback(FrontendServiceName, cbFESConnection, NULL); + CUnifiedNetwork::getInstance()->setServiceDownCallback(FrontendServiceName, cbFESDisconnection, NULL); + CUnifiedNetwork::getInstance()->setServiceUpCallback("*", cbServiceUp, NULL); + CUnifiedNetwork::getInstance()->setServiceDownCallback("*", cbServiceDown, NULL); + + // add a connection to the LS + string LSAddr; + if (haveArg('T')) + { + // use the command line param if set + LSAddr = getArg('T'); + } + else if (ConfigFile.exists ("LSHost")) + { + // use the config file param if set + LSAddr = ConfigFile.getVar("LSHost").asString(); + } + + if (haveArg('S')) + { + // use the command line param if set + uint shardId = atoi(IService::getInstance()->getArg('S').c_str()); + + nlinfo("Using shard id %u from command line '%s'", shardId, IService::getInstance()->getArg('S').c_str()); + anticipateShardId(shardId); + } + else if (ConfigFile.exists ("ShardId")) + { + // use the config file param if set + uint shardId = IService::getInstance()->ConfigFile.getVar ("ShardId").asInt(); + + nlinfo("Using shard id %u from config file '%s'", shardId, IService::getInstance()->ConfigFile.getVar ("ShardId").asString().c_str()); + anticipateShardId(shardId); + } + + // the config file must have a valid address where the login service is + nlassert(!LSAddr.empty()); + + // add default port if not set by the config file + if (LSAddr.find (":") == string::npos) + LSAddr += ":49999"; + + AllowDispatchMsgToLS = true; + + if (ConfigFile.getVarPtr("DontUseLSService") == NULL + || !ConfigFile.getVar("DontUseLSService").asBool()) + { + // We are using NeL Login Service + CUnifiedNetwork::getInstance()->addCallbackArray(LSCallbackArray, sizeof(LSCallbackArray)/sizeof(LSCallbackArray[0])); + if (!DontUseLS) + { + CUnifiedNetwork::getInstance()->setServiceUpCallback("LS", cbLSConnection, NULL); + CUnifiedNetwork::getInstance()->addService("LS", LSAddr); + } + } + // List of expected service instances + ConfigFile.setCallback( "ExpectedServices", cbUpdateExpectedServices ); + cbUpdateExpectedServices( ConfigFile.getVar( "ExpectedServices" ) ); + + + /* + * read config variable ShardOpenStateFile to update + * + */ + cbShardOpenStateFile(ShardOpenStateFile); + +// // create a welcome service module (for SU comm) +// IModuleManager::getInstance().createModule("WelcomeService", "ws", ""); +// // plug the module in the default gateway +// NLMISC::CCommandRegistry::getInstance().execute("ws.plug wg", InfoLog()); + } + + bool update () + { + // update the service status + + removeStatusTag("DEV_ONLY"); + removeStatusTag("RESTRICTED"); + removeStatusTag("Open"); + + if (ShardOpen == 0) + addStatusTag("DEV_ONLY"); + else if (ShardOpen == 1) + addStatusTag("RESTRICTED"); + else if (ShardOpen == 2) + addStatusTag("Open"); + + return true; + } + +}; + + +static const char* getCompleteServiceName(const IService* theService) +{ + static std::string s; + s= "welcome_service"; + + if (theService->haveLongArg("wsname")) + { + s+= "_"+theService->getLongArg("wsname"); + } + + if (theService->haveLongArg("fullwsname")) + { + s= theService->getLongArg("fullwsname"); + } + + return s.c_str(); +} + +static const char* getShortServiceName(const IService* theService) +{ + static std::string s; + s= "WS"; + + if (theService->haveLongArg("shortwsname")) + { + s= theService->getLongArg("shortwsname"); + } + + return s.c_str(); +} + +// Service instantiation +NLNET_SERVICE_MAIN( CWelcomeService, getShortServiceName(scn), getCompleteServiceName(scn), 0, FESCallbackArray, "", ""); + + +// welcome service module +//class CWelcomeServiceMod : +// public CEmptyModuleCommBehav > >, +// public WS::CWelcomeServiceSkel +//{ +// void onProcessModuleMessage(IModuleProxy *sender, const CMessage &message) +// { +// if (CWelcomeServiceSkel::onDispatchMessage(sender, message)) +// return; +// +// nlwarning("Unknown message '%s' received by '%s'", +// message.getName().c_str(), +// getModuleName().c_str()); +// } +// +// +// ////// CWelcomeServiceSkel implementation +// +// // ask the welcome service to welcome a user +// virtual void welcomeUser(NLNET::IModuleProxy *sender, uint32 userId, const std::string &userName, const CLoginCookie &cookie, const std::string &priviledge, const std::string &exPriviledge, WS::TUserRole mode, uint32 instanceId) +// { +// string ret = lsChooseShard(userName, +// cookie, +// priviledge, +// exPriviledge, +// mode, +// instanceId); +// +// if (!ret.empty()) +// { +// // TODO : correct this +// string fsAddr; +// CWelcomeServiceClientProxy wsc(sender); +// wsc.welcomeUserResult(this, userId, ret.empty(), fsAddr); +// } +// } +// +// // ask the welcome service to disconnect a user +// virtual void disconnectUser(NLNET::IModuleProxy *sender, uint32 userId) +// { +// nlstop; +// } +// +//}; + +namespace WS +{ + + void CWelcomeServiceMod::onModuleUp(IModuleProxy *proxy) + { + if (proxy->getModuleClassName() == "RingSessionManager") + { + if (_RingSessionManager != NULL) + { + nlwarning("WelcomeServiceMod::onModuleUp : receiving module up for RingSessionManager '%s', but already have it as '%s', replacing it", + proxy->getModuleName().c_str(), + _RingSessionManager->getModuleName().c_str()); + } + // store this module as the ring session manager + _RingSessionManager = proxy; + + // say hello to our new friend (transmit fixed session id if set in config file) + nlinfo("Registering welcome service module into session manager '%s'", proxy->getModuleName().c_str()); + uint32 sessionId = 0; + CConfigFile::CVar *varFixedSessionId = IService::getInstance()->ConfigFile.getVarPtr( "FixedSessionId" ); + if ( varFixedSessionId ) + sessionId = varFixedSessionId->asInt(); + CWelcomeServiceClientProxy wscp(proxy); + wscp.registerWS(this, IService::getInstance()->getShardId(), sessionId, OnlineServices.getOnlineStatus()); + + // Send counts + uint32 totalNbOnlineUsers = 0, totalNbPendingUsers = 0; + for (list::iterator it=FESList.begin(); it!=FESList.end(); ++it) + { + totalNbOnlineUsers += (*it).NbUser; + totalNbPendingUsers += (*it).NbPendingUsers; + } + CWelcomeServiceMod::getInstance()->updateConnectedPlayerCount(totalNbOnlineUsers, totalNbPendingUsers); + } + else if (proxy->getModuleClassName() == "LoginService") + { + _LoginService = proxy; + } + } + + void CWelcomeServiceMod::onModuleDown(IModuleProxy *proxy) + { + if (_RingSessionManager == proxy) + { + // remove this module as the ring session manager + _RingSessionManager = NULL; + } + else if (_LoginService == proxy) + _LoginService = NULL; + } + + + void CWelcomeServiceMod::welcomeUser(NLNET::IModuleProxy *sender, uint32 charId, const std::string &userName, const CLoginCookie &cookie, const std::string &priviledge, const std::string &exPriviledge, WS::TUserRole mode, uint32 instanceId) + { + nldebug( "ERLOG: welcomeUser(%u,%s,%s,%s,%s,%u,%u)", charId, userName.c_str(), cookie.toString().c_str(), priviledge.c_str(), exPriviledge.c_str(), (uint)mode.getValue(), instanceId ); + string ret = lsChooseShard(userName, + cookie, + priviledge, + exPriviledge, + mode, + instanceId, + charId & 0xF); + + uint32 userId = charId >> 4; + if (!ret.empty()) + { + nldebug( "ERLOG: lsChooseShard returned an error => welcomeUserResult"); + // TODO : correct this + string fsAddr; + CWelcomeServiceClientProxy wsc(sender); + wsc.welcomeUserResult(this, userId, false, fsAddr, ret); + } + else + { + nldebug( "ERLOG: lsChooseShard OK => adding to pending"); + TPendingFEResponseInfo pfri; + pfri.WSMod = this; + pfri.UserId = userId; + pfri.WaiterModule = sender; + PendingFeResponse.insert(make_pair(cookie, pfri)); + } + } + + void CWelcomeServiceMod::pendingUserLost(const NLNET::CLoginCookie &cookie) + { + if (!_LoginService) + return; + + CLoginServiceProxy ls(_LoginService); + + ls.pendingUserLost(this, cookie); + } + + + // register the module + NLNET_REGISTER_MODULE_FACTORY(CWelcomeServiceMod, "WelcomeService"); + +} // namespace WS + + +// +// Variables +// + +NLMISC_DYNVARIABLE(uint32, OnlineUsersNumber, "number of connected users on this shard") +{ + // we can only read the value + if (get) + { + uint32 nbusers = 0; + for (list::iterator it = FESList.begin(); it != FESList.end (); it++) + { + nbusers += (*it).NbUser; + } + *pointer = nbusers; + } +} + + +// +// Commands +// + + +NLMISC_COMMAND (frontends, "displays the list of all registered front ends", "") +{ + if(args.size() != 0) return false; + + log.displayNL ("Display the %d registered front end :", FESList.size()); + for (list::iterator it = FESList.begin(); it != FESList.end (); it++) + { +// log.displayNL ("> FE %u: nb estimated users: %u nb users: %u, nb pending users : %u", + log.displayNL ("> FE %u: nb users: %u, nb pending users : %u", + it->SId.get(), + it->NbUser, + it->NbPendingUsers); + } + log.displayNL ("End ot the list"); + + return true; +} + +NLMISC_COMMAND (users, "displays the list of all registered users", "") +{ + if(args.size() != 0) return false; + + log.displayNL ("Display the %d registered users :", UserIdSockAssociations.size()); + for (map::iterator it = UserIdSockAssociations.begin(); it != UserIdSockAssociations.end (); it++) + { + log.displayNL ("> %u SId=%u", (*it).first, (*it).second.get()); + } + log.displayNL ("End ot the list"); + + return true; +} + +NLMISC_COMMAND( displayOnlineServices, "Display the online service instances", "" ) +{ + OnlineServices.display( log ); + return true; +} + +NLMISC_VARIABLE( bool, OnlineStatus, "Main online status of the shard" ); diff --git a/code/ryzom/server/src/ryzom_welcome_service/ryzom_welcome_service.h b/code/ryzom/server/src/ryzom_welcome_service/ryzom_welcome_service.h new file mode 100644 index 000000000..92d1a5986 --- /dev/null +++ b/code/ryzom/server/src/ryzom_welcome_service/ryzom_welcome_service.h @@ -0,0 +1,112 @@ +// NeLNS - 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 "nel/misc/types_nl.h" +#include "nel/misc/singleton.h" + +#include "nel/net/module_manager.h" +#include "nel/net/module_builder_parts.h" + +#include "game_share/welcome_service_itf.h" + +std::string lsChooseShard (const std::string &userName, + const NLNET::CLoginCookie &cookie, + const std::string &userPriv, + const std::string &userExtended, + WS::TUserRole userRole, + uint32 instanceId, + uint32 charSlot); + + +bool disconnectClient(uint32 userId); + + +namespace WS +{ + // welcome service module + class CWelcomeServiceMod : + public NLNET::CEmptyModuleCommBehav > >, + public WS::CWelcomeServiceSkel, + public NLMISC::CManualSingleton + { + /// the ring session manager module (if any) + NLNET::TModuleProxyPtr _RingSessionManager; + /// the login service module (if any) + NLNET::TModuleProxyPtr _LoginService; + + void onModuleUp(NLNET::IModuleProxy *proxy); + void onModuleDown(NLNET::IModuleProxy *proxy); + + + ////// CWelcomeServiceSkel implementation + + // ask the welcome service to welcome a user + virtual void welcomeUser(NLNET::IModuleProxy *sender, uint32 userId, const std::string &userName, const NLNET::CLoginCookie &cookie, const std::string &priviledge, const std::string &exPriviledge, WS::TUserRole mode, uint32 instanceId); + + // ask the welcome service to disconnect a user + virtual void disconnectUser(NLNET::IModuleProxy *sender, uint32 userId) + { + disconnectClient(userId); + } + + public: + CWelcomeServiceMod() + { + CWelcomeServiceSkel::init(this); + } + + void reportWSOpenState(bool shardOpen) + { + if (_RingSessionManager == NULL) // skip if the RSM is offline + return; + + CWelcomeServiceClientProxy wscp(_RingSessionManager); + wscp.reportWSOpenState(this, shardOpen); + } + + // forward response from the front end for a player slot to play in + // to the client of this welcome service (usually the Ring Session Manager) + void frontendResponse(NLNET::IModuleProxy *waiterModule, uint32 userId, const std::string &reason, const NLNET::CLoginCookie &cookie, const std::string &fsAddr) + { + CWelcomeServiceClientProxy wscp(waiterModule); + wscp.welcomeUserResult(this, userId, reason.empty(), fsAddr, reason); + } + + // send the current number of players on this shard to the Ring Session Manager + void updateConnectedPlayerCount(uint32 nbOnlinePlayers, uint32 nbPendingPlayers) + { + if (_RingSessionManager == NULL) // skip if the RSM is offline + return; + + CWelcomeServiceClientProxy wscp(_RingSessionManager); + wscp.updateConnectedPlayerCount(this, nbOnlinePlayers, nbPendingPlayers); + } + + // inform the LS that a pending client is lost + void pendingUserLost(const NLNET::CLoginCookie &cookie); + }; + + struct TPendingFEResponseInfo + { + CWelcomeServiceMod *WSMod; + NLNET::TModuleProxyPtr WaiterModule; + uint32 UserId; + }; + typedef std::map TPendingFeReponses; + // the list of cookie string that are pending an + TPendingFeReponses PendingFeResponse; + +} // namespace WS diff --git a/code/ryzom/server/src/ryzom_welcome_service/ryzom_welcome_service.vcproj b/code/ryzom/server/src/ryzom_welcome_service/ryzom_welcome_service.vcproj index b83039254..c223fb76a 100644 --- a/code/ryzom/server/src/ryzom_welcome_service/ryzom_welcome_service.vcproj +++ b/code/ryzom/server/src/ryzom_welcome_service/ryzom_welcome_service.vcproj @@ -94,82 +94,6 @@ Name="VCPostBuildEventTool" /> - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + diff --git a/code/ryzom/server/src/shard_unifier_service/login_service.cpp b/code/ryzom/server/src/shard_unifier_service/login_service.cpp index b8d04e5aa..ad0ff1f5f 100644 --- a/code/ryzom/server/src/shard_unifier_service/login_service.cpp +++ b/code/ryzom/server/src/shard_unifier_service/login_service.cpp @@ -5,7 +5,7 @@ #include "nel/net/module_builder_parts.h" #include "nel/net/login_cookie.h" -#include "../../nelns/welcome_service/welcome_service_itf.h" +#include "game_share/welcome_service_itf.h" #include "game_share/utils.h" #include "server_share/mysql_wrapper.h" @@ -23,8 +23,6 @@ using namespace MSW; using namespace RSMGR; using namespace ENTITYLOC; -#include "../../../../nelns/welcome_service/welcome_service_itf.cpp" - namespace LS { diff --git a/code/ryzom/server/src/shard_unifier_service/ring_session_manager.cpp b/code/ryzom/server/src/shard_unifier_service/ring_session_manager.cpp index 7dc259524..0d2aa8882 100644 --- a/code/ryzom/server/src/shard_unifier_service/ring_session_manager.cpp +++ b/code/ryzom/server/src/shard_unifier_service/ring_session_manager.cpp @@ -4,7 +4,7 @@ #include "nel/net/module_builder_parts.h" #include "nel/net/callback_server.h" -#include "../../nelns/welcome_service/welcome_service_itf.h" +#include "game_share/welcome_service_itf.h" #include "game_share/r2_types.h"