7175f51921
We actually want to prune accounts based on the last time they were used or seen online at all, not just the last time they successfully joined the world. Keeping track of our own timestamp should catch scenarios like players sucessfully authing but failing to complete joining the world, or players who log in and then play for a long time after login. This also makes it safer to use with much shorter timeout periods and longer-running servers.
144 lines
3.7 KiB
Lua
144 lines
3.7 KiB
Lua
-- LUALOCALS < ---------------------------------------------------------
|
|
local ipairs, math, minetest, os, string, tonumber
|
|
= ipairs, math, minetest, os, string, tonumber
|
|
local math_random, os_time, string_format
|
|
= math.random, os.time, string.format
|
|
-- LUALOCALS > ---------------------------------------------------------
|
|
|
|
local modname = minetest.get_current_modname()
|
|
local modstore = minetest.get_mod_storage()
|
|
|
|
local scaninterval = tonumber(minetest.settings:get(modname .. "_interval")) or 3600
|
|
local maxdays = tonumber(minetest.settings:get(modname .. "_maxdays")) or 90
|
|
local keepauth = minetest.settings:get_bool(modname .. "_keepauth")
|
|
|
|
local protectnames = {
|
|
singleplayer = true,
|
|
[minetest.settings:get("name")] = true,
|
|
}
|
|
|
|
local noprune = "noprune"
|
|
minetest.register_privilege(noprune, {
|
|
description = "Do not automatically prune player for unuse",
|
|
give_to_admin = false,
|
|
give_to_singleplayer = false,
|
|
})
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
local queue_current
|
|
local queue_next
|
|
local queue_pos = 1
|
|
|
|
local function processqueue()
|
|
if queue_current then
|
|
local act = queue_current[queue_pos]
|
|
act()
|
|
queue_pos = queue_pos + 1
|
|
if queue_pos > #queue_current then
|
|
queue_current = nil
|
|
queue_pos = 1
|
|
end
|
|
end
|
|
if not queue_current then
|
|
queue_current = queue_next
|
|
queue_next = nil
|
|
end
|
|
if queue_current then
|
|
minetest.after(0, processqueue)
|
|
end
|
|
end
|
|
|
|
local function enqueue(act)
|
|
queue_next = queue_next or {}
|
|
queue_next[#queue_next + 1] = act
|
|
if not queue_current then
|
|
queue_current = queue_next
|
|
queue_next = nil
|
|
minetest.after(0, processqueue)
|
|
end
|
|
end
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
do
|
|
local function bump(pname)
|
|
modstore:set_string(pname, string_format("%d", os_time()))
|
|
end
|
|
minetest.register_on_authplayer(function(pname, _, success)
|
|
if success then bump(pname) end
|
|
end)
|
|
minetest.register_on_joinplayer(function(player)
|
|
bump(player:get_player_name())
|
|
end)
|
|
minetest.register_on_leaveplayer(function(player)
|
|
bump(player:get_player_name())
|
|
end)
|
|
local function scanall()
|
|
for _, player in ipairs(minetest.get_connected_players()) do
|
|
bump(player:get_player_name())
|
|
end
|
|
end
|
|
minetest.register_on_shutdown(scanall)
|
|
local function bgscan()
|
|
minetest.after(1, bgscan)
|
|
return scanall()
|
|
end
|
|
minetest.after(0, bgscan)
|
|
end
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
local function check(pname)
|
|
if protectnames[pname] or minetest.get_player_by_name(pname) then return end
|
|
|
|
local handler = minetest.get_auth_handler()
|
|
local data = handler.get_auth(pname)
|
|
if not data then return end
|
|
|
|
if data.privileges[noprune] then return end
|
|
|
|
local logintime = data.last_login
|
|
local seentime = tonumber(modstore:get_string(pname)) or 0
|
|
local seen = (logintime > seentime) and logintime or seentime
|
|
|
|
local now = os_time()
|
|
local daysago = (now - seen) / 86400
|
|
if daysago <= maxdays then return end
|
|
|
|
modstore:set_string(pname, "")
|
|
minetest.log("warning", string_format(
|
|
"Deleting player %q last seen %0.1f day(s) ago",
|
|
pname, daysago))
|
|
minetest.remove_player(pname)
|
|
if not keepauth then
|
|
minetest.log("warning", string_format(
|
|
"Purging player %q auth", pname))
|
|
return minetest.remove_player_auth(pname)
|
|
end
|
|
end
|
|
|
|
local function runscan()
|
|
local handler = minetest.get_auth_handler()
|
|
local names = {}
|
|
for name in handler.iterate() do
|
|
names[#names + 1] = name
|
|
end
|
|
for i = #names, 2, -1 do
|
|
local j = math_random(1, i)
|
|
local x = names[i]
|
|
names[i] = names[j]
|
|
names[j] = x
|
|
end
|
|
for i = 1, #names do
|
|
local pname = names[i]
|
|
enqueue(function() return check(pname) end)
|
|
end
|
|
end
|
|
|
|
local function startscan()
|
|
enqueue(runscan)
|
|
minetest.after(scaninterval, startscan)
|
|
end
|
|
minetest.after(0, startscan)
|