2019-03-27 06:23:19 -04:00
|
|
|
-- LUALOCALS < ---------------------------------------------------------
|
2024-08-24 12:18:52 -04:00
|
|
|
local io, ipairs, math, minetest, string, tonumber
|
|
|
|
= io, ipairs, math, minetest, string, tonumber
|
|
|
|
local io_close, io_open, math_random, string_format, string_gsub,
|
|
|
|
string_match, string_sub
|
|
|
|
= io.close, io.open, math.random, string.format, string.gsub,
|
|
|
|
string.match, string.sub
|
2019-03-27 06:23:19 -04:00
|
|
|
-- LUALOCALS > ---------------------------------------------------------
|
|
|
|
|
2016-05-03 22:33:09 -04:00
|
|
|
local modname = minetest.get_current_modname()
|
2020-01-25 08:06:36 -05:00
|
|
|
local modstore = minetest.get_mod_storage()
|
2016-05-03 22:33:09 -04:00
|
|
|
|
2020-09-01 20:04:18 -04:00
|
|
|
local phashkey = minetest.settings:get("szutil_motd_hashkey") or ""
|
|
|
|
local function phash(pname)
|
|
|
|
if #phashkey < 1 then return "0000" end
|
|
|
|
return string_sub(minetest.sha1(phashkey .. pname .. phashkey .. pname), 1, 4)
|
|
|
|
end
|
2020-07-05 09:48:19 -04:00
|
|
|
|
2022-04-21 23:40:29 -04:00
|
|
|
-- Permission to ignore all involuntary MOTD interaction.
|
|
|
|
-- The command will still work, but it skips the warnings and
|
|
|
|
-- mandatory display.
|
|
|
|
local exempt = modname .. "_exempt"
|
|
|
|
minetest.register_privilege(exempt, {
|
|
|
|
description = "Player will not be alerted about MOTD",
|
|
|
|
give_to_admin = false,
|
|
|
|
give_to_singleplayer = false
|
|
|
|
})
|
|
|
|
|
2020-07-05 09:48:19 -04:00
|
|
|
-- Function to read current MOTD
|
|
|
|
local readmotd
|
|
|
|
do
|
|
|
|
local motdpath = minetest.get_worldpath() .. "/" .. modname .. ".txt"
|
|
|
|
readmotd = function()
|
|
|
|
local f = io_open(motdpath, "rb")
|
|
|
|
if not f then return end
|
|
|
|
local motd = f:read("*all")
|
|
|
|
io_close(f)
|
|
|
|
if not string_match(motd, "%S") then return end
|
|
|
|
return motd
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Periodically scan for MOTD changes, and notify all online
|
|
|
|
-- players if there are any.
|
|
|
|
do
|
|
|
|
local motdinterval = tonumber(minetest.settings:get(modname .. "_interval"))
|
|
|
|
if motdinterval then
|
2023-06-12 20:40:14 -04:00
|
|
|
minetest.log("info", "polling motd for changes every " .. motdinterval .. "s")
|
2020-07-05 09:48:19 -04:00
|
|
|
local alertmotd = readmotd()
|
|
|
|
local function alertcheck()
|
|
|
|
minetest.after(motdinterval, alertcheck)
|
|
|
|
local motd = readmotd()
|
|
|
|
if motd == alertmotd then return end
|
2022-04-21 23:40:29 -04:00
|
|
|
for _, p in ipairs(minetest.get_connected_players()) do
|
|
|
|
if not minetest.check_player_privs(p, exempt) then
|
|
|
|
minetest.chat_send_player(p:get_player_name(),
|
|
|
|
"Updated MOTD. Please use /motd to review.")
|
|
|
|
end
|
|
|
|
end
|
2020-07-05 09:48:19 -04:00
|
|
|
alertmotd = motd
|
|
|
|
end
|
|
|
|
minetest.after(motdinterval, alertcheck)
|
|
|
|
end
|
|
|
|
end
|
2016-05-03 22:33:09 -04:00
|
|
|
|
|
|
|
-- Function to send the actual MOTD content to the player, in either
|
2019-03-27 06:23:19 -04:00
|
|
|
-- automatic mode (on login) or "forced" mode (on player request).
|
2016-05-03 22:33:09 -04:00
|
|
|
local function sendmotd(name, force)
|
2022-04-21 23:40:29 -04:00
|
|
|
-- Never auto-display to exempt players.
|
|
|
|
if (not force) and minetest.check_player_privs(name, exempt)
|
|
|
|
then return end
|
|
|
|
|
2016-05-03 22:53:57 -04:00
|
|
|
-- Load the MOTD fresh on each request, so changes can be
|
|
|
|
-- made while the server is running, and take effect immediately.
|
2020-07-05 09:48:19 -04:00
|
|
|
local motd = readmotd()
|
|
|
|
if not motd then return end
|
2016-05-03 22:33:09 -04:00
|
|
|
|
|
|
|
-- Compute a hash of the MOTD content, and figure out
|
|
|
|
-- if a player has already seen this version.
|
2020-01-25 08:06:36 -05:00
|
|
|
local hash = minetest.sha1(motd)
|
|
|
|
local seen = (modstore:get_string(name) or "") == hash
|
2016-05-03 22:33:09 -04:00
|
|
|
|
|
|
|
-- If player has seen this version and did not specifically
|
|
|
|
-- request redisplay, just send a chat message reminding them that
|
|
|
|
-- it's available if they want.
|
|
|
|
if seen and not force then
|
|
|
|
minetest.chat_send_player(name,
|
2019-09-01 12:52:22 -04:00
|
|
|
"No MOTD changes since your last view. Use /motd command to "
|
2016-05-03 22:33:09 -04:00
|
|
|
.. "review it any time.")
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Send MOTD as a nicely-formatted formspec popup.
|
2020-01-25 08:06:36 -05:00
|
|
|
local fsw = tonumber(minetest.settings:get(modname .. "_width")) or 8.5
|
|
|
|
local fsh = tonumber(minetest.settings:get(modname .. "_height")) or 6
|
|
|
|
minetest.show_formspec(name, modname,
|
|
|
|
"size[" .. fsw .. "," .. fsh .. ",true]"
|
|
|
|
.. "textarea[0.3,0;" .. fsw .. "," .. fsh .. ";;;"
|
2020-09-01 20:04:18 -04:00
|
|
|
.. minetest.formspec_escape(string_gsub(motd, "<phash>", phash(name)))
|
2020-01-25 08:06:36 -05:00
|
|
|
.. "]button_exit[0," .. (fsh - 0.75) .. ";" .. fsw
|
|
|
|
.. ",1;ok;" .. (minetest.settings:get(modname
|
|
|
|
.. "_button") or "Continue") .. "]")
|
2016-05-03 22:33:09 -04:00
|
|
|
|
|
|
|
-- If the player had already seen the MOTD (i.e. this is a
|
|
|
|
-- forced request) then we don't need to update the database or
|
|
|
|
-- send them a reminder.
|
|
|
|
if seen then return end
|
|
|
|
|
|
|
|
-- Update the seen database in-memory and on-disk
|
|
|
|
-- so we don't send another copy of the same content to the
|
|
|
|
-- same player automatically.
|
2020-01-25 08:06:36 -05:00
|
|
|
modstore:set_string(name, hash)
|
2016-05-03 22:33:09 -04:00
|
|
|
|
|
|
|
-- Remind the player where they can get the MOTD if they
|
|
|
|
-- want it, and explain why it may or may not appear again
|
|
|
|
-- automatically on future logins.
|
2019-09-01 12:52:22 -04:00
|
|
|
minetest.chat_send_player(name, "Updated MOTD. It will not display again "
|
|
|
|
.. "automatically, unless there are changes. Use /motd command to "
|
2016-05-03 22:33:09 -04:00
|
|
|
.. "review it at any time.")
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Display an "automatic" MOTD popup on new player joins, i.e. for completely
|
|
|
|
-- new players, or those who have not seen the latest version yet.
|
|
|
|
minetest.register_on_joinplayer(function(player) sendmotd(player:get_player_name()) end)
|
|
|
|
|
|
|
|
-- Force popup display for players who request it via the /motd command.
|
2019-12-06 19:07:56 -05:00
|
|
|
minetest.register_chatcommand("motd", {func = function(name) sendmotd(name, true) end})
|
2024-08-24 12:18:52 -04:00
|
|
|
|
|
|
|
-- Periodically scan the database for deleted players and
|
|
|
|
-- garbage-collect agreement hashes from them.
|
|
|
|
do
|
|
|
|
local batch = {}
|
|
|
|
local rescan = 0
|
|
|
|
minetest.register_globalstep(function(dtime)
|
|
|
|
if #batch > 0 then
|
|
|
|
local pname = batch[#batch]
|
|
|
|
batch[#batch] = nil
|
|
|
|
if not minetest.player_exists(pname) then
|
|
|
|
minetest.log("info", string_format(
|
|
|
|
"%s gc %s", modname, pname))
|
|
|
|
modstore:set_string(pname, "")
|
|
|
|
end
|
|
|
|
return
|
|
|
|
end
|
|
|
|
if rescan > 0 then
|
|
|
|
rescan = rescan - dtime
|
|
|
|
return
|
|
|
|
end
|
|
|
|
rescan = 60 * (math_random() + 0.5)
|
|
|
|
batch = modstore:get_keys()
|
|
|
|
end)
|
|
|
|
end
|