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,28 @@ 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 = MaNGOS::GetScheduledExitCountdownActions (m_scheduledExitCountdownActive);
2210+ if (actions.sendShutdownTimer )
2211+ {
2212+ ShutdownMsg ();
2213+ }
2214+
2215+ if (actions.sendConfiguredWarnings )
2216+ {
2217+ SendScheduledExitWarnings ();
2218+ }
2219+ }
2220+ }
2221+
2222+ CheckScheduledExit ();
2223+ }
22082224
22092225// / Shutdown the server
2210- void World::ShutdownServ (uint32 time, uint32 options, uint8 exitcode)
2226+ void World::ShutdownServ (uint32 time, uint32 options, uint8 exitcode)
22112227{
22122228 // ignore if server shutdown at next tick
22132229 if (m_stopEvent)
@@ -2235,8 +2251,8 @@ void World::ShutdownServ(uint32 time, uint32 options, uint8 exitcode)
22352251 else
22362252 {
22372253 m_ShutdownTimer = time;
2238- ShutdownMsg (true );
2239- }
2254+ ShutdownMsg (true );
2255+ }
22402256
22412257#ifdef ENABLE_PLAYERBOTS
22422258 sRandomPlayerbotMgr .LogoutAllBots ();
@@ -2249,14 +2265,189 @@ void World::ShutdownServ(uint32 time, uint32 options, uint8 exitcode)
22492265 e->OnShutdownInitiate (ShutdownExitCode (exitcode), ShutdownMask (options));
22502266 }
22512267#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- {
2268+ }
2269+
2270+ void World::LoadScheduledExitConfig ()
2271+ {
2272+ m_scheduledExit = MaNGOS::ScheduledExitSchedule ();
2273+ m_scheduledExitDelay = 0 ;
2274+ m_scheduledExitWarnings.clear ();
2275+ m_scheduledExitCountdownActive = false ;
2276+
2277+ if (!sConfig .GetBoolDefault (" ScheduledExit.Enable" , false ))
2278+ {
2279+ sLog .outString (" ScheduledExit: disabled" );
2280+ return ;
2281+ }
2282+
2283+ std::string dayText = sConfig .GetStringDefault (" ScheduledExit.DayOfWeek" , " 3" );
2284+ uint32 dayOfWeek = 0 ;
2285+ if (!MaNGOS::ParseScheduledExitUInt32 (dayText, dayOfWeek) || dayOfWeek > 6 )
2286+ {
2287+ sLog .outError (" ScheduledExit: invalid ScheduledExit.DayOfWeek '%s'; disabling scheduled exit" , dayText.c_str ());
2288+ return ;
2289+ }
2290+
2291+ std::string timeText = sConfig .GetStringDefault (" ScheduledExit.Time" , " 05:00" );
2292+ uint32 hour = 0 ;
2293+ uint32 minute = 0 ;
2294+ if (!MaNGOS::ParseScheduledExitTime (timeText, hour, minute))
2295+ {
2296+ sLog .outError (" ScheduledExit: invalid ScheduledExit.Time '%s'; disabling scheduled exit" , timeText.c_str ());
2297+ return ;
2298+ }
2299+
2300+ std::string modeText = sConfig .GetStringDefault (" ScheduledExit.Mode" , " restart" );
2301+ MaNGOS::ScheduledExitMode mode = MaNGOS::SCHEDULED_EXIT_MODE_RESTART;
2302+ if (!MaNGOS::ParseScheduledExitMode (modeText, mode))
2303+ {
2304+ sLog .outError (" ScheduledExit: invalid ScheduledExit.Mode '%s'; disabling scheduled exit" , modeText.c_str ());
2305+ return ;
2306+ }
2307+
2308+ std::string delayText = sConfig .GetStringDefault (" ScheduledExit.Delay" , " 900" );
2309+ uint32 delay = 0 ;
2310+ if (!MaNGOS::ParseScheduledExitUInt32 (delayText, delay))
2311+ {
2312+ sLog .outError (" ScheduledExit: invalid ScheduledExit.Delay '%s'; disabling scheduled exit" , delayText.c_str ());
2313+ return ;
2314+ }
2315+
2316+ std::vector<std::string> warningErrors;
2317+ std::vector<uint32> warningTimes = MaNGOS::ParseScheduledExitWarningTimes (
2318+ sConfig .GetStringDefault (" ScheduledExit.WarningTimes" , " 900,600,300,60" ), delay, warningErrors);
2319+
2320+ for (std::vector<std::string>::const_iterator itr = warningErrors.begin (); itr != warningErrors.end (); ++itr)
2321+ {
2322+ sLog .outError (" ScheduledExit: ignoring %s" , itr->c_str ());
2323+ }
2324+
2325+ for (std::vector<uint32>::const_iterator itr = warningTimes.begin (); itr != warningTimes.end (); ++itr)
2326+ {
2327+ if (*itr == 0 )
2328+ {
2329+ sLog .outError (" ScheduledExit: ignoring warning milestone 0" );
2330+ continue ;
2331+ }
2332+
2333+ std::ostringstream configKey;
2334+ configKey << " ScheduledExit.WarningText." << *itr;
2335+
2336+ ScheduledExitWarning warning;
2337+ warning.remainingSeconds = *itr;
2338+ warning.text = sConfig .GetStringDefault (configKey.str ().c_str (), " " );
2339+ if (warning.text .empty ())
2340+ {
2341+ warning.text = MaNGOS::BuildScheduledExitWarningText (mode, *itr);
2342+ }
2343+ warning.sent = false ;
2344+ m_scheduledExitWarnings.push_back (warning);
2345+ }
2346+
2347+ m_scheduledExit.enabled = true ;
2348+ m_scheduledExit.dayOfWeek = dayOfWeek;
2349+ m_scheduledExit.hour = hour;
2350+ m_scheduledExit.minute = minute;
2351+ m_scheduledExit.mode = mode;
2352+ m_scheduledExitDelay = delay;
2353+
2354+ if (MaNGOS::MarkScheduledExitHandledIfMatching (m_scheduledExit, safe_localtime (time (NULL )), m_scheduledExitState))
2355+ {
2356+ sLog .outString (" ScheduledExit: startup minute matches configured schedule; suppressing this minute to avoid restart loop" );
2357+ }
2358+
2359+ std::ostringstream milestones;
2360+ for (std::vector<ScheduledExitWarning>::const_iterator itr = m_scheduledExitWarnings.begin (); itr != m_scheduledExitWarnings.end (); ++itr)
2361+ {
2362+ if (itr != m_scheduledExitWarnings.begin ())
2363+ {
2364+ milestones << " ," ;
2365+ }
2366+ milestones << itr->remainingSeconds ;
2367+ }
2368+
2369+ sLog .outString (" ScheduledExit: enabled day=%u time=%02u:%02u mode=%s delay=%u warningTimes=%s" ,
2370+ m_scheduledExit.dayOfWeek , m_scheduledExit.hour , m_scheduledExit.minute ,
2371+ MaNGOS::ScheduledExitModeToString (m_scheduledExit.mode ), m_scheduledExitDelay,
2372+ milestones.str ().c_str ());
2373+ }
2374+
2375+ void World::CheckScheduledExit ()
2376+ {
2377+ if (!m_scheduledExit.enabled || m_stopEvent)
2378+ {
2379+ return ;
2380+ }
2381+
2382+ if (!MaNGOS::CheckAndMarkScheduledExit (m_scheduledExit, safe_localtime (m_gameTime), m_scheduledExitState))
2383+ {
2384+ return ;
2385+ }
2386+
2387+ if (m_ShutdownTimer > 0 )
2388+ {
2389+ sLog .outString (" ScheduledExit: %s scheduled for day=%u time=%02u:%02u skipped because shutdown/restart is already in progress" ,
2390+ MaNGOS::ScheduledExitModeToString (m_scheduledExit.mode ), m_scheduledExit.dayOfWeek , m_scheduledExit.hour , m_scheduledExit.minute );
2391+ return ;
2392+ }
2393+
2394+ StartScheduledExit ();
2395+ }
2396+
2397+ void World::StartScheduledExit ()
2398+ {
2399+ uint32 mask = m_scheduledExit.mode == MaNGOS::SCHEDULED_EXIT_MODE_RESTART ? SHUTDOWN_MASK_RESTART : SHUTDOWN_MASK_STOP;
2400+ uint8 exitCode = m_scheduledExit.mode == MaNGOS::SCHEDULED_EXIT_MODE_RESTART ? RESTART_EXIT_CODE : SHUTDOWN_EXIT_CODE;
2401+
2402+ sLog .outString (" ScheduledExit: firing scheduled %s with delay %u seconds" ,
2403+ MaNGOS::ScheduledExitModeToString (m_scheduledExit.mode ), m_scheduledExitDelay);
2404+
2405+ ResetScheduledExitWarnings ();
2406+ m_scheduledExitCountdownActive = m_scheduledExitDelay > 0 ;
2407+ ShutdownServ (m_scheduledExitDelay, mask, exitCode);
2408+
2409+ if (m_scheduledExitCountdownActive)
2410+ {
2411+ SendScheduledExitWarnings ();
2412+ }
2413+ }
2414+
2415+ void World::ResetScheduledExitWarnings ()
2416+ {
2417+ for (std::vector<ScheduledExitWarning>::iterator itr = m_scheduledExitWarnings.begin (); itr != m_scheduledExitWarnings.end (); ++itr)
2418+ {
2419+ itr->sent = false ;
2420+ }
2421+ }
2422+
2423+ void World::SendScheduledExitWarnings ()
2424+ {
2425+ if (!m_scheduledExitCountdownActive || !m_ShutdownTimer)
2426+ {
2427+ return ;
2428+ }
2429+
2430+ for (std::vector<ScheduledExitWarning>::iterator itr = m_scheduledExitWarnings.begin (); itr != m_scheduledExitWarnings.end (); ++itr)
2431+ {
2432+ if (!itr->sent && m_ShutdownTimer <= itr->remainingSeconds )
2433+ {
2434+ SendScheduledExitWarning (*itr);
2435+ }
2436+ }
2437+ }
2438+
2439+ void World::SendScheduledExitWarning (ScheduledExitWarning& warning)
2440+ {
2441+ warning.sent = true ;
2442+ SendServerMessage (SERVER_MSG_CUSTOM, warning.text .c_str ());
2443+ }
2444+
2445+ // / Display a shutdown message to the user(s)
2446+ void World::ShutdownMsg (bool show /* = false*/ , Player* player /* = NULL*/ )
2447+ {
2448+ // not show messages for idle shutdown mode
2449+ if (m_ShutdownMask & SHUTDOWN_MASK_IDLE)
2450+ {
22602451 return ;
22612452 }
22622453
@@ -2278,7 +2469,7 @@ void World::ShutdownMsg(bool show /*= false*/, Player* player /*= NULL*/)
22782469}
22792470
22802471// / Cancel a planned server shutdown
2281- void World::ShutdownCancel ()
2472+ void World::ShutdownCancel ()
22822473{
22832474 // nothing cancel or too later
22842475 if (!m_ShutdownTimer || m_stopEvent)
@@ -2288,8 +2479,10 @@ void World::ShutdownCancel()
22882479
22892480 ServerMessageType msgid = (m_ShutdownMask & SHUTDOWN_MASK_RESTART) ? SERVER_MSG_RESTART_CANCELLED : SERVER_MSG_SHUTDOWN_CANCELLED;
22902481
2291- m_ShutdownMask = 0 ;
2292- m_ShutdownTimer = 0 ;
2482+ m_ShutdownMask = 0 ;
2483+ m_ShutdownTimer = 0 ;
2484+ m_scheduledExitCountdownActive = false ;
2485+ ResetScheduledExitWarnings ();
22932486 m_ExitCode = SHUTDOWN_EXIT_CODE; // to default value
22942487 SendServerMessage (msgid);
22952488
0 commit comments