// NeL - 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 "stdnet.h" #include "nel/misc/app_context.h" #include "nel/misc/dynloadlib.h" #include "nel/misc/command.h" #include "nel/misc/path.h" #include "nel/misc/twin_map.h" #include "nel/misc/sstring.h" #include "nel/misc/smart_ptr_inline.h" #include "nel/net/module_common.h" #include "nel/net/module_manager.h" #include "nel/net/service.h" #include "nel/net/module_gateway.h" #include "nel/net/module.h" #include "nel/net/module_socket.h" #include "nel/net/net_log.h" using namespace std; using namespace NLMISC; // for init of the module manager (if not already done by the application) NLMISC_CATEGORISED_COMMAND(net, initModuleManager, "force the initialisation of the module manager", "") { nlunreferenced(rawCommandString); nlunreferenced(args); nlunreferenced(quiet); nlunreferenced(human); // not really hard in fact :) NLNET::IModuleManager::getInstance(); log.displayNL("Module manager is now initialised."); return true; } namespace NLNET { /// Implementation class for module manager class CModuleManager : public IModuleManager, public ICommandsHandler { NLMISC_SAFE_SINGLETON_DECL(CModuleManager); public: static void releaseInstance() { if (_Instance) { NLMISC::INelContext::getInstance().releaseSingletonPointer("CModuleManager", _Instance); delete _Instance; _Instance = NULL; } } struct TModuleLibraryInfo : public CRefCount { /// The file name of the library with access path std::string FullPathLibraryName; /// The short name of the library, i.e not path, no decoration (.dll or .so and NeL compilation mode tag) std::string ShortLibraryName; /// The list of module factory name that are in use in the library. std::vector ModuleFactoryList; /// The library handler CLibrary LibraryHandler; /// the NeL Module library interface pointer. CNelModuleLibrary *ModuleLibrary; }; /** the user set unique name root, replace the normal unique name * generation system if not empty */ string _UniqueNameRoot; typedef map > TModuleLibraryInfos; /// Module library registry TModuleLibraryInfos _ModuleLibraryRegistry; typedef std::map TModuleFactoryRegistry; /// Module factory registry TModuleFactoryRegistry _ModuleFactoryRegistry; typedef NLMISC::CTwinMap TModuleInstances; /// Modules instances tracker TModuleInstances _ModuleInstances; typedef NLMISC::CTwinMap TModuleIds; /// Modules IDs tracker TModuleIds _ModuleIds; /// Local module ID generator TModuleId _LastGeneratedId; typedef NLMISC::CTwinMap TModuleProxyIds; /// Modules proxy IDs tracker TModuleProxyIds _ModuleProxyIds; typedef map TModuleSockets; /// Module socket registry TModuleSockets _ModuleSocketsRegistry; typedef std::map TModuleGateways; /// Module factory registry TModuleGateways _ModuleGatewaysRegistry; /////////////////////////////////////////////////////////////////// // Methods /////////////////////////////////////////////////////////////////// static bool isInitialized() { return _Instance != NULL; } const std::string &getCommandHandlerName() const { static string moduleManagerName("moduleManager"); return moduleManagerName; } CModuleManager() : IModuleManager(), ICommandsHandler(), _LastGeneratedId(0) { registerCommandsHandler(); // register local module factory TLocalModuleFactoryRegistry &lmfr = TLocalModuleFactoryRegistry::instance(); addModuleFactoryRegistry(lmfr); } ~CModuleManager() { // unload any loaded module library while (!_ModuleLibraryRegistry.empty()) { TModuleLibraryInfo *mli = _ModuleLibraryRegistry.begin()->second; unloadModuleLibrary(mli->ShortLibraryName); } // delete any lasting modules while (!_ModuleInstances.getAToBMap().empty()) { deleteModule(_ModuleInstances.getAToBMap().begin()->second); } // there should not be proxies or gateway lasting // nlassert(_ModuleProxyInstances.getAToBMap().empty()); _Instance = NULL; } virtual void applicationExit() { TModuleInstances::TAToBMap::const_iterator first(_ModuleInstances.getAToBMap().begin()), last(_ModuleInstances.getAToBMap().end()); for (; first != last; ++first) { IModule *module = first->second; module->onApplicationExit(); } } virtual void setUniqueNameRoot(const std::string &uniqueNameRoot) { _UniqueNameRoot = uniqueNameRoot; } virtual const std::string &getUniqueNameRoot() { if (_UniqueNameRoot.empty()) { string hostName; if (IService::isServiceInitialized()) hostName = IService::getInstance()->getHostName(); else hostName = ::NLNET::CInetAddress::localHost().hostName(); int pid = ::getpid(); _UniqueNameRoot = hostName+":"+toString(pid); } return _UniqueNameRoot; } void addModuleFactoryRegistry(TLocalModuleFactoryRegistry &moduleFactoryRegistry) { vector moduleList; moduleFactoryRegistry.fillFactoryList(moduleList); // fill the module factory registry vector::iterator first(moduleList.begin()), last(moduleList.end()); for (; first != last; ++first) { const std::string &className = *first; IModuleFactory *factory = moduleFactoryRegistry.getFactory(className); if (_ModuleFactoryRegistry.find(*first) != _ModuleFactoryRegistry.end()) { // a module class of that name already exist nlinfo("CModuleManger : add module factory : module class '%s' is already registered; ignoring new factory @%p", className.c_str(), factory); } else { // store the factory nlinfo("Adding module '%s' factory", className.c_str()); _ModuleFactoryRegistry.insert(make_pair(className, factory)); } } } virtual bool loadModuleLibrary(const std::string &libraryName) { // build the short name string path = CFile::getPath(libraryName); string shortName = CLibrary::cleanLibName(libraryName); if (_ModuleLibraryRegistry.find(shortName) != _ModuleLibraryRegistry.end()) { // this lib is already loaded ! nlwarning("CModuleManager : trying to load library '%s' in '%s'\n" "but it is already loaded as '%s', ignoring.", shortName.c_str(), libraryName.c_str(), _ModuleLibraryRegistry[shortName]->FullPathLibraryName.c_str()); return false; } // now, load the library string fullName = NLMISC::CPath::standardizePath(path)+CLibrary::makeLibName(shortName); std::auto_ptr mli = auto_ptr(new TModuleLibraryInfo); if (!mli->LibraryHandler.loadLibrary(fullName, false, true, true)) { nlwarning("CModuleManager : failed to load the library '%s' in '%s'", shortName.c_str(), fullName.c_str()); return false; } // the lib is loaded, check that it is a 'pure' nel library if (!mli->LibraryHandler.isLibraryPure()) { nlwarning("CModuleManager : the library '%s' is not a pure Nel library", shortName.c_str()); return false; } // Check that the lib is a pure module library CNelModuleLibrary *modLib = dynamic_cast(mli->LibraryHandler.getNelLibraryInterface()); if (modLib == NULL) { nlwarning("CModuleManager : the library '%s' is not a pure Nel Module library", shortName.c_str()); return false; } // ok, all is fine ! we can store the loaded library info mli->FullPathLibraryName = fullName; TLocalModuleFactoryRegistry &lmfr = modLib->getLocalModuleFactoryRegistry(); lmfr.fillFactoryList(mli->ModuleFactoryList); mli->ModuleLibrary = modLib; mli->ShortLibraryName = shortName; pair ret = _ModuleLibraryRegistry.insert(make_pair(shortName, mli.release())); if (!ret.second) { nlwarning("CModuleManager : failed to store module library information !"); return false; } // fill the module factory registry addModuleFactoryRegistry(lmfr); return true; } virtual bool unloadModuleLibrary(const std::string &libraryName) { string shorName = CLibrary::cleanLibName(libraryName); TModuleLibraryInfos::iterator it(_ModuleLibraryRegistry.find(shorName)); if (it == _ModuleLibraryRegistry.end()) { nlwarning("CModuleManager : failed to unload library '%s' : unknown or not loaded library", shorName.c_str()); return false; } // by erasing this entry, we delete the CLibrary object that hold the library // module, thus destroying any factory the belong into and deleting the // instantiated module ! // Wow, that's pretty much for a single line ;) _ModuleLibraryRegistry.erase(it); return true; } /** Register a module factory in the manager */ // virtual void registerModuleFactory(class IModuleFactory *moduleFactory) // { // nlstop; // } /** Unregister a module factory */ virtual void unregisterModuleFactory(class IModuleFactory *moduleFactory) { // we need to remove the factory from the registry TModuleFactoryRegistry::iterator it(_ModuleFactoryRegistry.find(moduleFactory->getModuleClassName())); if (it == _ModuleFactoryRegistry.end()) { nlwarning("The module factory for class '%s' in not registered.", moduleFactory->getModuleClassName().c_str()); return; } else if (it->second != moduleFactory) { nlinfo("The module factory @%p for class '%s' is not the registered one, ignoring.", moduleFactory, moduleFactory->getModuleClassName().c_str()); return; } nlinfo("ModuleManager : unregistering factory for module class '%s'", moduleFactory->getModuleClassName().c_str()); // remove the factory _ModuleFactoryRegistry.erase(it); } /** Fill the vector with the list of available module. * Note that the vector is not cleared before being filled. */ virtual void getAvailableModuleClassList(std::vector &moduleClassList) { TModuleFactoryRegistry::iterator first(_ModuleFactoryRegistry.begin()), last(_ModuleFactoryRegistry.end()); for (; first != last; ++first) { moduleClassList.push_back(first->first); } } /** Create a new module instance. * The method create a module of the specified class with the * specified local name. * The class MUST be available in the factory and the * name MUST be unique OR empty. * If the name is empty, the method generate a name using * the module class and a number. */ virtual IModule *createModule(const std::string &className, const std::string &localName, const std::string ¶mString) { TModuleFactoryRegistry::iterator it(_ModuleFactoryRegistry.find(className)); if (it == _ModuleFactoryRegistry.end()) { nlwarning("createModule : unknown module class '%s'", className.c_str()); return NULL; } string moduleName = localName; if (moduleName.empty()) { // we need to generate a name uint i=0; do { moduleName = className+toString(i++); } while (_ModuleInstances.getB(moduleName) != NULL); } else { // check that the module name is unique if (_ModuleInstances.getB(moduleName) != NULL) { nlwarning("createModule : the name '%s' is already used by another module, can't instantiate the module", moduleName.c_str()); return NULL; } } IModuleFactory *mf = it->second; // sanity check nlassert(mf->getModuleClassName() == className); std::auto_ptr module = auto_ptr(mf->createModule()); if (module.get() == NULL) { nlwarning("createModule : factory failed to create a module instance for class '%s'", className.c_str()); return NULL; } CModuleBase *modBase = dynamic_cast(module.get()); if (modBase == NULL) { nlwarning("Invalid module returned by factory for class '%s'", className.c_str()); return NULL; } // init the module basic data modBase->_ModuleName = moduleName; modBase->_ModuleId = ++_LastGeneratedId; // init the module with parameter string TParsedCommandLine mii; mii.parseParamList(paramString); bool initResult = module->initModule(mii); // store the module in the manager _ModuleInstances.add(moduleName, module.get()); _ModuleIds.add(modBase->_ModuleId, module.get()); if (initResult) { // ok, all is fine, return the module return module.release(); } else { // error during initialization, delete the module nlwarning("Create module : the new module '%s' of class '%s' has failed to initilize properly.",\ moduleName.c_str(), className.c_str()); deleteModule(module.release()); return NULL; } } void deleteModule(IModule *module) { nlassert(module != NULL); // remove module from trackers nlassert(_ModuleInstances.getA(module) != NULL); nlassert(_ModuleIds.getA(module) != NULL); _ModuleInstances.removeWithB(module); _ModuleIds.removeWithB(module); // unplug the module if needed vector sockets; module->getPluggedSocketList(sockets); for (uint i=0; iunplugModule(sockets[i]); } // ask the factory to delete the module CModuleBase *modBase = dynamic_cast(module); nlassert(modBase != NULL); modBase->getFactory()->deleteModule(module); } /** Lookup in the created module for a module having the * specified local name. */ virtual IModule *getLocalModule(const std::string &moduleName) { TModuleInstances::TAToBMap::const_iterator it(_ModuleInstances.getAToBMap().find(moduleName)); if (it == _ModuleInstances.getAToBMap().end()) return NULL; else return it->second; } virtual void updateModules() { H_AUTO(CModuleManager_updateModules); // module are updated in creation order (i.e in module ID order) TModuleIds::TAToBMap::const_iterator first(_ModuleIds.getAToBMap().begin()), last(_ModuleIds.getAToBMap().end()); for (; first != last; ++first) { TModulePtr module = first->second; CModuleBase *modBase = dynamic_cast(module.getPtr()); if (modBase != NULL) { // look for module task to run while (!modBase->_ModuleTasks.empty()) { CModuleTask *task = modBase->_ModuleTasks.front(); task->resume(); // check for finished task if (task->isFinished()) { LNETL6_DEBUG("NLNETL6: updateModule : task %p is finished, delete and remove from task list", task); // delete the task and resume the next one if any delete task; modBase->_ModuleTasks.erase(modBase->_ModuleTasks.begin()); } else { // no more work for this update break; } } } { H_AUTO(CModuleManager_updateModules_2); // update the module internal first->second->onModuleUpdate(); } } } /** Lookup in the created socket for a socket having the * specified local name. */ virtual IModuleSocket *getModuleSocket(const std::string &socketName) { TModuleSockets::iterator it(_ModuleSocketsRegistry.find(socketName)); if (it == _ModuleSocketsRegistry.end()) return NULL; else return it->second; } /** Register a socket in the manager. */ virtual void registerModuleSocket(IModuleSocket *moduleSocket) { nlassert(moduleSocket != NULL); TModuleSockets::iterator it(_ModuleSocketsRegistry.find(moduleSocket->getSocketName())); nlassert(it == _ModuleSocketsRegistry.end()); nldebug("Registering module socket '%s'", moduleSocket->getSocketName().c_str()); _ModuleSocketsRegistry.insert(make_pair(moduleSocket->getSocketName(), moduleSocket)); } /** Unregister a socket in the manager. */ virtual void unregisterModuleSocket(IModuleSocket *moduleSocket) { nlassert(moduleSocket != NULL); TModuleSockets::iterator it(_ModuleSocketsRegistry.find(moduleSocket->getSocketName())); nlassert(it != _ModuleSocketsRegistry.end()); nldebug("Unregistering module socket '%s'", moduleSocket->getSocketName().c_str()); _ModuleSocketsRegistry.erase(it); } /** Lookup in the created gateway for a gateway having the * specified local name. */ virtual IModuleGateway *getModuleGateway(const std::string &gatewayName) { TModuleGateways::iterator it(_ModuleGatewaysRegistry.find(gatewayName)); if (it == _ModuleGatewaysRegistry.end()) return NULL; else return it->second; } /** Register a gateway in the manager. */ virtual void registerModuleGateway(IModuleGateway *moduleGateway) { nlassert(moduleGateway != NULL); TModuleGateways::iterator it(_ModuleGatewaysRegistry.find(moduleGateway->getGatewayName())); nlassert(it == _ModuleGatewaysRegistry.end()); nldebug("Registering module gateway '%s'", moduleGateway->getGatewayName().c_str()); _ModuleGatewaysRegistry.insert(make_pair(moduleGateway->getGatewayName(), moduleGateway)); } /** Unregister a socket in the manager. */ virtual void unregisterModuleGateway(IModuleGateway *moduleGateway) { nlassert(moduleGateway != NULL); TModuleGateways::iterator it(_ModuleGatewaysRegistry.find(moduleGateway->getGatewayName())); nlassert(it != _ModuleGatewaysRegistry.end()); nldebug("Unregistering module gateway '%s'", moduleGateway->getGatewayName().c_str()); _ModuleGatewaysRegistry.erase(it); } /** Get a module proxy with the module ID */ virtual TModuleProxyPtr getModuleProxy(TModuleId moduleProxyId) { const TModuleProxyPtr *pproxy = _ModuleProxyIds.getB(moduleProxyId); if (pproxy == NULL) return NULL; else return *pproxy; } /** Called by a module that is begin destroyed. * This remove module information from the * the manager */ // virtual void onModuleDeleted(IModule *module) // { // // not needed ? to remove // } virtual IModuleProxy *createModuleProxy( IModuleGateway *gateway, CGatewayRoute *route, uint32 distance, IModule *localModule, const std::string &moduleClassName, const std::string &moduleFullyQualifiedName, const std::string &moduleManifest, TModuleId foreignModuleId) { std::auto_ptr modProx = auto_ptr(new CModuleProxy(localModule, ++_LastGeneratedId, moduleClassName, moduleFullyQualifiedName, moduleManifest)); modProx->_Gateway = gateway; modProx->_Route = route; modProx->_Distance = distance; modProx->_ForeignModuleId = foreignModuleId; nldebug("Creating module proxy (ID : %u, foreign ID : %u, name : '%s', class : '%s' at %u hop", modProx->getModuleProxyId(), modProx->getForeignModuleId(), modProx->getModuleName().c_str(), modProx->getModuleClassName().c_str(), modProx->_Distance); // _ModuleProxyInstances.add(moduleFullyQualifiedName, TModuleProxyPtr(modProx.get())); _ModuleProxyIds.add(modProx->getModuleProxyId(), TModuleProxyPtr(modProx.get())); return modProx.release(); } virtual void releaseModuleProxy(TModuleId moduleProxyId) { TModuleProxyIds::TAToBMap::const_iterator it(_ModuleProxyIds.getAToBMap().find(moduleProxyId)); nlassert(it != _ModuleProxyIds.getAToBMap().end()); CRefPtr sanityCheck(it->second.getPtr()); nldebug("Releasing module proxy ('%s', ID : %u)", it->second->getModuleName().c_str(), moduleProxyId); // remove the smart ptr, must delete the proxy // _ModuleProxyInstances.removeWithB(it->second); _ModuleProxyIds.removeWithB(it->second); nlassertex(sanityCheck == NULL, ("Someone has kept a smart pointer on the proxy '%s' of class '%s'", sanityCheck->getModuleName().c_str(), sanityCheck->getModuleClassName().c_str())); } virtual uint32 getNbModule() { return (uint32)_ModuleInstances.getAToBMap().size(); } virtual uint32 getNbModuleProxy() { return (uint32)_ModuleProxyIds.getAToBMap().size(); } NLMISC_COMMAND_HANDLER_TABLE_BEGIN(CModuleManager) NLMISC_COMMAND_HANDLER_ADD(CModuleManager, dump, "dump various information about module manager state", ""); NLMISC_COMMAND_HANDLER_ADD(CModuleManager, loadLibrary, "load a pure nel library module (give the path if needed and only the undecorated lib name)", "[path]"); NLMISC_COMMAND_HANDLER_ADD(CModuleManager, unloadLibrary, "unload a pure nel library module (give the undecorated name, any path will be removed", ""); NLMISC_COMMAND_HANDLER_ADD(CModuleManager, createModule, "create a new module instance", " [*]"); NLMISC_COMMAND_HANDLER_ADD(CModuleManager, deleteModule, "delete a module instance", ""); NLMISC_COMMAND_HANDLER_TABLE_END NLMISC_CLASS_COMMAND_DECL(deleteModule) { nlunreferenced(rawCommandString); nlunreferenced(quiet); nlunreferenced(human); if (args.size() != 1) return false; TModulePtr const *module = _ModuleInstances.getB(args[0]); if (module == NULL) { log.displayNL("Unknow module '%s'", args[0].c_str()); return false; } nlinfo("Deleting module '%s'", args[0].c_str()); CRefPtr sanityCheck(*module); deleteModule(*module); if (sanityCheck != NULL) { log.displayNL("Failed to delete the module instance !"); return false; } return true; } NLMISC_CLASS_COMMAND_DECL(unloadLibrary) { nlunreferenced(rawCommandString); nlunreferenced(log); nlunreferenced(quiet); nlunreferenced(human); if (args.size() != 1) return false; unloadModuleLibrary(args[0]); return true; } NLMISC_CLASS_COMMAND_DECL(loadLibrary) { nlunreferenced(log); nlunreferenced(quiet); nlunreferenced(human); if (args.size() < 1) return false; CSString libName = rawCommandString; // remove the command name libName.strtok(" \t"); // load the library return this->loadModuleLibrary(libName); } NLMISC_CLASS_COMMAND_DECL(createModule) { nlunreferenced(log); nlunreferenced(quiet); nlunreferenced(human); if (args.size() < 2) return false; CSString moduleArgs = rawCommandString; // remove the command name moduleArgs.strtok(" \t"); // retrieve module class string moduleClass = moduleArgs.strtok(" \t"); // retrieve module instance name string moduleName = moduleArgs.strtok(" \t"); nlinfo("Creating module '%s' of class '%s' with params '%s'", moduleName.c_str(), moduleClass.c_str(), moduleArgs.c_str()); // create the module instance IModule *module = createModule(moduleClass, moduleName, moduleArgs); return module != NULL; } NLMISC_CLASS_COMMAND_DECL(dump) { nlunreferenced(rawCommandString); nlunreferenced(args); nlunreferenced(quiet); nlunreferenced(human); log.displayNL("Dumping CModuleManager internal states :"); { std::vector moduleList; TLocalModuleFactoryRegistry::instance().fillFactoryList(moduleList); log.displayNL(" List of %u local modules classes :", moduleList.size()); for (uint i=0; igetInitStringHelp().c_str()); } } } { log.displayNL(" List of %u loaded module libraries :", _ModuleLibraryRegistry.size()); TModuleLibraryInfos::iterator first(_ModuleLibraryRegistry.begin()), last(_ModuleLibraryRegistry.end()); for (; first != last; ++first) { TModuleLibraryInfo &mli = *(first->second); log.displayNL(" Library '%s' loaded from '%s', contains %u modules classes:", first->first.c_str(), mli.FullPathLibraryName.c_str(), mli.ModuleFactoryList.size()); { vector::iterator f2(mli.ModuleFactoryList.begin()), l2(mli.ModuleFactoryList.end()); for (uint i=0; isecond; log.displayNL(" ID:%5u : \tname = '%s' \tclass = '%s'", module->getModuleId(), module->getModuleName().c_str(), module->getModuleClassName().c_str()); } } { log.displayNL(" List of %u module proxies :", _ModuleProxyIds.getAToBMap().size()); TModuleProxyIds::TAToBMap::const_iterator first(_ModuleProxyIds.getAToBMap().begin()), last(_ModuleProxyIds.getAToBMap().end()); for (; first != last; ++first) { IModuleProxy *modProx = first->second; if (modProx->getGatewayRoute() != NULL) { log.displayNL(" ID:%5u (Foreign ID : %u) : \tname = '%s' \tclass = '%s'", modProx->getModuleProxyId(), modProx->getForeignModuleId(), modProx->getModuleName().c_str(), modProx->getModuleClassName().c_str()); } else { log.displayNL(" ID:%5u (Local proxy for module ID : %u) : \tname = '%s' \tclass = '%s'", modProx->getModuleProxyId(), modProx->getForeignModuleId(), modProx->getModuleName().c_str(), modProx->getModuleClassName().c_str()); } } } return true; } }; bool IModuleManager::isInitialized() { return CModuleManager::isInitialized(); } IModuleManager &IModuleManager::getInstance() { return CModuleManager::getInstance(); } void IModuleManager::releaseInstance() { CModuleManager::releaseInstance(); } NLMISC_SAFE_SINGLETON_IMPL(CModuleManager); extern void forceGatewayLink(); extern void forceLocalGatewayLink(); extern void forceGatewayTransportLink(); extern void forceGatewayL5TransportLink(); void forceLink() { forceGatewayLink(); forceLocalGatewayLink(); forceGatewayTransportLink(); forceGatewayL5TransportLink(); } } // namespace NLNET