8888#include " LFGMgr.h"
8989#include " DisableMgr.h"
9090#include " Language.h"
91- #include " CommandMgr.h"
92- #include " GitRevision.h"
93- #include " UpdateTime.h"
94- #include " GameTime.h"
91+ #include " CommandMgr.h"
92+ #include " GitRevision.h"
93+ #include " UpdateTime.h"
94+ #include " GameTime.h"
95+ #include " ScheduledExit.h"
9596
9697#ifdef ENABLE_ELUNA
9798#include " LuaEngine.h"
108109// WARDEN
109110#include " WardenCheckMgr.h"
110111
111- #include < iostream>
112- #include < sstream>
112+ #include < iostream>
113+ #include < sstream>
113114
114115INSTANTIATE_SINGLETON_1 (World);
115116
@@ -157,8 +158,10 @@ World::World()
157158 m_startTime = m_gameTime;
158159 m_maxActiveSessionCount = 0 ;
159160 m_maxQueuedSessionCount = 0 ;
160- m_MaintenanceTimeChecker = 0 ;
161- m_broadcastEnable = false ;
161+ m_MaintenanceTimeChecker = 0 ;
162+ m_scheduledExitDelay = 0 ;
163+ m_scheduledExitCountdownActive = false ;
164+ m_broadcastEnable = false ;
162165 m_broadcastList.clear ();
163166 m_broadcastWeight = 0 ;
164167
@@ -578,7 +581,7 @@ void World::LoadConfigSettings(bool reload)
578581 setConfig (CONFIG_UINT32_MAX_WHOLIST_RETURNS, " MaxWhoListReturns" , 49 );
579582 setConfig (CONFIG_UINT32_AUTOBROADCAST_INTERVAL, " AutoBroadcast" , 600 );
580583
581- if (getConfig (CONFIG_UINT32_AUTOBROADCAST_INTERVAL) > 0 )
584+ if (getConfig (CONFIG_UINT32_AUTOBROADCAST_INTERVAL) > 0 )
582585 {
583586 m_broadcastEnable = true ;
584587 }
@@ -590,9 +593,11 @@ void World::LoadConfigSettings(bool reload)
590593 if (reload && m_broadcastEnable)
591594 {
592595 m_broadcastTimer.SetInterval (getConfig (CONFIG_UINT32_AUTOBROADCAST_INTERVAL) * IN_MILLISECONDS);
593- }
594-
595- std::string forceLoadGridOnMaps = sConfig .GetStringDefault (" LoadAllGridsOnMaps" , " " );
596+ }
597+
598+ LoadScheduledExitConfig ();
599+
600+ std::string forceLoadGridOnMaps = sConfig .GetStringDefault (" LoadAllGridsOnMaps" , " " );
596601 if (!forceLoadGridOnMaps.empty ())
597602 {
598603 unsigned int pos = 0 ;
@@ -2182,7 +2187,7 @@ void World::_UpdateGameTime()
21822187 m_gameTime = thisTime;
21832188
21842189 // /- if there is a shutdown timer
2185- if (!m_stopEvent && m_ShutdownTimer > 0 && elapsed > 0 )
2190+ if (!m_stopEvent && m_ShutdownTimer > 0 && elapsed > 0 )
21862191 {
21872192 // /- ... and it is overdue, stop the world (set m_stopEvent)
21882193 if (m_ShutdownTimer <= elapsed)
@@ -2197,17 +2202,30 @@ void World::_UpdateGameTime()
21972202 } // minimum timer value to wait idle state
21982203 }
21992204 // /- ... else decrease it and if necessary display a shutdown countdown to the users
2200- else
2201- {
2202- m_ShutdownTimer -= elapsed;
2203-
2204- ShutdownMsg ();
2205- }
2206- }
2207- }
2205+ else
2206+ {
2207+ m_ShutdownTimer -= elapsed;
2208+
2209+ MaNGOS::ScheduledExitCountdownActions actions =
2210+ MaNGOS::GetScheduledExitCountdownActions (
2211+ m_scheduledExitCountdownActive);
2212+ if (actions.sendShutdownTimer )
2213+ {
2214+ ShutdownMsg ();
2215+ }
2216+
2217+ if (actions.sendConfiguredWarnings )
2218+ {
2219+ SendScheduledExitWarnings ();
2220+ }
2221+ }
2222+ }
2223+
2224+ CheckScheduledExit ();
2225+ }
22082226
22092227// / Shutdown the server
2210- void World::ShutdownServ (uint32 time, uint32 options, uint8 exitcode)
2228+ void World::ShutdownServ (uint32 time, uint32 options, uint8 exitcode)
22112229{
22122230 // ignore if server shutdown at next tick
22132231 if (m_stopEvent)
@@ -2235,8 +2253,8 @@ void World::ShutdownServ(uint32 time, uint32 options, uint8 exitcode)
22352253 else
22362254 {
22372255 m_ShutdownTimer = time;
2238- ShutdownMsg (true );
2239- }
2256+ ShutdownMsg (true );
2257+ }
22402258
22412259#ifdef ENABLE_PLAYERBOTS
22422260 sRandomPlayerbotMgr .LogoutAllBots ();
@@ -2249,14 +2267,210 @@ void World::ShutdownServ(uint32 time, uint32 options, uint8 exitcode)
22492267 e->OnShutdownInitiate (ShutdownExitCode (exitcode), ShutdownMask (options));
22502268 }
22512269#endif /* ENABLE_ELUNA */
2252- }
2253-
2254- // / Display a shutdown message to the user(s)
2255- void World::ShutdownMsg (bool show /* = false*/ , Player* player /* = NULL*/ )
2256- {
2257- // not show messages for idle shutdown mode
2258- if (m_ShutdownMask & SHUTDOWN_MASK_IDLE)
2259- {
2270+ }
2271+
2272+ void World::LoadScheduledExitConfig ()
2273+ {
2274+ m_scheduledExit = MaNGOS::ScheduledExitSchedule ();
2275+ m_scheduledExitDelay = 0 ;
2276+ m_scheduledExitWarnings.clear ();
2277+ m_scheduledExitCountdownActive = false ;
2278+
2279+ if (!sConfig .GetBoolDefault (" ScheduledExit.Enable" , false ))
2280+ {
2281+ sLog .outString (" ScheduledExit: disabled" );
2282+ return ;
2283+ }
2284+
2285+ std::string dayText = sConfig .GetStringDefault (" ScheduledExit.DayOfWeek" , " 3" );
2286+ uint32 dayOfWeek = 0 ;
2287+ if (!MaNGOS::ParseScheduledExitUInt32 (dayText, dayOfWeek) || dayOfWeek > 6 )
2288+ {
2289+ sLog .outError (" ScheduledExit: invalid ScheduledExit.DayOfWeek '%s'; "
2290+ " disabling scheduled exit" , dayText.c_str ());
2291+ return ;
2292+ }
2293+
2294+ std::string timeText = sConfig .GetStringDefault (" ScheduledExit.Time" , " 05:00" );
2295+ uint32 hour = 0 ;
2296+ uint32 minute = 0 ;
2297+ if (!MaNGOS::ParseScheduledExitTime (timeText, hour, minute))
2298+ {
2299+ sLog .outError (" ScheduledExit: invalid ScheduledExit.Time '%s'; "
2300+ " disabling scheduled exit" , timeText.c_str ());
2301+ return ;
2302+ }
2303+
2304+ std::string modeText = sConfig .GetStringDefault (" ScheduledExit.Mode" , " restart" );
2305+ MaNGOS::ScheduledExitMode mode = MaNGOS::SCHEDULED_EXIT_MODE_RESTART;
2306+ if (!MaNGOS::ParseScheduledExitMode (modeText, mode))
2307+ {
2308+ sLog .outError (" ScheduledExit: invalid ScheduledExit.Mode '%s'; "
2309+ " disabling scheduled exit" , modeText.c_str ());
2310+ return ;
2311+ }
2312+
2313+ std::string delayText = sConfig .GetStringDefault (" ScheduledExit.Delay" , " 900" );
2314+ uint32 delay = 0 ;
2315+ if (!MaNGOS::ParseScheduledExitUInt32 (delayText, delay))
2316+ {
2317+ sLog .outError (" ScheduledExit: invalid ScheduledExit.Delay '%s'; "
2318+ " disabling scheduled exit" , delayText.c_str ());
2319+ return ;
2320+ }
2321+
2322+ std::vector<std::string> warningErrors;
2323+ std::vector<uint32> warningTimes = MaNGOS::ParseScheduledExitWarningTimes (
2324+ sConfig .GetStringDefault (" ScheduledExit.WarningTimes" , " 900,600,300,60" ),
2325+ delay, warningErrors);
2326+
2327+ for (std::vector<std::string>::const_iterator itr = warningErrors.begin ();
2328+ itr != warningErrors.end (); ++itr)
2329+ {
2330+ sLog .outError (" ScheduledExit: ignoring %s" , itr->c_str ());
2331+ }
2332+
2333+ for (std::vector<uint32>::const_iterator itr = warningTimes.begin ();
2334+ itr != warningTimes.end (); ++itr)
2335+ {
2336+ if (*itr == 0 )
2337+ {
2338+ sLog .outError (" ScheduledExit: ignoring warning milestone 0" );
2339+ continue ;
2340+ }
2341+
2342+ std::ostringstream configKey;
2343+ configKey << " ScheduledExit.WarningText." << *itr;
2344+
2345+ ScheduledExitWarning warning;
2346+ warning.remainingSeconds = *itr;
2347+ warning.text = sConfig .GetStringDefault (configKey.str ().c_str (), " " );
2348+ if (warning.text .empty ())
2349+ {
2350+ warning.text = MaNGOS::BuildScheduledExitWarningText (mode, *itr);
2351+ }
2352+ warning.sent = false ;
2353+ m_scheduledExitWarnings.push_back (warning);
2354+ }
2355+
2356+ m_scheduledExit.enabled = true ;
2357+ m_scheduledExit.dayOfWeek = dayOfWeek;
2358+ m_scheduledExit.hour = hour;
2359+ m_scheduledExit.minute = minute;
2360+ m_scheduledExit.mode = mode;
2361+ m_scheduledExitDelay = delay;
2362+
2363+ if (MaNGOS::MarkScheduledExitHandledIfMatching (
2364+ m_scheduledExit, safe_localtime (time (NULL )), m_scheduledExitState))
2365+ {
2366+ sLog .outString (" ScheduledExit: startup minute matches configured schedule; "
2367+ " suppressing this minute to avoid restart loop" );
2368+ }
2369+
2370+ std::ostringstream milestones;
2371+ for (std::vector<ScheduledExitWarning>::const_iterator itr =
2372+ m_scheduledExitWarnings.begin ();
2373+ itr != m_scheduledExitWarnings.end (); ++itr)
2374+ {
2375+ if (itr != m_scheduledExitWarnings.begin ())
2376+ {
2377+ milestones << " ," ;
2378+ }
2379+ milestones << itr->remainingSeconds ;
2380+ }
2381+
2382+ sLog .outString (" ScheduledExit: enabled day=%u time=%02u:%02u mode=%s delay=%u warningTimes=%s" ,
2383+ m_scheduledExit.dayOfWeek , m_scheduledExit.hour , m_scheduledExit.minute ,
2384+ MaNGOS::ScheduledExitModeToString (m_scheduledExit.mode ), m_scheduledExitDelay,
2385+ milestones.str ().c_str ());
2386+ }
2387+
2388+ void World::CheckScheduledExit ()
2389+ {
2390+ if (!m_scheduledExit.enabled || m_stopEvent)
2391+ {
2392+ return ;
2393+ }
2394+
2395+ if (!MaNGOS::CheckAndMarkScheduledExit (
2396+ m_scheduledExit, safe_localtime (m_gameTime), m_scheduledExitState))
2397+ {
2398+ return ;
2399+ }
2400+
2401+ if (m_ShutdownTimer > 0 )
2402+ {
2403+ sLog .outString (" ScheduledExit: %s scheduled for day=%u time=%02u:%02u "
2404+ " skipped because shutdown/restart is already in progress" ,
2405+ MaNGOS::ScheduledExitModeToString (m_scheduledExit.mode ),
2406+ m_scheduledExit.dayOfWeek , m_scheduledExit.hour ,
2407+ m_scheduledExit.minute );
2408+ return ;
2409+ }
2410+
2411+ StartScheduledExit ();
2412+ }
2413+
2414+ void World::StartScheduledExit ()
2415+ {
2416+ uint32 mask = m_scheduledExit.mode == MaNGOS::SCHEDULED_EXIT_MODE_RESTART
2417+ ? SHUTDOWN_MASK_RESTART : SHUTDOWN_MASK_STOP;
2418+ uint8 exitCode = m_scheduledExit.mode == MaNGOS::SCHEDULED_EXIT_MODE_RESTART
2419+ ? RESTART_EXIT_CODE : SHUTDOWN_EXIT_CODE;
2420+
2421+ sLog .outString (" ScheduledExit: firing scheduled %s with delay %u seconds" ,
2422+ MaNGOS::ScheduledExitModeToString (m_scheduledExit.mode ), m_scheduledExitDelay);
2423+
2424+ ResetScheduledExitWarnings ();
2425+ m_scheduledExitCountdownActive = m_scheduledExitDelay > 0 ;
2426+ ShutdownServ (m_scheduledExitDelay, mask, exitCode);
2427+
2428+ if (m_scheduledExitCountdownActive)
2429+ {
2430+ SendScheduledExitWarnings ();
2431+ }
2432+ }
2433+
2434+ void World::ResetScheduledExitWarnings ()
2435+ {
2436+ for (std::vector<ScheduledExitWarning>::iterator itr =
2437+ m_scheduledExitWarnings.begin ();
2438+ itr != m_scheduledExitWarnings.end (); ++itr)
2439+ {
2440+ itr->sent = false ;
2441+ }
2442+ }
2443+
2444+ void World::SendScheduledExitWarnings ()
2445+ {
2446+ if (!m_scheduledExitCountdownActive || !m_ShutdownTimer)
2447+ {
2448+ return ;
2449+ }
2450+
2451+ for (std::vector<ScheduledExitWarning>::iterator itr =
2452+ m_scheduledExitWarnings.begin ();
2453+ itr != m_scheduledExitWarnings.end (); ++itr)
2454+ {
2455+ if (!itr->sent && m_ShutdownTimer <= itr->remainingSeconds )
2456+ {
2457+ SendScheduledExitWarning (*itr);
2458+ }
2459+ }
2460+ }
2461+
2462+ void World::SendScheduledExitWarning (ScheduledExitWarning& warning)
2463+ {
2464+ warning.sent = true ;
2465+ SendServerMessage (SERVER_MSG_CUSTOM, warning.text .c_str ());
2466+ }
2467+
2468+ // / Display a shutdown message to the user(s)
2469+ void World::ShutdownMsg (bool show /* = false*/ , Player* player /* = NULL*/ )
2470+ {
2471+ // not show messages for idle shutdown mode
2472+ if (m_ShutdownMask & SHUTDOWN_MASK_IDLE)
2473+ {
22602474 return ;
22612475 }
22622476
@@ -2278,7 +2492,7 @@ void World::ShutdownMsg(bool show /*= false*/, Player* player /*= NULL*/)
22782492}
22792493
22802494// / Cancel a planned server shutdown
2281- void World::ShutdownCancel ()
2495+ void World::ShutdownCancel ()
22822496{
22832497 // nothing cancel or too later
22842498 if (!m_ShutdownTimer || m_stopEvent)
@@ -2288,8 +2502,10 @@ void World::ShutdownCancel()
22882502
22892503 ServerMessageType msgid = (m_ShutdownMask & SHUTDOWN_MASK_RESTART) ? SERVER_MSG_RESTART_CANCELLED : SERVER_MSG_SHUTDOWN_CANCELLED;
22902504
2291- m_ShutdownMask = 0 ;
2292- m_ShutdownTimer = 0 ;
2505+ m_ShutdownMask = 0 ;
2506+ m_ShutdownTimer = 0 ;
2507+ m_scheduledExitCountdownActive = false ;
2508+ ResetScheduledExitWarnings ();
22932509 m_ExitCode = SHUTDOWN_EXIT_CODE; // to default value
22942510 SendServerMessage (msgid);
22952511
0 commit comments