khanat-opennel-code/code/ryzom/server/src/tick_service/tick_service.cpp

1262 lines
31 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/>.
#include "nel/misc/command.h"
#include "nel/misc/variable.h"
#include "nel/misc/common.h"
#include "nel/misc/file.h"
#include "game_share/ryzom_version.h"
#include "game_share/tick_event_handler.h"
#include "game_share/backup_service_interface.h"
#include "game_share/singleton_registry.h"
#include <time.h>
#include "tick_service.h"
#include <string>
#ifdef NL_OS_WINDOWS
# ifndef NL_COMP_MINGW
# define NOMINMAX
# endif
# include <windows.h>
#endif // NL_OS_WINDOWS
using namespace std;
using namespace NLMISC;
using namespace NLNET;
// force admin module to link in
extern void admin_modules_forceLink();
void foo()
{
admin_modules_forceLink();
}
CTickService * TS = NULL;
NLNET::TServiceId SlowestService(0);
NLNET::TServiceId SlowestCandidate(0);
double SlowestTock = 0.0;
string WaitingForServices;
const string GAME_CYCLE_FILE = "game_cycle.ticks";
const NLMISC::TGameCycle INTERVAL_FOR_SAVING_GAME_CYCLE = 300;
// when true the tick service stops broadcasting ticks
bool Pause = false;
// In the Tick Service, set TotalSpeedLoop to the duration of a whole tick cycle
// (between send tick and the last tock received)
extern CVariable<sint32> TotalSpeedLoop;
extern CVariable<sint32> TickSpeedLoop;
static NLMISC::TTime BeginOfTickTime = 0;
CVariable<uint> TickSendingMode( "tick", "TickSendingMode", "0=Continuous 1=StepByStep 2=Fastest", 0,
0, true );
/*CVariable<float> WaitForBSThreshold( "tick", "WaitForBSThreshold", "Threshold for BSAckDelay beyond which tick starts to slow down to wait for BS (expressed as a factor of _TickTimeStep)", 1.0f,
0, true );
*/
//-----------------------------------------------
// cbRegister
//
//-----------------------------------------------
static void cbRegister(CMessage& msgin, const string &serviceName, NLNET::TServiceId serviceId)
{
if( serviceId.get() > 255 )
{
nlwarning("<cbRegister> Service %s id is too higher than 255 : %d, this service will be not registered",serviceName.c_str(),serviceId.get());
return;
}
bool tocking = true;
uint16 threshold = 0;
msgin.serial( tocking );
msgin.serial( threshold );
TS->registerClient( serviceId, tocking, threshold );
CMessage msgout( "REGISTERED" );
TGameTime gameTime = TS->getGameTime();
TGameTime gameTimeStep = TS->getGameTimeStep();
TGameCycle gameCycle = TS->getGameCycle();
msgout.serial( gameTime );
msgout.serial( gameTimeStep );
msgout.serial( gameCycle );
CUnifiedNetwork::getInstance()->send(serviceId,msgout);
if( TS->getClientCount() == 1 )
{
TS->FirstTime = true;
BeginOfTickTime = CTime::getLocalTime();
TS->checkTockReceived();
}
} // cbRegister //
//-----------------------------------------------
// cbTock
//
//-----------------------------------------------
static void cbTock(CMessage& msgin, const string &serviceName, NLNET::TServiceId serviceId)
{
// Receive measures of client service
TS->MainTimeMeasures.CurrentMirrorMeasures.push_back( CMirrorGameCycleTimeMeasureMS() );
CMirrorGameCycleTimeMeasureMS& mm = TS->MainTimeMeasures.CurrentMirrorMeasures.back();
msgin.serial( mm );
mm.MSId = serviceId;
// Process Tock
//nlinfo( "TOCK rcvd at %.6f", CTime::ticksToSecond( CTime::getPerformanceTime() ) );
SlowestCandidate=serviceId;
TS->addTock( serviceId );
} // cbTock //
//-----------------------------------------------
// cbHaltTick
//
//-----------------------------------------------
static void cbHaltTick(CMessage& msgin, const string &serviceName, NLNET::TServiceId serviceId)
{
std::string reason = "<UNKNOWN>";
try
{
msgin.serial(reason);
}
catch(const Exception&)
{
}
reason = toString("service %s:%d halted: %s", serviceName.c_str(), serviceId.get(), reason.c_str());
TS->haltTick(reason);
} // cbHaltTick //
//-----------------------------------------------
// cbResumeTick
//
//-----------------------------------------------
static void cbResumeTick(CMessage& msgin, const string &serviceName, NLNET::TServiceId serviceId)
{
TS->resumeTick();
} // cbHaltTick //
//-----------------------------------------------
// callback table for input message
//
//-----------------------------------------------
TUnifiedCallbackItem CbArray[] =
{
{ "REGISTER", cbRegister },
{ "TOCK", cbTock },
{ "HALT_TICK", cbHaltTick },
{ "RESUME_TICK", cbResumeTick },
};
/*
* halt ticking
*/
void CTickService::haltTick(const std::string& reason)
{
if (CurrentMode == TickHalted)
return;
CurrentMode = TickHalted;
nlwarning("***********************************************************");
nlwarning("WARNING:");
nlwarning("TICK SERVICE IS ENTERING HALTED MODE");
nlwarning("HALT REQUEST IS '%s'", reason.c_str());
nlwarning("***********************************************************");
HaltedReason = reason;
IService::setCurrentStatus("HALTED");
}
/*
* resume ticking
*/
void CTickService::resumeTick()
{
if (CurrentMode == TickRunning)
return;
// restart ticking
CurrentMode = TickRunning;
nlwarning("***********************************************************");
nlwarning("WARNING:");
nlwarning("TICK SERVICE IS RESUMED");
nlwarning("HALT REQUEST WAS '%s'", HaltedReason.c_str());
nlwarning("***********************************************************");
// check in case tocks were received during halt
checkTockReceived();
IService::clearCurrentStatus("HALTED");
}
//-----------------------------------------------
// registerClient
//
//-----------------------------------------------
void CTickService::registerClient( NLNET::TServiceId serviceId, bool tocking, uint16 threshold )
{
if( serviceId.get() < _ClientInfos.size() )
{
if( _ClientInfos[serviceId.get()].Registered == true )
{
nlerror("<CTickService::registerClient> The service %d is alredy registered",serviceId.get());
}
_ClientInfos[serviceId.get()].Registered = true;
_ClientInfos[serviceId.get()].Tocking = tocking;
_ClientInfos[serviceId.get()].Threshold = threshold;
_QuickLog.displayNL( "%u: +%hu", getGameCycle(), serviceId.get() );
}
else
{
nlwarning("<CTickService::registerClient> Service id too high : %d (max is %d)",serviceId.get(),_ClientInfos.size()-1);
}
} // registerClient //
//-----------------------------------------------
// addTock
//
//-----------------------------------------------
void CTickService::addTock( NLNET::TServiceId serviceId )
{
time_t t; time( &t );
_QuickLog.displayNL( "%s: %u: [<-%hu] TOCK-r", IDisplayer::dateToHumanString(t), getGameCycle(), serviceId.get() );
if( serviceId.get() < _ClientInfos.size() )
{
if( _ClientInfos[serviceId.get()].Registered )
{
if( _ClientInfos[serviceId.get()].TockReceived )
{
if( _ClientInfos[serviceId.get()].Threshold == 0 )
{
nlwarning("<CTickService::addTock> Tock received more than once despite a threshold null");
}
}
else
{
_ClientInfos[serviceId.get()].TockReceived = true;
_QuickLog.displayNL( "%u: [<-%hu] TOCK-p", getGameCycle(), serviceId.get() );
}
if( _ClientInfos[serviceId.get()].TockMissingCount > 0 )
{
_ClientInfos[serviceId.get()].TockMissingCount--;
}
else
{
nlwarning("<CTickService::addTock> Receiving a tock, but there was no tock missing !");
}
}
else
{
nlwarning("(TICKS)<CTickService::addTock> Tock received from service %d which is not registered",serviceId.get());
}
checkTockReceived();
}
else
{
nlwarning("<CTickService::addTock> Service id too high : %d (max is %d)", serviceId.get(), _ClientInfos.size()-1);
}
} // addTock //
//-----------------------------------------------
// checkTockReceived
//
//-----------------------------------------------
void CTickService::checkTockReceived()
{
// Do not tick if ticking is halted
if (CurrentMode == TickHalted)
return;
// check if all the tocks have been received (or if missing is less than threshold )
uint i;
for( i=0; i<_ClientInfos.size(); i++ )
{
// if this client is supposed to send a tock
if( _ClientInfos[i].Registered )
{
// if tock was not received yet
if( _ClientInfos[i].TockReceived == false )
{
// if we have to wait for this client
if( _ClientInfos[i].Tocking == true )
{
// we check threshold
if( _ClientInfos[i].TockMissingCount > _ClientInfos[i].Threshold )
{
return;
}
}
}
}
}
TotalSpeedLoop = (sint32)(CTime::getLocalTime() - BeginOfTickTime);
// set the TickSpeedLoop var to the same value
TickSpeedLoop = TotalSpeedLoop;
MainTimeMeasures.CurrentTickServiceMeasure[PrevTotalTickDuration] = (uint16)TotalSpeedLoop.get();
//nlinfo( "End of tick at %.6f", CTime::ticksToSecond( CTime::getPerformanceTime() ) );
// Step by step mode
if( TickSendingMode.get() == StepByStep )
{
if( _StepCount > 0 )
{
// broadcast the tick
broadcastTick();
_StepCount--;
}
}
// Continuous or fastest mode
else
{
TLocalTime currentTime = ((double)CTime::getLocalTime())/1000.0;
SlowestService=SlowestCandidate;
SlowestTock = currentTime - _TickSendTime;
// if it's the first time we send a tick, we init the tick send time
if( FirstTime )
{
_TickSendTime = currentTime - _TickTimeStep;
_LastTickTimeStep = _TickTimeStep;
FirstTime = false;
}
// setup the default value for the time step to use
NLMISC::TLocalTime effectiveTimeStep= _TickTimeStep;
// if the backup service is taking longer than _TickTimeStep to respond, then we need to slow down to a little slower than its pace
/* NLMISC::TLocalTime lastBSAckDelay= ((NLMISC::TLocalTime)Bsi.getLastAckDelay())/1000.0;
if ((WaitForBSThreshold>=1.0f) && (float)lastBSAckDelay>(float)_TickTimeStep*WaitForBSThreshold)
{
effectiveTimeStep= lastBSAckDelay+ (lastBSAckDelay-_TickTimeStep)/2;
}
*/
// smooth out BS update rate fluctuations with a simple filter and store away the time step value for next time round
effectiveTimeStep= (effectiveTimeStep+3*_LastTickTimeStep)/4;
_LastTickTimeStep= effectiveTimeStep;
// if we have to wait before sending tick
while ( ( currentTime < (_TickSendTime + effectiveTimeStep) ) && ( TickSendingMode.get() != Fastest) )
{
if ( currentTime < _TickSendTime )
{
nlinfo( "Backward time sync detected (about %.1f s)", _TickSendTime - currentTime );
_TickSendTime = currentTime;
}
else
{
nlSleep( (uint32)((_TickSendTime + effectiveTimeStep - currentTime)*1000.0) );
}
currentTime = ((double)CTime::getLocalTime())/1000.0;
}
// increment the send time
TLocalTime oldTime = _TickSendTime;
_TickSendTime = ((double)CTime::getLocalTime())/1000.0;
double dt = _TickSendTime - oldTime;
// round off the send time to iron out high frequency effects
if (_TickSendTime<oldTime+effectiveTimeStep)
_TickSendTime=oldTime+effectiveTimeStep;
if (effectiveTimeStep*1000.0>1.0)
{
dt = double(uint32(dt*1000.0)/ uint32(effectiveTimeStep*1000.0) * uint32(effectiveTimeStep*1000.0))/1000.0;
_TickSendTime = oldTime + dt;
}
//nlinfo( " %"NL_I64"u %"NL_I64"u", (TTime)((_TickSendTime-oldTime)*1000), (TTime)((d2-oldTime)*1000) );
// broadcast the tick
broadcastTick();
// update the tick time step
//updateTickTimeStep
}
} // checkTockReceived //
//-----------------------------------------------
// broadcastTick
//
//-----------------------------------------------
void CTickService::broadcastTick()
{
if ( Pause == true ) return;
MainTimeMeasures.beginNewCycle();
BeginOfTickTime = CTime::getLocalTime();
// increment the game time and cycle
_GameTime += _GameTimeStep;
_GameCycle++;
CTickEventHandler::setGameCycle( _GameCycle ); // to display the good value in the variable
// we send a tick to every registered client
uint i;
for( i=0; i<_ClientInfos.size(); i++ )
{
if( _ClientInfos[i].Registered )
{
CMessage msgout;
if( _GameTimeStepHasChanged )
{
msgout.setType( "STEP_TICK" );
msgout.serial( _GameTimeStep );
_QuickLog.displayNL( "%u: [->%u] STEP_TICK", getGameCycle(), i );
}
else
{
msgout.setType( "TICK" );
time_t t; time( &t );
_QuickLog.displayNL( "%s: %u: [->%u] TICK", IDisplayer::dateToHumanString(t), getGameCycle(), i );
}
CUnifiedNetwork::getInstance()->send(NLNET::TServiceId(i),msgout);
TSockId host;
CCallbackNetBase *cnb;
cnb = CUnifiedNetwork::getInstance()->getNetBase(NLNET::TServiceId(i),host);
if( cnb )
{
cnb->flush( host );
}
// we are now waiting for a tick from this client
_ClientInfos[i].TockReceived = false;
_ClientInfos[i].TockMissingCount++;
}
}
_GameTimeStepHasChanged = false;
CSingletonRegistry::getInstance()->tickUpdate();
} // broadcastTick //
//-----------------------------------------------
// cbClientDisconnection
//
//-----------------------------------------------
void cbClientDisconnection( const std::string &serviceName, NLNET::TServiceId serviceId, void *arg)
{
TS->unregisterClient( serviceId );
TS->checkTockReceived();
} // cbClientDisconnection //
//-----------------------------------------------
// unregisterClient
//
//-----------------------------------------------
void CTickService::unregisterClient( NLNET::TServiceId serviceId )
{
if( serviceId.get() < _ClientInfos.size() )
{
if ( _ClientInfos[serviceId.get()].Registered )
{
_ClientInfos[serviceId.get()].Registered = false;
_ClientInfos[serviceId.get()].TockReceived = true;
_ClientInfos[serviceId.get()].Tocking = true;
_ClientInfos[serviceId.get()].Threshold = 0;
_ClientInfos[serviceId.get()].TockMissingCount = 0;
_QuickLog.displayNL( "%u: -%hu", getGameCycle(), serviceId.get() );
}
else
{
nlinfo("<CTickService::unregisterClient> Service %d disconnection detected but this service is not registered : nothing to do",serviceId.get());
}
}
else
{
nlwarning("<CTickService::unregisterClient> Service %d : service id too big ! (max is %d)",serviceId.get(),_ClientInfos.size()-1);
}
} // unregisterClient //
//-----------------------------------------------
// getClientCount
//
//-----------------------------------------------
uint16 CTickService::getClientCount()
{
uint16 clientCount = 0;
uint i;
for( i=0; i<_ClientInfos.size(); i++ )
{
if( _ClientInfos[i].Registered )
{
clientCount++;
}
}
return clientCount;
} // getClientCount //
//-----------------------------------------------
// displayGameTime()
//
//-----------------------------------------------
void CTickService::displayGameTime() const
{
CMessage msgout( "DISPLAY_TIME" );
uint i;
for( i=0; i<_ClientInfos.size(); i++ )
{
if( _ClientInfos[i].Registered )
{
CUnifiedNetwork::getInstance()->send(NLNET::TServiceId(i),msgout);
}
}
nlinfo("(TICKS)<CTickService::displayTime> : Game time in the Tick Service : %f sec (local: %f ms)", (double)_GameTime,(double)CTime::getLocalTime());
} // displayGameTime //
/*
* Initialise the service
*/
void CTickService::init()
{
setVersion (RYZOM_VERSION);
CurrentMode = TickRunning;
CSingletonRegistry::getInstance()->init();
// keep pointer on class
TS = this;
_StepCount = 1;
// maximum 256 clients can be connected
_ClientInfos.resize(256);
try
{
_GameTime = ConfigFile.getVar("GameTime").asFloat();
}
catch(const Exception &)
{
// init the game time
_GameTime = 0.0f;
}
try
{
_GameCycle = ConfigFile.getVar("GameCycle").asInt();
_SavedGameCycle = _GameCycle;
}
catch(const Exception &)
{
// init the game cycle from file
loadGameCycle();
}
CTickEventHandler::setGameCycle( _GameCycle ); // to display the good value in the variable
try
{
_TickTimeStep = ConfigFile.getVar("TickTimeStep").asFloat();
}
catch(const Exception &)
{
// tick service time step between two ticks
_TickTimeStep = 0.1f;
}
try
{
_GameTimeStep = ConfigFile.getVar("GameTimeStep").asFloat();
}
catch(const Exception &)
{
// game time between two ticks
_GameTimeStep = 0.1f;
}
// thus the time step will be sent along with the first tick
_GameTimeStepHasChanged = true;
// local time when last tick was sent
_TickSendTime = 0;
FirstTime = true;
CUnifiedNetwork::getInstance()->setServiceDownCallback("*", cbClientDisconnection, NULL);
RecentHistory.setParam( 20 );
_QuickLog.addDisplayer( &RecentHistory, false );
_RangeMirrorManager.init();
} // init //
/*
* Release
*/
void CTickService::release()
{
if ( saveGameCycle() )
{
// nlinfo( "Game cycle %u saved to %s", _GameCycle, GAME_CYCLE_FILE.c_str() );
}
CSingletonRegistry::getInstance()->release();
}
/*
* Update
*/
bool CTickService::update()
{
CSingletonRegistry::getInstance()->serviceUpdate();
if (CurrentMode == TickHalted)
{
WaitingForServices = "TICK HALTED: "+HaltedReason;
}
else
{
WaitingForServices = "";
// check if all the tocks have been received (or if missing is less than threshold )
uint i;
for( i=0; i<_ClientInfos.size(); i++ )
{
// if this client is supposed to send a tock
if( _ClientInfos[i].Registered )
{
// if tock was not received yet
if( _ClientInfos[i].TockReceived == false )
{
// if we have to wait for this client
if( _ClientInfos[i].Tocking == true )
{
// we check threshold
if( _ClientInfos[i].TockMissingCount > _ClientInfos[i].Threshold )
{
WaitingForServices += toString(i) + " ";
}
}
}
}
}
// don't saveGameCycle if tick halted
// because BS probably sent an error report, and it would be hazardous
// to send BS new commands!!
if ( _GameCycle > _SavedGameCycle + INTERVAL_FOR_SAVING_GAME_CYCLE )
{
saveGameCycle();
}
}
return true;
}
/*
* Save to file
*/
bool CTickService::saveGameCycle()
{
_SavedGameCycle = _GameCycle; // do it anyway
try
{
CBackupMsgSaveFile msg( GAME_CYCLE_FILE, CBackupMsgSaveFile::SaveFile, Bsi );
msg.DataMsg.serialVersion( 0 );
msg.DataMsg.serial( _GameCycle );
Bsi.sendFile(msg);
/*
COFile file;
if ( file.open( SaveShardRoot.get() + IService::getInstance()->SaveFilesDirectory.toString() + GAME_CYCLE_FILE ) )
{
file.serialVersion( 0 );
file.serial( _GameCycle );
file.close();
}
else
throw EFileNotOpened( SaveShardRoot.get() + IService::getInstance()->SaveFilesDirectory.toString() + GAME_CYCLE_FILE );
return true;
*/
return true;
}
catch (const Exception &e)
{
nlwarning( "Can't save game cycle: %s", e.what() );
return false;
}
}
struct TTickFileCallback : public IBackupFileReceiveCallback
{
CTickService *TickService;
TTickFileCallback(CTickService *tickService)
: TickService(tickService)
{
}
virtual void callback(const CFileDescription& fileDescription, NLMISC::IStream& dataStream)
{
TickService->tickFileCallback(fileDescription, dataStream);
}
};
void CTickService::tickFileCallback(const CFileDescription& fileDescription, NLMISC::IStream& dataStream)
{
if (!fileDescription.FileName.empty())
{
dataStream.serialVersion( 0 );
dataStream.serial( _GameCycle );
_SavedGameCycle = _GameCycle;
}
else
{
_GameCycle = 0;
_SavedGameCycle = 0;
nlwarning("No tick file found, starting with fresh tick");
}
}
/*
* Load from file
*/
bool CTickService::loadGameCycle()
{
TTickFileCallback *cb = new TTickFileCallback(this);
Bsi.syncLoadFile(GAME_CYCLE_FILE, cb);
return true;
// try
// {
// CIFile file;
// if ( file.open( Bsi.getLocalPath() + GAME_CYCLE_FILE ) )
// {
// file.serialVersion( 0 );
// file.serial( _GameCycle );
// file.close();
// _SavedGameCycle = _GameCycle;
// }
// else
// throw EFileNotOpened( Bsi.getLocalPath() + GAME_CYCLE_FILE );
// nlinfo( "Loaded game cycle %u from %s, will be saved every %u game cycles", _GameCycle, (CPath::standardizePath(IService::getInstance()->SaveFilesDirectory.toString()) + GAME_CYCLE_FILE).c_str(), INTERVAL_FOR_SAVING_GAME_CYCLE );
// return true;
// }
// catch (const Exception &e)
// {
// nlwarning( "Can't load game cycle: %s", e.what() );
// _GameCycle = 0;
// _SavedGameCycle = _GameCycle;
// return false;
// }
}
/*
*
*/
CTickServiceGameCycleTimeMeasure::CTickServiceGameCycleTimeMeasure() : HistoryMain( CTickService::getInstance()->getServiceId(), NLNET::TServiceId(std::numeric_limits<uint16>::max()), false ) {}
/*
*
*/
void CTickServiceGameCycleTimeMeasure::beginNewCycle()
{
if ( ! CurrentMirrorMeasures.empty() )
{
// Update stats
for ( TMirrorMeasures::const_iterator imm=CurrentMirrorMeasures.begin(); imm!=CurrentMirrorMeasures.end(); ++imm )
{
storeMeasureToHistory( HistoryByMirror, imm->MirrorMeasure, imm->MSId, TServiceId(0) );
for ( std::vector<CServiceGameCycleTimeMeasure>::const_iterator ism=(*imm).ServiceMeasures.begin(); ism!=(*imm).ServiceMeasures.end(); ++ism )
{
storeMeasureToHistory( HistoryByService, (*ism).ServiceMeasure, (*ism).ClientServiceId, (*imm).MSId );
}
}
//nlinfo( "AV: C=%hu H=%hu", CurrentTickServiceMeasure[0], HistoryMain.Stats[MHTMax][0] );
HistoryMain.updateStats( CurrentTickServiceMeasure );
//nlinfo( "AP: C=%hu H=%hu", CurrentTickServiceMeasure[0], HistoryMain.Stats[MHTMax][0] );
// Clear current
CurrentMirrorMeasures.clear();
}
}
/*
*
*/
void CTickServiceGameCycleTimeMeasure::resetMeasures()
{
HistoryByMirror.clear();
HistoryByService.clear();
HistoryMain.reset( false );
}
/*
*
*/
void CTickServiceGameCycleTimeMeasure::displayStats( NLMISC::CLog *log )
{
log->displayNL( "Shard timings at GC %u:", CTickEventHandler::getGameCycle() );
log->displayRawNL( "___AVG__" );
displayStat( log, MHTSum );
log->displayRawNL( "___MAX___" );
displayStat( log, MHTMax );
log->displayRawNL( "___MIN___" );
displayStat( log, MHTMin );
}
const char * ServiceTimeMeasureTypeToCString [NbServiceTimeMeasureTypes] =
{ "TickTockInterval", "TickUpdateDuration", "PrevProcessMirrorUpdateDuration", "PrevReceiveMsgsViaMirrorDuration", "PrevTotalGameCycleDuration" };
const char * MirrorTimeMeasureTypeToCString [NbMirrorTimeMeasureTypes] =
{ "WaitAllReceivedDeltaDuration", "BuildAndSendDeltaDuration", "PrevApplyReceivedDeltaDuration", "PrevSendUMMDuration" };
const char * TickServiceTimeMeasureTypeToCString [NbTickServiceTimeMeasureTypes] =
{ "PrevTotalTickDuration" };
/*
*
*/
void CTickServiceGameCycleTimeMeasure::displayStat( NLMISC::CLog *log, TTimeMeasureHistoryStat stat )
{
log->displayRawNL( "\tTICKS, %u measures:", HistoryMain.NbMeasures );
uint divideBy = (HistoryMain.NbMeasures==0) ? 0 : ((stat==MHTSum) ? HistoryMain.NbMeasures : 1);
HistoryMain.Stats[stat].displayStat( log, TickServiceTimeMeasureTypeToCString, divideBy );
{
CMirrorTimeMeasure gatheredStats [NbTimeMeasureHistoryStats] = { 0, std::numeric_limits<uint16>::max(), 0 };
for ( std::vector<CMirrorTimeMeasureHistory>::const_iterator ihm=HistoryByMirror.begin(); ihm!=HistoryByMirror.end(); ++ihm )
{
log->displayRawNL( "\tMS-%hu, %u measures:", (*ihm).ServiceId.get(), (*ihm).NbMeasures );
divideBy = (stat==MHTSum) ? (*ihm).NbMeasures : 1;
(*ihm).Stats[stat].displayStat( log, MirrorTimeMeasureTypeToCString, divideBy );
gatheredStats[MHTSum] += (*ihm).Stats[stat] / (*ihm).NbMeasures;
gatheredStats[MHTMin].copyLowerValues( (*ihm).Stats[stat] );
gatheredStats[MHTMax].copyHigherValues( (*ihm).Stats[stat] );
}
if ( HistoryByMirror.size() > 1 )
{
log->displayRawNL( "\tAll mirror services:" );
gatheredStats[stat].displayStat( log, MirrorTimeMeasureTypeToCString, (stat==MHTSum) ? (uint)HistoryByMirror.size() : 1 ); // not displaying the 3 stats
}
}
{
CServiceTimeMeasure gatheredStats [NbTimeMeasureHistoryStats] = { 0, std::numeric_limits<uint16>::max(), 0 };
for ( std::vector<CServiceTimeMeasureHistory>::const_iterator ihs=HistoryByService.begin(); ihs!=HistoryByService.end(); ++ihs )
{
log->displayRawNL( "\t%s (on MS-%hu), %u measures:", CUnifiedNetwork::getInstance()->getServiceUnifiedName( (*ihs).ServiceId ).c_str(), (*ihs).ParentServiceId.get(), (*ihs).NbMeasures );
divideBy = (stat==MHTSum) ? (*ihs).NbMeasures : 1;
(*ihs).Stats[stat].displayStat( log, ServiceTimeMeasureTypeToCString, divideBy );
gatheredStats[MHTSum] += (*ihs).Stats[stat] / (*ihs).NbMeasures;
gatheredStats[MHTMin].copyLowerValues( (*ihs).Stats[stat] );
gatheredStats[MHTMax].copyHigherValues( (*ihs).Stats[stat] );
}
if ( HistoryByService.size() > 1 )
{
log->displayRawNL( "\tAll client services:" );
gatheredStats[stat].displayStat( log, ServiceTimeMeasureTypeToCString, (stat==MHTSum) ? (uint)HistoryByService.size() : 1 ); // not displaying the 3 stats
}
}
}
//-----------------------------------------------
// NLNET_SERVICE_MAIN
//-----------------------------------------------
NLNET_SERVICE_MAIN( CTickService, "TICKS", "tick_service", 0, CbArray, "", "" );
//
// Variables
//
NLMISC_VARIABLE(NLNET::TServiceId, SlowestService, "SId of the slowest service");
NLMISC_VARIABLE(double, SlowestTock, "Time in millisecond to receive the slowest tock");
NLMISC_VARIABLE(string, WaitingForServices, "Services that haven't tocked yet");
NLMISC_DYNVARIABLE(TGameTime, GameTime, "Current game time in second")
{
if (get) *pointer = TS->getGameTime();
}
NLMISC_DYNVARIABLE(TLocalTime, TickTimeStep, "Current tick time step in second")
{
if (get) *pointer = TS->getTickTimeStep();
else TS->setTickTimeStep(*pointer);
}
NLMISC_DYNVARIABLE(string, TickMode, "Current ticking mode")
{
if (get)
{
if (TS->CurrentMode == CTickService::TickRunning)
*pointer = "Normal";
else
*pointer = "HALTED: "+TS->HaltedReason;
}
}
//
// Commands
//
NLMISC_COMMAND(haltTick, "Halt tick server", "[reason]" )
{
if (args.size() > 1)
return false;
log.displayNL("TICK SERVICE HALTED MANUALLY.");
log.displayNL("PLEASE RESTART TICK SERVICE USING resumeTick COMMAND.");
std::string reason = "MANUAL HALT";
if (args.size() == 1)
reason += ": "+args[0];
TS->haltTick(reason);
return true;
}
NLMISC_COMMAND(resumeTick, "Resume tick server", "" )
{
log.displayNL("TICK SERVICE RESUMED MANUALLY.");
TS->resumeTick();
return true;
}
NLMISC_COMMAND(displayTimingsOfShard, "Display all timings", "" )
{
TS->MainTimeMeasures.displayStats( &log );
return true;
}
NLMISC_COMMAND(resetTimingsOfShard, "Reset all timings", "" )
{
TS->MainTimeMeasures.resetMeasures();
return true;
}
NLMISC_COMMAND(displayGameTime, "The tick service and all connected services display its current game time", "")
{
TS->displayGameTime();
return true;
}
NLMISC_COMMAND(send,"enable send (in the step by step mode)"," ")
{
TS->enableSend();
return true;
}
NLMISC_COMMAND(clients,"Give the number of clients"," ")
{
log.displayNL("Number of connected client : %d", TS->getClientCount());
return true;
}
/*
NLMISC_COMMAND(getServicesState,"Give the current state of all services"," ")
{
// todo end the code
uint i;
for( i=0; i<_ClientInfos.size(); i++ )
{
// if this client is supposed to send a tock
if( _ClientInfos[i].Registered )
{
// if tock was not received yet
if( _ClientInfos[i].TockReceived == false )
{
// if we have to wait for this client
if( _ClientInfos[i].Tocking == true )
{
// we check threshold
if( _ClientInfos[i].TockMissingCount > _ClientInfos[i].Threshold )
{
return;
}
}
}
}
}
return true;
}
*/
NLMISC_COMMAND(displayRecentHistory,"Display the history of tick events","")
{
TS->RecentHistory.write( &log );
return true;
}
NLMISC_COMMAND( pause, "Pause the tick service", "" )
{
Pause = !Pause;
if( Pause )
nlinfo("Pause On");
else
{
nlinfo("Pause Off");
TS->checkTockReceived();
}
return true;
}
/*
*/
/*
// ADDED by ben: BS test purpose, you can remove this
NLMISC_COMMAND( sendFileBS, "send file to BS", "<file to send> <file name to be written>")
{
if (args.size() != 2)
return false;
CIFile fi;
if (!fi.open(args[0]))
return false;
std::vector<uint8> buffer(fi.getFileSize());
fi.serialBuffer(&(buffer[0]), buffer.size());
CBackupMsgSaveFile msgSave;
msgSave.Data.serialBuffer(&(buffer[0]), buffer.size());
msgSave.FileName = args[1];
Bsi.sendFile(msgSave);
return true;
}
NLMISC_COMMAND( sendFileAndCheckBS, "send file to BS", "<file to send> <file name to be written>")
{
if (args.size() != 2)
return false;
CIFile fi;
if (!fi.open(args[0]))
return false;
std::vector<uint8> buffer(fi.getFileSize());
fi.serialBuffer(&(buffer[0]), buffer.size());
CBackupMsgSaveFile msgSave;
msgSave.Data.serialBuffer(&(buffer[0]), buffer.size());
msgSave.FileName = args[1];
Bsi.sendFile(msgSave, true);
return true;
}
NLMISC_COMMAND( appendFileBS, "append file to BS", "<file to send> <file name to be written>")
{
if (args.size() != 2)
return false;
CIFile fi;
if (!fi.open(args[0]))
return false;
std::vector<uint8> buffer(fi.getFileSize());
fi.serialBuffer(&(buffer[0]), buffer.size());
CBackupMsgSaveFile msgSave;
msgSave.Data.serialBuffer(&(buffer[0]), buffer.size());
msgSave.FileName = args[1];
Bsi.append(msgSave);
return true;
}
NLMISC_COMMAND( appendLineBS, "append line to file via BS", "<line to be sent> <file name to be written>")
{
if (args.size() != 2)
return false;
Bsi.append(args[0], args[1]);
return true;
}
NLMISC_COMMAND( deleteFileBS, "delete file via BS", "<file name to be deleted> [backup file]")
{
if (args.size() < 1 || args.size() > 2)
return false;
bool backupFile = false;
if (args.size() == 2)
{
NLMISC::fromString(args[1], backupFile);
}
Bsi.deleteFile(args[0], backupFile);
return true;
}
class CFileReceiveCb: public IBackupFileReceiveCallback
{
public:
virtual void callback(const CFileDescription& fileDescription, NLMISC::IStream& dataStream)
{
nlinfo("received file '%s'", fileDescription.FileName.c_str());
}
};
NLMISC_COMMAND( loadFileBS, "load a file from BS", "<file to be loaded>")
{
if (args.size() != 1)
return false;
Bsi.requestFile(args[0], new CFileReceiveCb());
return true;
}
*/
class CFileClassReceiveCb: public IBackupFileClassReceiveCallback
{
public:
virtual void callback(const CFileDescriptionContainer& list)
{
nlinfo("received fileclass");
uint i;
for (i=0; i<list.size(); ++i)
nlinfo("file: %s", list[i].FileName.c_str());
nlinfo("end of class");
}
};
NLMISC_COMMAND( loadFileClassBS, "load a fileclass from BS", "<directory> <classes to be loaded>")
{
if (args.size() <= 1)
return false;
std::string directory;
std::vector<CBackupFileClass> classes;
CBackupFileClass bclass;
directory = args[0];
uint i;
for (i=1; i<args.size(); ++i)
bclass.Patterns.push_back(args[i]);
classes.push_back(bclass);
Bsi.requestFileClass(directory, classes, new CFileClassReceiveCb());
return true;
}
//NLMISC_COMMAND( dumpVectorMemory, "Dump memory used in vector", "")
//{
// static char buffer[1024*16];
// _vector_memory_usage::dump_memory(buffer, 1024*16);
//
// log.displayNL("Vector memory usage : \n%s", buffer);
//
// return true;
//}