Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,4 @@ local.properties
.loadpath
.project
.cproject
/.vs
26 changes: 19 additions & 7 deletions conf/ServerAutoShutdown.conf.dist
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,26 @@ ServerAutoShutdown.Enabled = 0

ServerAutoShutdown.EveryDays = 1

#
# ServerAutoShutdown.Weekday
# Description: If set (0-6, where 0=Sunday, 1=Monday, ... 6=Saturday), the server will restart on this weekday at the configured time.
# Overrides ServerAutoShutdown.EveryDays if set to a valid value.
# Default: -1 (disabled)
#
# Description: If set, the server will restart on the specified weekdays at the configured time. Overrides ServerAutoShutdown.EveryDays if set to a valid value.
# Weekday bitmask where each day corresponds to a bit position:
# Sunday = 1 = 0b0000001 (2^0)
# Monday = 2 = 0b0000010 (2^1)
# Tuesday = 4 = 0b0000100 (2^2)
# Wednesday = 8 = 0b0001000 (2^3)
# Thursday = 16 = 0b0010000 (2^4)
# Friday = 32 = 0b0100000 (2^5)
# Saturday = 64 = 0b1000000 (2^6)
# To select multiple days, add the corresponding decimal values.
# Examples:
# Monday: 2^1 = 2 (binary: 0b0000010), just one day a week
# Tuesday and Wednesday: 4 + 8 = 12 (binary: 0b0000100 + 0b0001000 = 0b0001100), both the 2 days a week
# Sunday, Friday, Saturday: 1 + 32 + 64 = 97 (binary: 0b0000001 + 0b0100000 + 0b1000000 = 0b1100001), the selected 3 days a week
# All days: 1+2+4+8+16+32+64 = 127 (binary: 0b0000001 + 0b0000010 + 0b0000100 + 0b0001000 + 0b0010000 + 0b0100000 + 0b1000000 = 0b1111111)
#
# Default: 0 - disabled, use ServerAutoShutdown.EveryDays
# 1-127 - Enable masked weekdays

ServerAutoShutdown.Weekday = -1
ServerAutoShutdown.WeekdayMask = 0

#
# ServerAutoShutdown.Time
Expand Down
123 changes: 39 additions & 84 deletions src/ServerAutoShutdown.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,11 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include "Common.h"
#include "Config.h"
#include "Duration.h"
#include "GameEventMgr.h"
#include "Language.h"
#include "Log.h"
#include "ObjectMgr.h"
#include "ServerAutoShutdown.h"
#include "StringConvert.h"
#include "StringFormat.h"
#include "TaskScheduler.h"
#include "Tokenize.h"
#include "Util.h"
#include "World.h"
#include "WorldSessionMgr.h"

Expand All @@ -36,43 +28,33 @@ namespace
// Scheduler - for update
TaskScheduler scheduler;

time_t GetNextResetTime(time_t time, uint32 restartDays, uint8 restartHour, uint8 restartMinute, uint8 restartSecond)
time_t GetNextShutdownTime(time_t timestamp, uint8 weekdayMask, uint32 restartDays, uint8 restartHour, uint8 restartMinute, uint8 restartSecond)
{
tm timeLocal = Acore::Time::TimeBreakdown(time);
tm timeLocal = Acore::Time::TimeBreakdown(timestamp);
timeLocal.tm_hour = restartHour;
timeLocal.tm_min = restartMinute;
timeLocal.tm_sec = restartSecond;

time_t midnightLocal = mktime(&timeLocal);

if (restartDays > 1 || midnightLocal <= time)
midnightLocal += DAY * restartDays;

return midnightLocal;
}

// Returns the next time_t for the given weekday (0=Sunday, 1=Monday, ..., 6=Saturday) at the given hour/min/sec
time_t GetNextWeekdayTime(time_t now, int weekday, uint8 restartHour, uint8 restartMinute, uint8 restartSecond)
{
tm timeLocal = Acore::Time::TimeBreakdown(now);
int currentWeekday = timeLocal.tm_wday;
int daysUntil = (weekday - currentWeekday + 7) % 7;
if (daysUntil == 0)
if (weekdayMask != 0)
{
// If today, check if the time has already passed
if (timeLocal.tm_hour > restartHour ||
(timeLocal.tm_hour == restartHour && timeLocal.tm_min > restartMinute) ||
(timeLocal.tm_hour == restartHour && timeLocal.tm_min == restartMinute && timeLocal.tm_sec >= restartSecond))
for (int weekdayIndex = 0; weekdayIndex < 7; ++weekdayIndex) //Sunday=0 Monday=1 ... Saturday=6
{
daysUntil = 7;
if (weekdayMask & (1 << ((timeLocal.tm_wday + weekdayIndex) % 7))) // Check if the target day of the week (current day + weekdayIndex) is match in the weekday mask
{
timeLocal.tm_mday += weekdayIndex; //max hit times is 2 ,if 2, first time weekdayIndex must 0, so mktime will both right wherenever hit 1 or 2 times
time_t ShutdownMaskTime = mktime(&timeLocal);
if (ShutdownMaskTime - 10 > timestamp)
return ShutdownMaskTime;
}
}
timeLocal.tm_mday += 7; // if no match, set the day of next week
return mktime(&timeLocal);
}
timeLocal.tm_mday += daysUntil;
timeLocal.tm_hour = restartHour;
timeLocal.tm_min = restartMinute;
timeLocal.tm_sec = restartSecond;
time_t result = mktime(&timeLocal);
return result;

time_t ShutdownDaysTime = mktime(&timeLocal);
if (restartDays > 1 || ShutdownDaysTime - 10 <= timestamp)
ShutdownDaysTime += DAY * restartDays;
return ShutdownDaysTime;
}
}

Expand All @@ -89,9 +71,12 @@ void ServerAutoShutdown::Init()
if (!_isEnableModule)
return;

uint8 weekdayMask = sConfigMgr->GetOption<uint8>("ServerAutoShutdown.WeekdayMask", 0);
uint32 restartDays = sConfigMgr->GetOption<uint32>("ServerAutoShutdown.EveryDays", 1);
std::string configTime = sConfigMgr->GetOption<std::string>("ServerAutoShutdown.Time", "04:00:00");
auto const& tokens = Acore::Tokenize(configTime, ':', false);
uint32 preAnnounceSeconds = sConfigMgr->GetOption<uint32>("ServerAutoShutdown.PreAnnounce.Seconds", HOUR);

auto const& tokens = Acore::Tokenize(configTime, ':', false);
if (tokens.size() != 3)
{
LOG_ERROR("module", "> ServerAutoShutdown: Incorrect time in config option 'ServerAutoShutdown.Time' - '{}'", configTime);
Expand All @@ -105,7 +90,6 @@ void ServerAutoShutdown::Init()
for (auto const& itr : index)
if (!Acore::StringTo<uint8>(tokens.at(itr)))
return false;

return true;
};

Expand All @@ -116,65 +100,45 @@ void ServerAutoShutdown::Init()
return;
}

int weekday = sConfigMgr->GetOption<int>("ServerAutoShutdown.Weekday", -1);
uint32 restartDays = sConfigMgr->GetOption<uint32>("ServerAutoShutdown.EveryDays", 1);
uint8 restartHour = *Acore::StringTo<uint8>(tokens.at(0));
uint8 restartMinute = *Acore::StringTo<uint8>(tokens.at(1));
uint8 restartSecond = *Acore::StringTo<uint8>(tokens.at(2));

auto nowTime = time(nullptr);
uint64 nextResetTime = 0;

if (weekday >= 0 && weekday <= 6)
if (weekdayMask > 127)
{
nextResetTime = GetNextWeekdayTime(nowTime, weekday, restartHour, restartMinute, restartSecond);
}
else
{
if (restartDays < 1 || restartDays > 365)
{
LOG_ERROR("module", "> ServerAutoShutdown: Incorrect day in config option 'ServerAutoShutdown.EveryDays' - '{}'", restartDays);
_isEnableModule = false;
return;
}
nextResetTime = GetNextResetTime(nowTime, restartDays, restartHour, restartMinute, restartSecond);
LOG_ERROR("module", "> ServerAutoShutdown: Incorrect weekdayMask in config option 'ServerAutoShutdown.weekdayMask' - '{}'", weekdayMask);
_isEnableModule = false;
return;
}

if (restartDays < 1 || restartDays > 365)
{
LOG_ERROR("module", "> ServerAutoShutdown: Incorrect day in config option 'ServerAutoShutdown.EveryDays' - '{}'", restartDays);
_isEnableModule = false;
return;
}
else if (restartHour > 23)

if (restartHour < 0 or restartHour > 23 or restartMinute < 0 or restartMinute > 59 or restartSecond < 0 or restartSecond > 59)
{
LOG_ERROR("module", "> ServerAutoShutdown: Incorrect hour in config option 'ServerAutoShutdown.Time' - '{}'", configTime);
_isEnableModule = false;
return;
}
else if (restartMinute >= 60)
{
LOG_ERROR("module", "> ServerAutoShutdown: Incorrect minute in config option 'ServerAutoShutdown.Time' - '{}'", configTime);
_isEnableModule = false;
}
else if (restartSecond >= 60)

if (preAnnounceSeconds > DAY)
{
LOG_ERROR("module", "> ServerAutoShutdown: Incorrect second in config option 'ServerAutoShutdown.Time' - '{}'", configTime);
_isEnableModule = false;
LOG_ERROR("module", "> ServerAutoShutdown: Ahah, how could this happen? Time to preannouce has been set to more than 1 day? ({}). Change to 1 hour (3600)", preAnnounceSeconds);
preAnnounceSeconds = HOUR;
}

auto nowTime = time(nullptr);
uint64 nextResetTime = GetNextShutdownTime(nowTime, weekdayMask, restartDays, restartHour, restartMinute, restartSecond);
uint32 diffToShutdown = nextResetTime - static_cast<uint32>(nowTime);

if (diffToShutdown < 10)
{
LOG_WARN("module", "> ServerAutoShutdown: Next time to shutdown < 10 seconds, Set next period");
if (weekday >= 0 && weekday <= 6)
nextResetTime += WEEK;
else
nextResetTime += DAY * restartDays;
diffToShutdown = nextResetTime - static_cast<uint32>(nowTime);
}

LOG_INFO("module", " ");
LOG_INFO("module","> ServerAutoShutdown: System loading");
LOG_INFO("module", "> ServerAutoShutdown: System loading");

// Cancel all task for support reload config
scheduler.CancelAll();
Expand All @@ -184,13 +148,6 @@ void ServerAutoShutdown::Init()
LOG_INFO("module", "> ServerAutoShutdown: Remaining time to shutdown - {}", Acore::Time::ToTimeString<Seconds>(diffToShutdown));
LOG_INFO("module", " ");

uint32 preAnnounceSeconds = sConfigMgr->GetOption<uint32>("ServerAutoShutdown.PreAnnounce.Seconds", HOUR);
if (preAnnounceSeconds > DAY)
{
LOG_ERROR("module", "> ServerAutoShutdown: Ahah, how could this happen? Time to preannouce has been set to more than 1 day? ({}). Change to 1 hour (3600)", preAnnounceSeconds);
preAnnounceSeconds = HOUR;
}

uint32 timeToPreAnnounce = static_cast<uint32>(nextResetTime) - preAnnounceSeconds;
uint32 diffToPreAnnounce = timeToPreAnnounce - static_cast<uint32>(nowTime);

Expand All @@ -202,8 +159,8 @@ void ServerAutoShutdown::Init()
preAnnounceSeconds = diffToShutdown;
}

LOG_INFO("module", "> ServerAutoShutdown: Next time to pre annouce - {}", Acore::Time::TimeToHumanReadable(Seconds(timeToPreAnnounce)));
LOG_INFO("module", "> ServerAutoShutdown: Remaining time to pre annouce - {}", Acore::Time::ToTimeString<Seconds>(diffToPreAnnounce));
LOG_INFO("module", "> ServerAutoShutdown: Next time to pre announce - {}", Acore::Time::TimeToHumanReadable(Seconds(timeToPreAnnounce)));
LOG_INFO("module", "> ServerAutoShutdown: Remaining time to pre announce - {}", Acore::Time::ToTimeString<Seconds>(diffToPreAnnounce));
LOG_INFO("module", " ");

StartPersistentGameEvents();
Expand All @@ -213,9 +170,7 @@ void ServerAutoShutdown::Init()
{
std::string preAnnounceMessageFormat = sConfigMgr->GetOption<std::string>("ServerAutoShutdown.PreAnnounce.Message", "[SERVER]: Automated (quick) server restart in {}");
std::string message = Acore::StringFormat(preAnnounceMessageFormat, Acore::Time::ToTimeString<Seconds>(preAnnounceSeconds, TimeOutput::Seconds, TimeFormat::FullText));

LOG_INFO("module", "> {}", message);

sWorldSessionMgr->SendServerMessage(SERVER_MSG_STRING, message);
sWorld->ShutdownServ(preAnnounceSeconds, SHUTDOWN_MASK_RESTART, SHUTDOWN_EXIT_CODE);
});
Expand Down