// 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 . #error DERPRECATED #include "stdpch.h" #include "shutdown_handler.h" #include "nel/net/service.h" #include "nel/net/message.h" #include "nel/net/unified_network.h" #include "player_manager/player_manager.h" #include "player_manager/player.h" using namespace std; using namespace NLMISC; using namespace NLNET; // Shutdown State CShutdownHandler::TState CShutdownHandler::_State = CShutdownHandler::Running; // Shutdown Timeout NLMISC::TTime CShutdownHandler::_ShutdownTimeout = 0; // Shutdown Timeout NLMISC::TTime CShutdownHandler::_NextBroadcastMessage; // ShardOpen has been closed bool CShutdownHandler::_ShardClosed = false; // Broadcast Message Rate uint CShutdownHandler::_BroadcastMessageRate = 0; // Game cycle of last config file check NLMISC::TGameCycle CAutomaticShutdownHandler::_LastGCChecked = 0; static const TSecTime MaxTime = ~0; //std::numeric_limits::max() // Time of next planned shutdown sequence start TSecTime CAutomaticShutdownHandler::_NextPlannedShutdownStartTime = MaxTime; // Time of next planned shutdown sequence end TSecTime CAutomaticShutdownHandler::_NextPlannedShutdownEndTime = MaxTime; /** * Shutdown Counter, in minutes */ CVariable ShutdownCounter("egs", "ShutdownCounter", "Time to shutdown in minutes", 5, 0, true); /** * Broadcast shutdown message rate in seconds */ CVariable BroadcastShutdownMessageRate("egs", "BroadcastShutdownMessageRate", "Number of seconds between 2 shutdown message in seconds", 30, 0, true); /** * Close shard Access at */ CVariable CloseShardAccessAt("egs", "CloseShardAccessAt", "Time to shutdown to close shard access, in seconds", 60, 0, true); // Callback for DailyShutdownSequenceTime void cbChangeDailyShutdownSequenceTime( IVariable& var ) { CAutomaticShutdownHandler::computePlannedShutdownTimes(); } /** * DailyShutdownSequenceTime */ CVariable DailyShutdownSequenceTime("egs","DailyShutdownSequenceTime", "Time of day when the service will start a shutdown sequence (ex: \"20:55\"). Set \"\" or -1 to disable)", string(), 0, true, cbChangeDailyShutdownSequenceTime, true ); /** * Daily Shutdown Counter, in minutes */ CVariable DailyShutdownCounterMinutes("egs", "DailyShutdownCounterMinutes", "Time to shutdown in minutes", 1, 0, true); /** * DailyShutdownBroadcastMessage */ CVariable DailyShutdownBroadcastMessage("egs","DailyShutdownBroadcastMessage", "Message to broadcast before daily shutdown", string("The shard will be shut down in 1 minute"), 0, true ); /** * CheckShutdownPeriodGC */ CVariable CheckShutdownPeriodGC("egs","CheckShutdownPeriodGC", "Automatic shutdown sequence is tested every CheckShutdownPeriodGC game cycles", 50, 0, true ); /* * Inits Handler */ void CShutdownHandler::init() { _State = Running; _ShutdownTimeout = NLMISC::CTime::getLocalTime(); _NextBroadcastMessage = NLMISC::CTime::getLocalTime(); _ShardClosed = false; } /* * Update Handler */ void CShutdownHandler::update() { if (_State == ShuttingDown) { NLMISC::TTime now = NLMISC::CTime::getLocalTime(); // time to shutdown? if (_ShutdownTimeout <= now) { nlinfo("CShutdownHandler::update(): disconnect all players from shard"); disconnectPlayers(); _State = Closed; return; } // time to close shard access? if (_ShutdownTimeout-(CloseShardAccessAt*60*1000) <= now && !_ShardClosed) { nlinfo("CShutdownHandler::update(): close access to shard, SET_SHARD_OPEN sent to WS"); // send WS setShardOpen message CMessage msgShardOpen("SET_SHARD_OPEN"); uint8 close = 0; msgShardOpen.serial(close); CUnifiedNetwork::getInstance()->send("WS", msgShardOpen); _ShardClosed = true; } // time to broadcast message? if (_NextBroadcastMessage <= now) { broadcastShutdownMessage(); } } } /* * Release Handler */ void CShutdownHandler::release() { } /* * Start Shutdown Counter */ void CShutdownHandler::startShutdown(sint shutdownCounter, sint broadcastMessageRate) { nlinfo("CShutdownHandler::startShutdown(): starting count down to shutdown"); if (_State != Running) { nlinfo("CShutdownHandler::startShutdown(): shutdown already started, left as is"); return; } NLMISC::TTime now = NLMISC::CTime::getLocalTime(); nlinfo("CShutdownHandler::startShutdown(): counter set to %u seconds", ShutdownCounter.get()); // time in ms _State = ShuttingDown; _ShutdownTimeout = now + (shutdownCounter > 0 ? shutdownCounter : ShutdownCounter)*60*1000; _BroadcastMessageRate = (broadcastMessageRate > 0 ? broadcastMessageRate : BroadcastShutdownMessageRate); _NextBroadcastMessage = now; _ShardClosed = false; } /* * Cancel Shutdown */ void CShutdownHandler::cancelShutdown() { nlinfo("CShutdownHandler::cancelShutdown(): cancelling shard shutdown"); if (_State != ShuttingDown) { nlinfo("CShutdownHandler::cancelShutdown(): shard is not currently shutting down, shard left as is"); return; } _State = Running; broadcastMessage(std::string("Shutting down cancelled")); if (_ShardClosed) { nlinfo("CShutdownHandler::cancelShutdown(): WS ShardOpen state modified, sending restore request"); CMessage msgShardOpen("RESTORE_SHARD_OPEN"); CUnifiedNetwork::getInstance()->send("WS", msgShardOpen); } } /* * Restart shard * Actually reset WS ShardOpen variable to OpenForAll */ void CShutdownHandler::restartShard() { nlinfo("CShutdownHandler::restartShard(): restarting shard after shutdown"); if (_State != Closed) { nlinfo("CShutdownHandler::restartShard(): shard is not closed"); return; } _State = Running; broadcastMessage(std::string("Shard is now restarted and open to public.")); // send SET_SHARD_OPEN to WS nlinfo("CShutdownHandler::restartShard(): WS ShardOpen state modified, sending restore request"); CMessage msgShardOpen("RESTORE_SHARD_OPEN"); CUnifiedNetwork::getInstance()->send("WS", msgShardOpen); } /* * Get current shard state */ std::string CShutdownHandler::getState() { if (_State == Running) { return "Running"; } else if (_State == ShuttingDown) { sint shutdown = (sint)((_ShutdownTimeout-NLMISC::CTime::getLocalTime()) / 1000); if (_ShardClosed) { return NLMISC::toString("Shutdown in %d seconds, shard access closed", shutdown); } else { return NLMISC::toString("Shutdown in %d seconds", shutdown); } } else { return "Closed, ready to restart"; } } /* * Broadcast message */ void CShutdownHandler::broadcastMessage(const ucstring& message) { _NextBroadcastMessage = NLMISC::CTime::getLocalTime() + _BroadcastMessageRate*1000; /// \todo handle ucstring somewhere here... PlayerManager.broadcastMessage(1, 0, 0, message.toString()); } /* * Broadcast Shutdown message */ void CShutdownHandler::broadcastShutdownMessage() { nlinfo("CShutdownHandler::broadcastShutdownMessage(): broadcasting shutdown message"); std::string shutdownMessage; const sint timeAccuracy = 10; sint shutdown = (sint)((_ShutdownTimeout-NLMISC::CTime::getLocalTime()) / 1000); shutdown += (timeAccuracy/2); // clamp to 0 if (shutdown < 0) shutdown = 0; shutdown = shutdown - (shutdown%timeAccuracy); if (shutdown < timeAccuracy) { broadcastMessage(std::string("Shutting down...")); } else { if (shutdown < 60) { broadcastMessage(NLMISC::toString("Shard shuts down in %d seconds", shutdown)); } else { uint min = shutdown/60; uint sec = shutdown%60; broadcastMessage(NLMISC::toString("Shard shuts down in %dmn %ds", min, sec)); } } } /* * Disconnect all players */ void CShutdownHandler::disconnectPlayers() { nlinfo("CShutdownHandler::disconnectPlayers(): disconnecting all players"); CMessage msgout("DISCONNECT_ALL_CLIENTS"); CUnifiedNetwork::getInstance()->send("FS", msgout); /* const CPlayerManager::TMapPlayers& playerMap = PlayerManager.getPlayers(); // browse through all players to disconnect them CPlayerManager::TMapPlayers::const_iterator it; for (it = playerMap.begin(); it != playerMap.end(); ) { const CPlayerManager::SCPlayer& player = (*it).second; ++it; if (player.Player != NULL) { uint32 userId = player.Player->getUserId(); PlayerManager.savePlayer(userId); PlayerManager.disconnectPlayer(userId); } } */ } /* * Update. * If the shutdown sequence is started, it can be canceled by changing DailyShutdownSequenceTime. */ void CAutomaticShutdownHandler::update() { if ( CTickEventHandler::getGameCycle() - _LastGCChecked >= CheckShutdownPeriodGC ) { _LastGCChecked = CTickEventHandler::getGameCycle(); TSecTime nowSec = NLMISC::CTime::getSecondsSince1970(); // Time to start an automatic shutdown sequence? if ( nowSec >= _NextPlannedShutdownStartTime ) { string msg = DailyShutdownBroadcastMessage.get(); PlayerManager.broadcastMessage( 1, 0, 0, msg ); nlinfo( msg.c_str() ); _NextPlannedShutdownEndTime = _NextPlannedShutdownStartTime + (DailyShutdownCounterMinutes.get()*60); _NextPlannedShutdownStartTime = MaxTime; } // Time to shutdown? else if ( nowSec >= _NextPlannedShutdownEndTime ) { _NextPlannedShutdownEndTime = MaxTime; IService::getInstance()->exit(); } } } /* * Use daily shutdown sequence time to compute next time */ void CAutomaticShutdownHandler::computePlannedShutdownTimes( NLMISC::CLog *log ) { string dailyTimeStr = DailyShutdownSequenceTime.get(); if ( dailyTimeStr.empty() || (dailyTimeStr == "-1") ) { // No automatic shutdown sequence _NextPlannedShutdownStartTime = MaxTime; _NextPlannedShutdownEndTime = MaxTime; if ( log ) log->displayNL( "Automatic shutdown sequence disabled" ); } else { // Setup next automatic shutdown sequence time_t currentTime = time( NULL ); struct tm *localTime = localtime( ¤tTime ); struct tm shutdownTime = *localTime; string::size_type cp = dailyTimeStr.find( ':' ); shutdownTime.tm_hour = atoi( dailyTimeStr.substr( 0, cp ).c_str() ); shutdownTime.tm_min = 0; if ( cp != string::npos ) shutdownTime.tm_min = atoi( dailyTimeStr.substr( cp+1 ).c_str() ); shutdownTime.tm_sec = 0; shutdownTime.tm_isdst = -1; _NextPlannedShutdownStartTime = nl_mktime( &shutdownTime ); char *dayWhenStr; if ( _NextPlannedShutdownStartTime > (TSecTime)currentTime ) { dayWhenStr = "today"; } else { dayWhenStr = "tomorrow"; ++shutdownTime.tm_mday; _NextPlannedShutdownStartTime = nl_mktime( &shutdownTime ); } _NextPlannedShutdownEndTime = _NextPlannedShutdownStartTime + (DailyShutdownCounterMinutes.get()*60); if ( log ) log->displayNL( "Next automatic shutdown sequence will begin %s at %02u:%02u with %u-minute delay", dayWhenStr, shutdownTime.tm_hour, shutdownTime.tm_min, DailyShutdownCounterMinutes.get() ); } } NLMISC_DYNVARIABLE(std::string, ShutdownState, "Current shutdown state of the shard, as a string (Read only)") { // read or write the variable if (get) *pointer = CShutdownHandler::getState(); } // Commands NLMISC_COMMAND(startShutdown, "Ask the EGS to shutdown the whole shard", "[time to shutdown, minutes] [time between 2 messages, seconds]") { if (args.size() > 2) return false; sint counter = -1; sint msgRate = -1; if (args.size() >= 1) counter = atoi(args[0].c_str()); if (args.size() >= 2) msgRate = atoi(args[1].c_str()); CShutdownHandler::startShutdown(counter, msgRate); return true; } NLMISC_COMMAND(cancelShutdown, "Cancel current shutdown in progress", "") { CShutdownHandler::cancelShutdown(); return true; } NLMISC_COMMAND(restartShard, "Restart the shard after a shutdown", "") { CShutdownHandler::restartShard(); return true; }