105 lines
2.9 KiB
Lua

-- LUALOCALS < ---------------------------------------------------------
local minetest, pairs, string, tonumber
= minetest, pairs, string, tonumber
local string_format
= string.format
-- LUALOCALS > ---------------------------------------------------------
local modname = minetest.get_current_modname()
local invert = minetest.settings:get_bool(modname .. "_invert")
local timeout = tonumber(minetest.settings:get(modname .. "_timeout")) or 600
local reason = minetest.settings:get(modname .. "_reason") or "idle timeout"
minetest.register_privilege(modname, {
description = (invert and "Kick" or "Do not kick") .. " this player when idle",
give_to_singleplayer = false,
give_to_admin = false
})
local function kickable(pname)
return (not minetest.check_player_privs(pname, modname)) == (not invert)
end
local function now() return minetest.get_us_time() / 1000000 end
local times = {}
local actions = {}
minetest.register_chatcommand(modname, {
description = "Check player idle time and last action",
privs = {server = true},
func = function(_, param)
if not minetest.get_player_by_name(param) then
return true, string_format("Player %q not found", param)
end
if not kickable(param) then
return true, string_format("Player %q exempt", param)
end
local time = times[param]
if not time then
return true, string_format("Player %q no data", param)
end
return true, string_format(
"Player %q idle %0.2f/%0.2f last action %q",
param,
now() - time,
timeout,
actions[param] or "unknown")
end
})
local function bumpn(pname, action)
times[pname] = now()
actions[pname] = action
end
local function bump(player, action)
if not (player and player.get_player_name) then return end
local pname = player:get_player_name()
if not pname then return end
return bumpn(pname, action)
end
local function wrapon(event, idx, func)
minetest["register_on_" .. event](function(...)
return func(({...})[idx], event)
end)
end
wrapon("joinplayer", 1, bump)
wrapon("placenode", 3, bump)
wrapon("dignode", 3, bump)
wrapon("punchnode", 3, bump)
wrapon("chat_message", 1, bumpn)
wrapon("player_receive_fields", 1, bump)
wrapon("craft", 2, bump)
wrapon("player_inventory_action", 1, bump)
local looks = {}
local ctlbits = {}
local function checkplayer(player, pname)
local bits = player:get_player_control_bits()
local oldbits = ctlbits[pname]
ctlbits[pname] = bits
local look = player:get_look_dir()
local oldlook = looks[pname]
looks[pname] = look
if bits ~= oldbits then
return bumpn(pname, "control")
elseif not (oldlook and vector.distance(look, oldlook) < 0.001) then
return bumpn(pname, "lookdir")
end
end
minetest.register_globalstep(function()
for _, player in pairs(minetest.get_connected_players()) do
local pname = player:get_player_name()
if kickable(pname) then
checkplayer(player, pname)
if now() - times[pname] >= timeout then
minetest.kick_player(pname, reason)
end
end
end
end)