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"