2012-05-29 13:31:11 +00:00
// 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
# define NOMINMAX
# 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 ( ~ 0 ) , 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 ( " \t TICKS, %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 , ~ 0 , 0 } ;
for ( std : : vector < CMirrorTimeMeasureHistory > : : const_iterator ihm = HistoryByMirror . begin ( ) ; ihm ! = HistoryByMirror . end ( ) ; + + ihm )
{
log - > displayRawNL ( " \t MS-%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 ( " \t All mirror services: " ) ;
gatheredStats [ stat ] . displayStat ( log , MirrorTimeMeasureTypeToCString , ( stat = = MHTSum ) ? ( uint ) HistoryByMirror . size ( ) : 1 ) ; // not displaying the 3 stats
}
}
{
CServiceTimeMeasure gatheredStats [ NbTimeMeasureHistoryStats ] = { 0 , ~ 0 , 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 ( " \t All 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;
//}