// 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 . #include "stdpch.h" // net #include "nel/net/unified_network.h" #include "nel/net/service.h" #include "nel/misc/command.h" #include "nel/misc/hierarchical_timer.h" #include "tick_event_handler.h" #include "time_weather_season/time_and_season.h" #include "tick_proxy_time_measure.h" #include "timer.h" using namespace std; using namespace NLMISC; using namespace NLNET; // game time TGameTime CTickEventHandler::_GameTime = 0; // game time step TGameTime CTickEventHandler::_GameTimeStep = 0.1; // game cycle TGameCycle CTickEventHandler::_GameCycle = 0; /// Shall we tock at the beginning of at the end of tickUpdate() ? bool CTickEventHandler::_TockAtBeginOfTickUpdate = false; NLMISC::CLightMemDisplayer RecentHistory; NLMISC::CLog _QuickLog; // user callbacks void (*userCbUpdate)() = NULL; void (*userCbSync)() = NULL; // TickSpeedLoop variable. We store 1 min to get the average (see "help TickSpeedLoop") CVariable TickSpeedLoop("tick", "TickSpeedLoop", "Duration of the tick update (in ms)", -1, 600); // TotalSpeedLoop variable. We store 1 min to get the average. CVariable TotalSpeedLoop("tick", "TotalSpeedLoop", "Time of game cycle (from TICK from end of UMM) (in ms)", -1, 600 ); // Time of state C1 is TickSpeedLoop. // Time of state C2 (see ms_automaton.cpp) CVariable ProcessMirrorUpdatesSpeed("tick", "ProcessMirrorUpdatesSpeed", "", -1, 600 ); // Time of state C3 (see ms_automaton.cpp) CVariable ReceiveMessagesViaMirrorSpeed("tick", "ReceiveMessagesViaMirrorSpeed", "", -1, 600 ); TAccurateTime TimeBeforeTickUpdate = 0; //----------------------------------------------- // cbRegistered // //----------------------------------------------- static void cbRegistered(CMessage& msgin, const std::string &serviceName, TServiceId serviceId) { TGameTime gameTime = 0; msgin.serial( gameTime ); CTickEventHandler::setGameTime( gameTime ); TGameTime gameTimeStep = 0; msgin.serial( gameTimeStep ); CTickEventHandler::setGameTimeStep( gameTimeStep ); TGameCycle gameCycle = 0; msgin.serial( gameCycle ); CTickEventHandler::setGameCycle( gameCycle ); // user callback if( userCbSync ) { userCbSync(); } CTimerManager::getInstance()->syncTick(); } // cbRegistered // //----------------------------------------------- // cbTick // //----------------------------------------------- static void cbTick(CMessage& msgin, const std::string &serviceName, NLNET::TServiceId serviceId) { CTickEventHandler::tickUpdate( serviceId ); } // cbTick // //----------------------------------------------- // cbStepAndTick // //----------------------------------------------- static void cbStepAndTick(CMessage& msgin, const std::string &serviceName, TServiceId serviceId) { TGameTime gameTimeStep = 0; msgin.serial( gameTimeStep ); CTickEventHandler::setGameTimeStep( gameTimeStep ); CTickEventHandler::tickUpdate( serviceId ); } // cbStepAndTick // //----------------------------------------------- // cbDisplayTime // //----------------------------------------------- static void cbDisplayTime( CMessage& msgin, const string &serviceName, TServiceId serviceId ) { nlinfo(" : Time in the service : %f", (double)CTickEventHandler::getGameTime()); } // cbDisplayTime // // array of callback items TUnifiedCallbackItem cbArray[] = { { "REGISTERED", cbRegistered }, { "TICK", cbTick }, { "STEP_TICK", cbStepAndTick }, { "DISPLAY_TIME", cbDisplayTime }, }; //----------------------------------------------- // tickUpdate // //----------------------------------------------- void CTickEventHandler::tickUpdate( TServiceId serviceId ) { // increment the time and the number of cycles _GameTime += (TGameTime)_GameTimeStep; _GameCycle++; //nldebug( "TCK-%u: Tick", getGameCycle() ); time_t t; time( &t ); _QuickLog.displayNL( "%s: %u: Tick", IDisplayer::dateToHumanString( t ), getGameCycle() ); //nldebug( "--GC-%u-->", _GameCycle ); if ( _TockAtBeginOfTickUpdate ) sendTockBack( serviceId ); // user update callback if ( userCbUpdate ) { H_AUTO(TickUpdate); TimeBeforeTickUpdate = getAccurateTime(); userCbUpdate(); TickSpeedLoop = accTimeToMs( getAccurateTime() - TimeBeforeTickUpdate ); } if ( ! _TockAtBeginOfTickUpdate ) sendTockBack( serviceId ); } //----------------------------------------------- // sendTockBack // //----------------------------------------------- void CTickEventHandler::sendTockBack( NLNET::TServiceId serviceId ) { // Send back a tock CMessage msgout( "TOCK" ); uint16 v = (TickSpeedLoop.get()<0xFFFF) ? (uint16)TickSpeedLoop.get() : 0xFFFF; msgout.serial( v ); v = (uint16)ProcessMirrorUpdatesSpeed.get(); msgout.serial( v ); v = (uint16)ReceiveMessagesViaMirrorSpeed.get(); msgout.serial( v ); v = (TotalSpeedLoop.get()<0xFFFF) ? (uint16)TotalSpeedLoop.get() : 0xFFFF; msgout.serial( v ); CUnifiedNetwork::getInstance()->send( serviceId, msgout ); TSockId host; CCallbackNetBase *cnb; cnb = CUnifiedNetwork::getInstance()->getNetBase(serviceId, host); if( cnb ) { cnb->flush( host ); } //nldebug( "TCK-%u: Tocked", getGameCycle() ); time_t t; time( &t ); _QuickLog.displayNL( "%s: %u: Tocked", IDisplayer::dateToHumanString( t ), getGameCycle() ); } //----------------------------------------------- // cbTicksUp // //----------------------------------------------- void cbTicksUp(const std::string &serviceName, TServiceId id, void *arg) { if ( CUnifiedNetwork::getInstance()->isServiceLocal( id ) ) { nlinfo ("Tick Event Handler: Local MS is up, I can start"); // register to tick service (or tick proxy) CMessage msgout("REGISTER"); bool tocking = true; uint16 threshold = 0; CConfigFile::CVar *cvTocking = IService::getInstance()->ConfigFile.getVarPtr("Tocking"); if(cvTocking) tocking = (cvTocking->asInt()==0) ? false : true; if(!tocking) { CConfigFile::CVar *cvThreshold = IService::getInstance()->ConfigFile.getVarPtr("Threshold"); if(cvThreshold) threshold = cvThreshold->asInt(); } nlinfo("This service %s and has a threshold of %d",(tocking?"tocks":"doesn't tock"), threshold); msgout.serial( tocking ); msgout.serial( threshold ); CUnifiedNetwork::getInstance()->send( id, msgout ); } } // cbTicksUp // void cbTicksDown(const std::string &serviceName, TServiceId id, void *arg) { if ( CUnifiedNetwork::getInstance()->isServiceLocal( id ) ) { TickSpeedLoop = -1; } } //-------------------------------------------------------------- // init // //-------------------------------------------------------------- void CTickEventHandler::init( void (*updateFunc)(), void (*syncFunc)(), bool tockAtBeginOfTickUpdate ) { // set the callback userCbUpdate = updateFunc; nlassert( !userCbSync ); // the callback must be init first by this method (mirror must be init userCbSync = syncFunc; _TockAtBeginOfTickUpdate = tockAtBeginOfTickUpdate; // we unactive tick message to avoid flooding DebugLog->addNegativeFilter ("TICK"); DebugLog->addNegativeFilter ("TOCK"); DebugLog->addNegativeFilter ("14+5"); CUnifiedNetwork::getInstance()->addCallbackArray(cbArray,sizeof(cbArray)/sizeof(cbArray[0])); CUnifiedNetwork::getInstance()->setServiceUpCallback ("MS", cbTicksUp, NULL); CUnifiedNetwork::getInstance()->setServiceDownCallback ("MS", cbTicksDown, NULL); RecentHistory.setParam( 6 ); _QuickLog.addDisplayer( &RecentHistory, false ); } // init /* * Set a callback to call when receiving the first game cycle (call this method in your init()) * \param syncFunc will be call when the ticks send the syncro * \param allowReplaceCallback true if we allow the callback to be replaced * \return previous callback if exist, otherwise NULL */ TUserSyncCallback CTickEventHandler::setSyncCallback( TUserSyncCallback syncFunc, bool allowReplaceCallback ) { if( userCbSync ) { // sync callback is already set, check if replacement is allowed nlassert( allowReplaceCallback ); TUserSyncCallback previousSyncFunc = userCbSync; userCbSync = syncFunc; return previousSyncFunc; } else { userCbSync = syncFunc; return NULL; } } // setSyncCallback // NLMISC_CATEGORISED_DYNVARIABLE(tick, NLMISC::TGameCycle, TickGameCycle, "game cycle (in tick)") { // we can only read the value if (get) *pointer = CTickEventHandler::getGameCycle (); } NLMISC_CATEGORISED_DYNVARIABLE(tick, NLMISC::TGameTime, TickGameTime, "game time (in second)") { // we can only read the value if (get) *pointer = CTickEventHandler::getGameTime (); } NLMISC_CATEGORISED_DYNVARIABLE(tick, bool, ExpediteTock, "ExpediteTock") { if (get) *pointer = CTickEventHandler::getTockAtBeginOfTickUpdate(); } NLMISC_CATEGORISED_COMMAND(tick,displayTickRecentHistory,"Display the history of tick events","") { RecentHistory.write( &log ); return true; } NLMISC_CATEGORISED_COMMAND(tick,setDayTime,"set the current time of the day"," ") { if ( args.size() != 2) return false; float newHour; fromString(args[0], newHour); if ( newHour > 23.0f || newHour < 0.0f ) { log.displayNL( "hour must be < 24" ); return true; } float newMinute; fromString(args[1], newMinute); if ( newMinute > 59.0f || newMinute < 0.0f ) { log.displayNL( "minutes must be < 60" ); return true; } newHour+= newMinute / 60.0f; float prevHour = (float)fmod(CTickEventHandler::getGameCycle() / float(RYZOM_HOURS_IN_TICKS), 24.0f ); if ( newHour >= prevHour ) { CTickEventHandler::setGameCycle( (NLMISC::TGameCycle) (CTickEventHandler::getGameCycle() + (newHour-prevHour) * RYZOM_HOURS_IN_TICKS) ); } else { CTickEventHandler::setGameCycle( (NLMISC::TGameCycle) (CTickEventHandler::getGameCycle() + (24.0f + newHour - prevHour) * RYZOM_HOURS_IN_TICKS) ); } return true; }