2021-03-06 10:02:26 -05:00
|
|
|
-- LUALOCALS < ---------------------------------------------------------
|
|
|
|
local io, ipairs, math, minetest, pairs, string, tonumber
|
|
|
|
= io, ipairs, math, minetest, pairs, string, tonumber
|
|
|
|
local io_open, math_floor, string_format
|
|
|
|
= io.open, math.floor, string.format
|
|
|
|
-- LUALOCALS > ---------------------------------------------------------
|
|
|
|
|
|
|
|
local modname = minetest.get_current_modname()
|
|
|
|
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
-- Global configuration
|
|
|
|
|
|
|
|
local function conf(n)
|
|
|
|
return minetest.settings:get(modname .. "_" .. n)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Default restart grace time.
|
|
|
|
local grace = tonumber(conf("grace")) or 300
|
|
|
|
|
|
|
|
-- Amount of time before a restart that the countdown will be
|
|
|
|
-- announced/displayed.
|
|
|
|
local countdown = conf("countdown") or grace
|
|
|
|
|
|
|
|
-- Amount of time during which the countdown will flash.
|
|
|
|
local critical = conf("countdown") or 10
|
|
|
|
|
|
|
|
-- Primary color of the countdown HUD.
|
|
|
|
local hudcolor = conf("hudcolor") or 0xFFFF00
|
|
|
|
|
|
|
|
-- Flashing color of HUD during critical countdown.
|
|
|
|
local hudcolorflash = conf("hudcolor") or 0xFF0000
|
|
|
|
|
|
|
|
-- Don't restart the server too often; give players at least this
|
|
|
|
-- much time after a restart, if any are on.
|
|
|
|
local minuptime = tonumber(conf("minuptime")) or 7200
|
|
|
|
|
|
|
|
-- Always restart after the server has been up this long.
|
|
|
|
local maxuptime = tonumber(conf("maxuptime")) or 86400
|
|
|
|
|
|
|
|
-- Restart shutdown message.
|
|
|
|
local shtudownmsg = string_format("\n\n%s\n%s",
|
|
|
|
conf("shutdownmsg1") or "SERVER RESTARTING FOR UPDATES",
|
2021-03-06 18:56:23 -05:00
|
|
|
conf("shutdownmsg2") or "Please reconnect in about 10 seconds")
|
2021-03-06 10:02:26 -05:00
|
|
|
|
|
|
|
-- Message when players are kicked off for restart
|
|
|
|
local kickmsg = conf("kickmsg") or "*** Kicking players off for restart"
|
|
|
|
|
2021-03-11 08:31:29 -05:00
|
|
|
-- Message when restarting but there are no players online
|
|
|
|
local restartmsg = conf("restartmsg") or "*** Restarting server"
|
|
|
|
|
2021-03-06 10:02:26 -05:00
|
|
|
-- Message used to announce pending restart in chat.
|
|
|
|
local chatmsg = conf("chatmsg") or "*** Server restart in %s"
|
|
|
|
|
|
|
|
-- Message used to display pending restart in HUD.
|
|
|
|
local hudmsg = conf("hudmsg") or "RESTART IN %s"
|
|
|
|
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
-- Global time functions
|
|
|
|
|
|
|
|
-- Function to get current uptime of server.
|
|
|
|
local uptime
|
|
|
|
do
|
|
|
|
local starttime = minetest.get_us_time()
|
|
|
|
uptime = function()
|
|
|
|
return (minetest.get_us_time() - starttime) / 1000000
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- If restart requested, this is the uptime value at which the
|
|
|
|
-- restart should happen, nil if none pending.
|
|
|
|
local req
|
|
|
|
|
|
|
|
-- Get number of seconds until restart, if any.
|
|
|
|
local function remain()
|
|
|
|
return req and req - uptime()
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Get formatted restart time, if any
|
|
|
|
local function remaintext()
|
|
|
|
if not req then return end
|
|
|
|
local cdown = math_floor(remain())
|
|
|
|
local min = cdown / 60
|
|
|
|
local sec = cdown % 60
|
|
|
|
if min < 60 then return string_format("%d:%02d", min, sec) end
|
|
|
|
local hr = min / 60
|
|
|
|
min = min % 60
|
|
|
|
return string_format("%d:%02d:%02d", hr, min, sec)
|
|
|
|
end
|
|
|
|
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
-- Trigger conditions
|
|
|
|
|
|
|
|
do
|
|
|
|
local function restarttrigger(reason, time)
|
|
|
|
minetest.log("RESTART REQUEST (" .. reason .. ")")
|
|
|
|
req = uptime() + (time or grace)
|
|
|
|
if time then return end
|
|
|
|
if req < minuptime then req = minuptime
|
|
|
|
elseif req > maxuptime then req = maxuptime end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Server admins can manually request a restart, including with a
|
|
|
|
-- custom grace time. Restarts cannot be canceled entirely (nor
|
|
|
|
-- should they be, probably), but can be delayed indefinitely.
|
|
|
|
minetest.register_chatcommand("trigger_restart", {
|
|
|
|
description = "Signal a restart request manually, or reset countdown",
|
|
|
|
privs = {server = true},
|
|
|
|
func = function(name, param)
|
|
|
|
restarttrigger("manual by " .. name, tonumber(param))
|
|
|
|
end
|
|
|
|
})
|
|
|
|
|
|
|
|
-- Automatically detect a restart condition.
|
|
|
|
local function restartcheck()
|
|
|
|
if req then return end
|
|
|
|
|
|
|
|
-- Trigger restart if the server has reached its maximum uptime.
|
2022-01-26 22:01:18 -05:00
|
|
|
if uptime() >= maxuptime - grace then
|
2021-03-06 10:02:26 -05:00
|
|
|
return restarttrigger("max uptime")
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Trigger a restart if a file exists in the world dir to allow
|
|
|
|
-- an external script to request it.
|
|
|
|
local f = io_open(minetest.get_worldpath() .. "/restart")
|
|
|
|
if f then
|
|
|
|
f:close()
|
|
|
|
return restarttrigger("file trigger")
|
|
|
|
end
|
|
|
|
|
|
|
|
return minetest.after(2, restartcheck)
|
|
|
|
end
|
|
|
|
restartcheck()
|
|
|
|
end
|
|
|
|
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
-- Announce pending restarts in chat streams
|
|
|
|
|
2021-03-11 08:31:29 -05:00
|
|
|
local announced
|
2021-03-06 10:02:26 -05:00
|
|
|
do
|
|
|
|
local lastsent
|
|
|
|
minetest.register_globalstep(function()
|
|
|
|
-- Skip if no countdown yet.
|
|
|
|
if not req then return end
|
|
|
|
|
|
|
|
-- Never announce if no players online.
|
2022-01-06 07:14:17 -05:00
|
|
|
if #minetest.get_connected_players() < 1 then
|
|
|
|
lastsent = nil
|
|
|
|
return
|
|
|
|
end
|
2021-03-06 10:02:26 -05:00
|
|
|
|
|
|
|
-- Announce if the remaining time has changed, or
|
|
|
|
-- we've crossed to/from the active countdown phase.
|
2022-01-06 07:14:17 -05:00
|
|
|
if (announced ~= req) or (not lastsent)
|
|
|
|
or ((lastsent > countdown) ~= (remain() > countdown)) then
|
2021-03-06 10:02:26 -05:00
|
|
|
announced = req
|
|
|
|
lastsent = remain()
|
|
|
|
minetest.chat_send_all(string_format(chatmsg, remaintext()))
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
2021-03-11 08:31:29 -05:00
|
|
|
------------------------------------------------------------------------
|
|
|
|
-- Handle actual restart event
|
|
|
|
|
|
|
|
do
|
|
|
|
local shuttingdown
|
|
|
|
minetest.register_globalstep(function()
|
|
|
|
if shuttingdown or not req then return end
|
|
|
|
local pcount = #minetest.get_connected_players()
|
|
|
|
if pcount > 0 and remain() > 0 then return end
|
|
|
|
shuttingdown = true
|
|
|
|
if #minetest.get_connected_players() > 0 then
|
|
|
|
minetest.chat_send_all(kickmsg)
|
|
|
|
elseif announced then
|
|
|
|
minetest.chat_send_all(restartmsg)
|
|
|
|
end
|
|
|
|
return minetest.request_shutdown(shtudownmsg, true)
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
2021-03-06 10:02:26 -05:00
|
|
|
------------------------------------------------------------------------
|
|
|
|
-- Announce pending restarts via HUD
|
|
|
|
|
|
|
|
do
|
|
|
|
local huds = {}
|
|
|
|
minetest.register_on_leaveplayer(function(player)
|
|
|
|
huds[player:get_player_name()] = nil
|
|
|
|
end)
|
|
|
|
minetest.register_globalstep(function()
|
|
|
|
if #minetest.get_connected_players() < 1 then return end
|
|
|
|
|
|
|
|
if (not req) or (remain() > countdown) then
|
|
|
|
for pname, hud in pairs(huds) do
|
|
|
|
local player = minetest.get_player_by_name(pname)
|
|
|
|
if player then player:hud_remove(hud.id) end
|
|
|
|
huds[pname] = nil
|
|
|
|
end
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
local number = (remain() < critical and (remain() - math_floor(remain()) < 0.5))
|
|
|
|
and hudcolorflash or hudcolor
|
|
|
|
local text = string_format(hudmsg, remaintext())
|
|
|
|
|
|
|
|
for _, player in ipairs(minetest.get_connected_players()) do
|
|
|
|
local pname = player:get_player_name()
|
|
|
|
local hud = huds[pname]
|
|
|
|
if hud then
|
|
|
|
if hud.number ~= number then
|
|
|
|
player:hud_change(hud.id, "number", number)
|
|
|
|
hud.number = number
|
|
|
|
end
|
|
|
|
if hud.text ~= text then
|
|
|
|
player:hud_change(hud.id, "text", text)
|
|
|
|
hud.text = text
|
|
|
|
end
|
|
|
|
else
|
|
|
|
huds[pname] = {
|
|
|
|
number = number,
|
|
|
|
text = text,
|
|
|
|
id = player:hud_add({
|
|
|
|
label = "restart_warn",
|
|
|
|
hud_elem_type = "text",
|
|
|
|
position = {x = 0.5, y = 0.8},
|
|
|
|
text = text,
|
|
|
|
number = number,
|
|
|
|
alignment = {x = 0, y = 1},
|
|
|
|
offset = {x = 0, y = 0}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
end
|