khanat-opennel-code/code/ryzom/server/src/ai_data_service/ai_service.cpp

322 lines
8.8 KiB
C++

// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// 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 <http://www.gnu.org/licenses/>.
//===================================================================
/*
***
when a service goes down I must remember that the managers are no longer running
in the normal way I can try to bring them back up on the remaining services
***
when a service comes up and the rest of the services are over-loaded it would be
good to be able to load ballance (if loading is fast enough)
- tell new service to load static data and prepare to launch.
- wait for new service to say 'ready'
- tell old service to transfer to new service (ie: save to ram & transmit to new service)
- * old service must continue to forward packets to the new service to ensure smooth continuation
- old service tells me when he's down
- new service tells me when he's up
***
Need to deal with 'save's as well...
***
Need init() and release() in order to register message callbacks, etc?
*/
/*
//-------------------------------------------------------------------------
// Some global variables
uint32 GlobalServicesUp=0;
NLMISC_VARIABLE(uint32, GlobalServicesUp, "ServicesUp");
*/
#include "nel/misc/debug.h"
#include "ai_manager.h"
#include "ai_service.h"
#include "ai_files.h"
#include "messages.h"
//===================================================================
//---------------------------------------------------
// INSTANTIATED CLASS: Public methods
// a few read accessors
NLNET::TServiceId CAIService::id() const
{
// compute the index of this AIService class instance in the array of
// CAIService (this is stored in the _services array)
return NLNET::TServiceId(this-_services);
}
bool CAIService::isUp() const
{
return _isUp;
}
uint CAIService::powerCPU() const
{
return _powerCPU;
}
uint CAIService::powerRAM() const
{
return _powerRAM;
}
// assign a given manager to this service (ie open and run it)
void CAIService::assignMgr(sint mgrId)
{
// get a pointer to the manager in question
CAIManager *m=CAIManager::getManagerById(mgrId);
if (m==NULL)
return;
// if this method has not been called by the manager's own method then ping pong
if (!m->isAssigned())
{
m->assign(id());
return;
}
// if the manager is assigned to a different service then bomb
if (m->serviceId()!=id())
{
nlwarning("Cannot assign manager %04d (%s) to service %d as it is already assigned to service %d",
mgrId, m->name().c_str(), id().get(), m->serviceId().get());
return;
}
// if the service is up then send it a message to launch the manager
if (isUp())
{
// std::string dataBuf
// dataBuf.resize(binFileSize(id()));
// FILE *f=fopen(binFileName(id()),"rb");
// if (f==NULL)
// {
// nlinfo("Failed to load action list file: %s (try 'aiMake')",binFileName(id()));
// return false;
// }
// if (fread(dataBuf.buffer(),1,binFileSize(id()),f) != binFileSize(id()))
// {
// nlinfo("Read error in binary file: %s",binFileName(id()));
// return false;
// }
// fclose(f);
// CMsgAIUploadActions(id(),dataBuf).send();
// CMsgAIOpenMgrs(mgrId,m->name()).send(id());
m->setIsUp(true);
// return true;
}
// return false;
}
// unassign a manager currently running on this service (ie close it)
void CAIService::unassignMgr(sint mgrId)
{
// if the manager isn't assigned to this service then bomb
if (CAIManager::getManagerById(mgrId)->serviceId()!=id())
{
nlwarning("Cannot stop manager %04d (%s) on service %d as it is assigned to service %d",
mgrId, CAIManager::getManagerById(mgrId)->name().c_str(), id().get(), CAIManager::getManagerById(mgrId)->serviceId().get());
return;
}
// transfer control to the singleton to finish the work
closeMgr(mgrId);
}
// reassign a manager currently assigned to this service to another service
void CAIService::reassignMgr(sint mgrId, NLNET::TServiceId serviceId)
{
// get a pointerto the service
CAIService *s=getServiceById(serviceId);
if (s==NULL)
return;
// this is a very simple implementation of control transfer... should be revised later
unassignMgr(mgrId);
s->assignMgr(mgrId);
}
//---------------------------------------------------
// INSTANTIATED CLASS: Private methods
CAIService::CAIService()
{
// if the following assert fails it's because a CAIService object has
// been instantiated outside of the singleton's array
nlassert(id().get() < maxServices());
}
//===================================================================
// *** END OF THE INSTANTIATED CLASS *** START OF THE SINGLETON ***
//===================================================================
//---------------------------------------------------
// SINGLETON: Data
class CAIService CAIService::_services[RYAI_AI_SERVICE_MAX_SERVICES];
//---------------------------------------------------
// SINGLETON: Public methods
// get the number of allocated managers
uint CAIService::numServices()
{
uint count=0;
for (uint i=0;i<maxServices();i++)
if (_services[i].isUp())
count++;
return count;
}
// get a pointer to the manager with given handle (0..maxManagers-1)
CAIService *CAIService::getServiceById(NLNET::TServiceId id)
{
if (id.get() >= maxServices())
{
nlwarning("CAIService::getServiceById(id): id %d not in range 0..%d",id.get(),maxServices()-1);
return NULL;
}
return &(_services[NLNET::TServiceId8(id).get()]);
}
// get a pointer to the manager with given index (0..numManagers-1)
CAIService *CAIService::getServiceByIdx(uint idx)
{
uint count=0;
for (uint i=0;i<maxServices();i++)
if (_services[i].isUp())
{
if (idx==count)
return &(_services[i]);
count++;
}
nlwarning("CAIService::getServiceByIdx(idx): idx (%d)>=maxServices (%d)",idx,count);
return NULL;
}
// select a service to assign the manager to and assign it
// if no services are available then the manager is queued until one
// becomes available
// managers opened via this interface are queued back up and re-launched
// if their service goes down
void CAIService::openMgr(sint mgrId)
{
// get a pointer to the manager in question
CAIManager *m=CAIManager::getManagerById(mgrId);
if (m==NULL)
return;
m->setIsOpen(true);
}
// close a manager (on whichever service its running)
void CAIService::closeMgr(sint mgrId)
{
// get a pointer to the manager in question
CAIManager *m=CAIManager::getManagerById(mgrId);
if (m==NULL)
return;
// if the manager is flagged as up and running on a service then shut it down
if (m->isUp())
{
CAIService *s=getServiceById(m->serviceId());
if (s!=NULL && s->isUp())
{
// send a message to the service to stop the manager
CMsgAICloseMgrs(mgrId).send(m->serviceId());
}
// clear the manager's isUp() flag
m->setIsUp(false);
}
// if the manager is assigned to a service then transfer control to the manager's close()
if (m->isAssigned())
m->close();
}
// update routine called by service_main update every net loop
void CAIService::update()
{
#define IN_TRANSIT_PACKET_LIMIT 1
for (uint i=0;i<maxServices();i++)
{
CAIService &s=_services[i];
if (s.isUp())
{
// see whether we have enough spare capacity to start sending data about a new manager
// if so look through the managers for any that are awaiting assignment
if (s._dataSentCount+s._dataToSend.size()-s._dataAckCount<IN_TRANSIT_PACKET_LIMIT)
{
// look for the heaviest manager that doesn't blow my quotas
uint best=~0u;
uint bestScore=0;
for (uint j=0;j<CAIManager::maxManagers();j++)
{
CAIManager *m=CAIManager::getManagerById(j);
if (m->isOpen() && !m->isAssigned())
if (m->weightRAM()<=s._unusedPowerRAM && m->weightCPU()<=s._unusedPowerCPU)
if (m->weightRAM()+m->weightCPU()/2>=bestScore)
{
best=j;
bestScore=m->weightRAM()+m->weightCPU()/2;
}
}
// if we found a manager then go ahead and assign it
if (best!=~0u)
s.assignMgr(best);
}
// if we have data waiting to be sent then send it
while (!s._dataToSend.empty() && s._dataSentCount-s._dataAckCount<IN_TRANSIT_PACKET_LIMIT)
{
NLNET::CUnifiedNetwork::getInstance()->send( s.id(), *(s._dataToSend.begin()) );
s._dataToSend.pop_front();
s._dataSentCount++;
}
}
}
#undef IN_TRANSIT_PACKET_LIMIT
}
//===================================================================