1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "shutdown_handler.h"
23 #include "nel/net/service.h"
24 #include "nel/net/message.h"
25 #include "nel/net/unified_network.h"
27 #include "player_manager/player_manager.h"
28 #include "player_manager/player.h"
31 using namespace NLMISC
;
32 using namespace NLNET
;
36 CShutdownHandler::TState
CShutdownHandler::_State
= CShutdownHandler::Running
;
39 NLMISC::TTime
CShutdownHandler::_ShutdownTimeout
= 0;
42 NLMISC::TTime
CShutdownHandler::_NextBroadcastMessage
;
44 // ShardOpen has been closed
45 bool CShutdownHandler::_ShardClosed
= false;
47 // Broadcast Message Rate
48 uint
CShutdownHandler::_BroadcastMessageRate
= 0;
50 // Game cycle of last config file check
51 NLMISC::TGameCycle
CAutomaticShutdownHandler::_LastGCChecked
= 0;
53 static const TSecTime MaxTime
= ~0; //std::numeric_limits<TSecTime>::max()
55 // Time of next planned shutdown sequence start
56 TSecTime
CAutomaticShutdownHandler::_NextPlannedShutdownStartTime
= MaxTime
;
58 // Time of next planned shutdown sequence end
59 TSecTime
CAutomaticShutdownHandler::_NextPlannedShutdownEndTime
= MaxTime
;
63 * Shutdown Counter, in minutes
65 CVariable
<uint
> ShutdownCounter("egs", "ShutdownCounter", "Time to shutdown in minutes", 5, 0, true);
68 * Broadcast shutdown message rate in seconds
70 CVariable
<uint
> BroadcastShutdownMessageRate("egs", "BroadcastShutdownMessageRate", "Number of seconds between 2 shutdown message in seconds", 30, 0, true);
73 * Close shard Access at
75 CVariable
<uint
> CloseShardAccessAt("egs", "CloseShardAccessAt", "Time to shutdown to close shard access, in seconds", 60, 0, true);
78 // Callback for DailyShutdownSequenceTime
79 void cbChangeDailyShutdownSequenceTime( IVariable
& var
)
81 CAutomaticShutdownHandler::computePlannedShutdownTimes();
85 * DailyShutdownSequenceTime
87 CVariable
<string
> 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 );
90 * Daily Shutdown Counter, in minutes
92 CVariable
<uint
> DailyShutdownCounterMinutes("egs", "DailyShutdownCounterMinutes", "Time to shutdown in minutes", 1, 0, true);
95 * DailyShutdownBroadcastMessage
97 CVariable
<string
> DailyShutdownBroadcastMessage("egs","DailyShutdownBroadcastMessage", "Message to broadcast before daily shutdown", string("The shard will be shut down in 1 minute"), 0, true );
100 * CheckShutdownPeriodGC
102 CVariable
<uint
> CheckShutdownPeriodGC("egs","CheckShutdownPeriodGC", "Automatic shutdown sequence is tested every CheckShutdownPeriodGC game cycles", 50, 0, true );
108 void CShutdownHandler::init()
111 _ShutdownTimeout
= NLMISC::CTime::getLocalTime();
112 _NextBroadcastMessage
= NLMISC::CTime::getLocalTime();
113 _ShardClosed
= false;
119 void CShutdownHandler::update()
121 if (_State
== ShuttingDown
)
123 NLMISC::TTime now
= NLMISC::CTime::getLocalTime();
126 if (_ShutdownTimeout
<= now
)
128 nlinfo("CShutdownHandler::update(): disconnect all players from shard");
136 // time to close shard access?
137 if (_ShutdownTimeout
-(CloseShardAccessAt
*60*1000) <= now
&& !_ShardClosed
)
139 nlinfo("CShutdownHandler::update(): close access to shard, SET_SHARD_OPEN sent to WS");
141 // send WS setShardOpen message
142 CMessage
msgShardOpen("SET_SHARD_OPEN");
144 msgShardOpen
.serial(close
);
145 CUnifiedNetwork::getInstance()->send("WS", msgShardOpen
);
150 // time to broadcast message?
151 if (_NextBroadcastMessage
<= now
)
153 broadcastShutdownMessage();
161 void CShutdownHandler::release()
170 * Start Shutdown Counter
172 void CShutdownHandler::startShutdown(sint shutdownCounter
, sint broadcastMessageRate
)
174 nlinfo("CShutdownHandler::startShutdown(): starting count down to shutdown");
176 if (_State
!= Running
)
178 nlinfo("CShutdownHandler::startShutdown(): shutdown already started, left as is");
182 NLMISC::TTime now
= NLMISC::CTime::getLocalTime();
184 nlinfo("CShutdownHandler::startShutdown(): counter set to %u seconds", ShutdownCounter
.get());
187 _State
= ShuttingDown
;
188 _ShutdownTimeout
= now
+ (shutdownCounter
> 0 ? shutdownCounter
: ShutdownCounter
)*60*1000;
189 _BroadcastMessageRate
= (broadcastMessageRate
> 0 ? broadcastMessageRate
: BroadcastShutdownMessageRate
);
190 _NextBroadcastMessage
= now
;
191 _ShardClosed
= false;
197 void CShutdownHandler::cancelShutdown()
199 nlinfo("CShutdownHandler::cancelShutdown(): cancelling shard shutdown");
201 if (_State
!= ShuttingDown
)
203 nlinfo("CShutdownHandler::cancelShutdown(): shard is not currently shutting down, shard left as is");
209 broadcastMessage(std::string("Shutting down cancelled"));
213 nlinfo("CShutdownHandler::cancelShutdown(): WS ShardOpen state modified, sending restore request");
214 CMessage
msgShardOpen("RESTORE_SHARD_OPEN");
215 CUnifiedNetwork::getInstance()->send("WS", msgShardOpen
);
221 * Actually reset WS ShardOpen variable to OpenForAll
223 void CShutdownHandler::restartShard()
225 nlinfo("CShutdownHandler::restartShard(): restarting shard after shutdown");
227 if (_State
!= Closed
)
229 nlinfo("CShutdownHandler::restartShard(): shard is not closed");
235 broadcastMessage(std::string("Shard is now restarted and open to public."));
237 // send SET_SHARD_OPEN to WS
238 nlinfo("CShutdownHandler::restartShard(): WS ShardOpen state modified, sending restore request");
239 CMessage
msgShardOpen("RESTORE_SHARD_OPEN");
240 CUnifiedNetwork::getInstance()->send("WS", msgShardOpen
);
248 * Get current shard state
250 std::string
CShutdownHandler::getState()
252 if (_State
== Running
)
256 else if (_State
== ShuttingDown
)
258 sint shutdown
= (sint
)((_ShutdownTimeout
-NLMISC::CTime::getLocalTime()) / 1000);
262 return NLMISC::toString("Shutdown in %d seconds, shard access closed", shutdown
);
266 return NLMISC::toString("Shutdown in %d seconds", shutdown
);
271 return "Closed, ready to restart";
279 void CShutdownHandler::broadcastMessage(const ucstring
& message
)
281 _NextBroadcastMessage
= NLMISC::CTime::getLocalTime() + _BroadcastMessageRate
*1000;
283 /// \todo handle ucstring somewhere here...
284 PlayerManager
.broadcastMessage(1, 0, 0, message
.toString());
288 * Broadcast Shutdown message
290 void CShutdownHandler::broadcastShutdownMessage()
292 nlinfo("CShutdownHandler::broadcastShutdownMessage(): broadcasting shutdown message");
294 std::string shutdownMessage
;
295 const sint timeAccuracy
= 10;
297 sint shutdown
= (sint
)((_ShutdownTimeout
-NLMISC::CTime::getLocalTime()) / 1000);
299 shutdown
+= (timeAccuracy
/2);
305 shutdown
= shutdown
- (shutdown
%timeAccuracy
);
307 if (shutdown
< timeAccuracy
)
309 broadcastMessage(std::string("Shutting down..."));
315 broadcastMessage(NLMISC::toString("Shard shuts down in %d seconds", shutdown
));
319 uint min
= shutdown
/60;
320 uint sec
= shutdown
%60;
321 broadcastMessage(NLMISC::toString("Shard shuts down in %dmn %ds", min
, sec
));
328 * Disconnect all players
330 void CShutdownHandler::disconnectPlayers()
332 nlinfo("CShutdownHandler::disconnectPlayers(): disconnecting all players");
334 CMessage
msgout("DISCONNECT_ALL_CLIENTS");
335 CUnifiedNetwork::getInstance()->send("FS", msgout
);
338 const CPlayerManager::TMapPlayers& playerMap = PlayerManager.getPlayers();
340 // browse through all players to disconnect them
341 CPlayerManager::TMapPlayers::const_iterator it;
342 for (it = playerMap.begin(); it != playerMap.end(); )
344 const CPlayerManager::SCPlayer& player = (*it).second;
347 if (player.Player != NULL)
349 uint32 userId = player.Player->getUserId();
350 PlayerManager.savePlayer(userId);
351 PlayerManager.disconnectPlayer(userId);
360 * If the shutdown sequence is started, it can be canceled by changing DailyShutdownSequenceTime.
362 void CAutomaticShutdownHandler::update()
364 if ( CTickEventHandler::getGameCycle() - _LastGCChecked
>= CheckShutdownPeriodGC
)
366 _LastGCChecked
= CTickEventHandler::getGameCycle();
367 TSecTime nowSec
= NLMISC::CTime::getSecondsSince1970();
369 // Time to start an automatic shutdown sequence?
370 if ( nowSec
>= _NextPlannedShutdownStartTime
)
372 string msg
= DailyShutdownBroadcastMessage
.get();
373 PlayerManager
.broadcastMessage( 1, 0, 0, msg
);
374 nlinfo( msg
.c_str() );
375 _NextPlannedShutdownEndTime
= _NextPlannedShutdownStartTime
+ (DailyShutdownCounterMinutes
.get()*60);
376 _NextPlannedShutdownStartTime
= MaxTime
;
379 else if ( nowSec
>= _NextPlannedShutdownEndTime
)
381 _NextPlannedShutdownEndTime
= MaxTime
;
382 IService::getInstance()->exit();
389 * Use daily shutdown sequence time to compute next time
391 void CAutomaticShutdownHandler::computePlannedShutdownTimes( NLMISC::CLog
*log
)
393 string dailyTimeStr
= DailyShutdownSequenceTime
.get();
394 if ( dailyTimeStr
.empty() || (dailyTimeStr
== "-1") )
396 // No automatic shutdown sequence
397 _NextPlannedShutdownStartTime
= MaxTime
;
398 _NextPlannedShutdownEndTime
= MaxTime
;
400 log
->displayNL( "Automatic shutdown sequence disabled" );
404 // Setup next automatic shutdown sequence
405 time_t currentTime
= time( NULL
);
406 struct tm
*localTime
= localtime( ¤tTime
);
407 struct tm shutdownTime
= *localTime
;
408 string::size_type cp
= dailyTimeStr
.find( ':' );
409 shutdownTime
.tm_hour
= atoi( dailyTimeStr
.substr( 0, cp
).c_str() );
410 shutdownTime
.tm_min
= 0;
411 if ( cp
!= string::npos
)
412 shutdownTime
.tm_min
= atoi( dailyTimeStr
.substr( cp
+1 ).c_str() );
413 shutdownTime
.tm_sec
= 0;
414 shutdownTime
.tm_isdst
= -1;
415 _NextPlannedShutdownStartTime
= nl_mktime( &shutdownTime
);
417 if ( _NextPlannedShutdownStartTime
> (TSecTime
)currentTime
)
419 dayWhenStr
= "today";
423 dayWhenStr
= "tomorrow";
424 ++shutdownTime
.tm_mday
;
425 _NextPlannedShutdownStartTime
= nl_mktime( &shutdownTime
);
427 _NextPlannedShutdownEndTime
= _NextPlannedShutdownStartTime
+ (DailyShutdownCounterMinutes
.get()*60);
429 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() );
434 NLMISC_DYNVARIABLE(std::string
, ShutdownState
, "Current shutdown state of the shard, as a string (Read only)")
436 // read or write the variable
438 *pointer
= CShutdownHandler::getState();
444 NLMISC_COMMAND(startShutdown
, "Ask the EGS to shutdown the whole shard", "[time to shutdown, minutes] [time between 2 messages, seconds]")
452 if (args
.size() >= 1)
453 counter
= atoi(args
[0].c_str());
455 if (args
.size() >= 2)
456 msgRate
= atoi(args
[1].c_str());
458 CShutdownHandler::startShutdown(counter
, msgRate
);
462 NLMISC_COMMAND(cancelShutdown
, "Cancel current shutdown in progress", "")
464 CShutdownHandler::cancelShutdown();
468 NLMISC_COMMAND(restartShard
, "Restart the shard after a shutdown", "")
470 CShutdownHandler::restartShard();