// 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 . //=================================================================== /* AI Managers -- individual -- what's my id what's my root name (what's my source file name) which service am i on do I need recompiling what's my load weighting -- container -- vector? get manager by name get manager by id queue of managers waiting to be allocated to servers - queue is processed on service update, dispatching new 'manager up's to services who are currently not in the process of loading *** if we add a concept of manager families then we can launch rafts of managers together this can make it easy to explicitly ballance loading *** even better would be to group by service and assume that all services have same power this way all can be launched and assigned as services come up. if the services are launched @ 1 per CPU or one per server then we can explictly overload, and automatically load ballance afterwards... */ #include "nel/misc/debug.h" #include "nel/misc/file.h" #include "nel/misc/config_file.h" #include "nel/misc/path.h" #include "nel/net/service.h" #include "game_share/xml.h" #include "ai_share/ai_share.h" #include "ai_manager.h" #include "ai_service.h" #include "ai_files.h" #include "aids_actions.h" using namespace NLMISC; using namespace NLNET; using namespace std; //=================================================================== //--------------------------------------------------- // INSTANTIATED CLASS: Public methods //------------- // a few read accessors (static properties) // the manager id (0..255) sint CAIManager::id() const { return int(this-_managers); } // the manager name .. ie the source file name minus extension const std::string &CAIManager::name() const { return _name; } // the CPU load rating of the manager for auto-load ballancing purposes uint CAIManager::weightCPU() const { return _weightCPU; } // the RAM load rating of the manager for auto-load ballancing purposes uint CAIManager::weightRAM() const { return _weightRAM; } //------------- // a few read accessors (state of the files on disk) // indicates whether newer source files than object files have been located bool CAIManager::needCompile() const { return _needCompile; } // indicate whether an object file has been located in the object directory bool CAIManager::objExists() const { return _objExists; } //------------- // a few read accessors (relating to assignment to & execution by an ai service) // has the manager been opened (it may still be waiting to be assigned) bool CAIManager::isOpen() const { return _isOpen; } // has the manager been assigned to a service bool CAIManager::isAssigned() const { return _isAssigned; } // is the manager up and running on the assigned service bool CAIManager::isUp() const { return _isUp; } // the id of the service to which the manager is assigned NLNET::TServiceId CAIManager::serviceId() const { return _service; } //------------- // a few basic actions (relating to disk files) const std::string &xmlDelimitedString(CxmlNode *xmlNode,const std::string &delimiter) { static const std::string emptyString; for (uint i=0;ichildCount();++i) { CxmlNode *child=xmlNode->child(i); if (child->type()==delimiter) if (child->childCount()==1) if (child->child(0)->type()=="") { return child->child(0)->txt(); } } return emptyString; } const std::string &getProp(CxmlNode *xmlNode,const std::string &propertyName) { static const std::string emptyString; for (uint i=0;ichildCount();++i) { CxmlNode *child=xmlNode->child(i); if (child->type()=="PROPERTY") { const std::string &name= xmlDelimitedString(child,std::string("NAME")); if (name==propertyName) return xmlDelimitedString(child,std::string("STRING")); } } return emptyString; } // compile the source files to generate new object files void CAIManager::compile() { // get the file names of input and output files std::string srcFile=CAIFiles::fullSrcFileName(id()); std::string objFile=CAIFiles::fullObjFileName(id()); // make sure this file isn't in the ignore list CConfigFile::CVar *varPtr; varPtr=IService::getInstance()->ConfigFile.getVarPtr(std::string("IgnorePrimitives")); if (varPtr==NULL) { nlwarning("Cannot compile file '%s' as IgnorePrimitives variable not found in .cfg file: Please add 'IgnorePrimitives={\"\"};' and try again",CAIFiles::fullSrcFileName(id()).c_str()); return; } for (uint i=0;isize();++i) if (CAIFiles::srcName(id())==CFile::getFilenameWithoutExtension(varPtr->asString(i))) { nlinfo("Skipping file in .cfg ignoreList: %s",CAIFiles::fullSrcFileName(id()).c_str()); return; } // compile the input file nlinfo("Compile %s => %s",srcFile.c_str(),objFile.c_str()); CAIDSActions::CurrentManager=id(); AI_SHARE::parsePrimFile(srcFile.c_str()); // make sure this file isn't in the ignore list (if the compiler found nothing interesting it will have been added) varPtr=IService::getInstance()->ConfigFile.getVarPtr(std::string("IgnorePrimitives")); for (uint i=0;isize();++i) if (CAIFiles::srcName(id())==CFile::getFilenameWithoutExtension(varPtr->asString(i))) { nlinfo("- Skipping file as it has just been added to .cfg ignoreList: %s",CAIFiles::fullSrcFileName(id()).c_str()); return; } // write the output file CAIFiles::writeObjFile(id()); // write the binary output file (for debugging only) NLMISC::COFile file; if (file.open(objFile+"_out")) { std::string s; MgrDfnRootNode.serialToString(s); file.serial(s); file.close(); } else nlwarning("CAIManager::compile(): Failed to open the output file: %s",(objFile+"_out").c_str()); } // delete the object files (but not the save files) void CAIManager::clean() { CAIFiles::clean(id()); } //------------- // a few basic actions (relating to assignment to & execution by an ai service) // open the manager on an unspecified service // (may be queued until a service is available) void CAIManager::open() { CAIService::openMgr(id()); } // assign manager to a specified service and begin execution void CAIManager::assign(NLNET::TServiceId serviceId) { // make sure that the manager isn't assigned to a service already if (isAssigned()) { nlwarning("Cannot assign manager %04d (%s) to service %d as already assigned to %d", id(), name().c_str(), serviceId.get(), _service.get()); return; } // flag me as assigned _isAssigned=true; _service=serviceId; // transfer control to the service's assignMgr() method CAIService *service=CAIService::getServiceById(serviceId); if (service!=NULL) service->assignMgr(id()); } // stop execution on the current service and assign to a new service void CAIManager::reassign(NLNET::TServiceId serviceId) { CAIService *service=CAIService::getServiceById(_service); if (service!=NULL) service->reassignMgr(id(),serviceId); } // stop execution of a manager void CAIManager::close() { // make sure that the manager isn't assigned to a service already if (!isAssigned()) { nlwarning("Cannot unassign manager %04d (%s) as it is already unassigned", id(), name().c_str()); return; } // if the service is running then transfer control to the service singleton's closeMgr() method if (isUp()) { CAIService::closeMgr(id()); return; } // flag me as unassigned _isAssigned=false; _isOpen=false; _service.set(0); } //------------- // a few basic actions (miscelaneous) // display information about the state of the manager void CAIManager::display() const { if (isAssigned()) nlinfo("AI Manager %04d: %s: %s ON SERVICE %d (%s)", id(), _name.c_str(), isUp()? "UP AND RUNNING": /* else */ "ASSIGNED TO BUT NOT YET UP", _service.get(), isOpen()? "auto-assigned": /* else */ "manualy assigned" ); else nlinfo("AI Manager %04d: %s: %s", id(), _name.c_str(), isOpen()? "OPEN - AWAITING ASSIGNMENT": !objExists()? "NOT OPEN - OBJECT FILE NOT FOUND": needCompile()? "NOT OPEN - OBJECT FILE OLDER THAN SOURCE": /* else */ "NOT OPEN - OBJECT FILE IS UP TO DATE" ); } //------------- // a few write accessors (miscelaneous) // set the name assigned to manager // if no name previously assigned then reset all manager properties // if a name already exists and does not match new name then do nohing and return false bool CAIManager::set(const std::string &name) { // if we already have a name associated with this slot then simply check that it matches the new name if (!_name.empty()) return (_name==name); _reset(); _name=name; return true; } // set the state of the needCompile flag void CAIManager::setNeedCompile(bool val) { _needCompile=val; } // set the state of the objFileExists flag void CAIManager::setObjFileExists(bool val) { _objExists=val; } // set the state of the isUp flag void CAIManager::setIsUp(bool val) { _isUp=val; } // set the state of the isOpen flag void CAIManager::setIsOpen(bool val) { _isOpen=val; } //--------------------------------------------------- // INSTANTIATED CLASS: Private methods // default constructor - may only be instantiated by the singleton CAIManager::CAIManager(): MgrDfnRootNode(std::string()) { // manager id - make sure that the managers are all in the one static array // note that id is calaulated from the array address and the addess of 'this' nlassert(uint(id())=maxManagers()) { nlwarning("CAIManager::getManagerById(id): id %d not in range 0..%d",id,maxManagers()-1); return NULL; } return &(_managers[id]); } // get a pointer to the manager with given index (0..numManagers-1) CAIManager *CAIManager::getManagerByIdx(uint idx) { uint count=0; for (uint i=0;i=numManagers (%d)",idx,count); return NULL; } // get the handle for the manager of given name and optionally create a new // handle if none found - return -1 if none found or no free slots int CAIManager::nameToId(std::string name, bool assignNewIfNotFound) { // see if the name is a numeric version of an id uint val=atoi(name.c_str()); if (!name.empty() && name.size()<=4 && ( (val>0 && val