d161b83a90
We're already kicking off GC on startup, and it shouldn't be able to get way out of hand within an hour, so we don't need to scan quite so intensely.
298 lines
7.2 KiB
Lua
298 lines
7.2 KiB
Lua
-- LUALOCALS < ---------------------------------------------------------
|
|
local ipairs, math, minetest, next, pairs, pcall, string, table
|
|
= ipairs, math, minetest, next, pairs, pcall, string, table
|
|
local math_random, string_format, string_gmatch, string_match,
|
|
table_concat
|
|
= math.random, string.format, string.gmatch, string.match,
|
|
table.concat
|
|
-- LUALOCALS > ---------------------------------------------------------
|
|
|
|
local modname = minetest.get_current_modname()
|
|
local modstore = minetest.get_mod_storage()
|
|
|
|
local cache = {}
|
|
for k, v in pairs(modstore:to_table().fields) do
|
|
cache[k] = minetest.string_to_pos(v)
|
|
end
|
|
|
|
minetest.register_globalstep(function()
|
|
for _, player in ipairs(minetest.get_connected_players()) do
|
|
local pname = player:get_player_name()
|
|
local pos = player:get_pos()
|
|
local opos = cache[pname]
|
|
if (not opos) or (not vector.equals(pos, opos)) then
|
|
modstore:set_string(pname, minetest.pos_to_string(pos))
|
|
cache[pname] = pos
|
|
end
|
|
end
|
|
end)
|
|
|
|
local function expire()
|
|
for k in pairs(cache) do
|
|
if not minetest.player_exists(k) then
|
|
modstore:set_string(k, "")
|
|
cache[k] = nil
|
|
end
|
|
end
|
|
minetest.after(5 + math_random() * 5, expire)
|
|
end
|
|
minetest.after(0, expire)
|
|
|
|
minetest.register_privilege(modname, {
|
|
description = "Can see position of other players",
|
|
give_to_singleplayer = false,
|
|
give_to_admin = true
|
|
})
|
|
|
|
local function matchplayers(spec)
|
|
local found = {}
|
|
for k, v in pairs(cache) do
|
|
local matched
|
|
for p in string_gmatch(spec, "[^%s]+") do
|
|
if not matched then
|
|
local ok, res = pcall(function() return string_match(k, p) end)
|
|
matched = matched or (ok and res)
|
|
end
|
|
end
|
|
if matched then
|
|
local player = minetest.get_player_by_name(k)
|
|
found[k] = {
|
|
name = k,
|
|
pos = player and player:get_pos() or v,
|
|
player = player
|
|
}
|
|
end
|
|
end
|
|
return found
|
|
end
|
|
|
|
minetest.register_chatcommand("pos", {
|
|
description = "Get current position of players",
|
|
privs = {[modname] = true},
|
|
func = function(_, param)
|
|
local rpt = {}
|
|
for k, v in pairs(matchplayers(param)) do
|
|
rpt[#rpt + 1] = k .. " at "
|
|
.. minetest.pos_to_string(vector.round(v.pos))
|
|
.. (v.player and " [online]" or "")
|
|
end
|
|
if #rpt < 1 then rpt = {"no match found"} end
|
|
return true, table_concat(rpt, "\n")
|
|
end
|
|
})
|
|
|
|
------------------------------------------------------------------------
|
|
-- Teleport to offline players
|
|
|
|
local teleport = minetest.registered_chatcommands.teleport
|
|
if teleport and teleport.func then
|
|
local oldfunc = teleport.func
|
|
teleport.func = function(caller, ...)
|
|
local oldget = minetest.get_player_by_name
|
|
local function helper(...)
|
|
minetest.get_player_by_name = oldget
|
|
return ...
|
|
end
|
|
function minetest.get_player_by_name(name, ...)
|
|
local player = oldget(name, ...)
|
|
if player then return player end
|
|
local s = cache[name]
|
|
if s then
|
|
|
|
return {
|
|
get_pos = function() return s end,
|
|
set_pos = function()
|
|
minetest.after(0, function()
|
|
return minetest.chat_send_player(caller,
|
|
"Cannot teleport an offline player.")
|
|
end)
|
|
end,
|
|
get_attach = function() end,
|
|
}
|
|
end
|
|
end
|
|
return helper(oldfunc(caller, ...))
|
|
end
|
|
end
|
|
|
|
------------------------------------------------------------------------
|
|
-- Configure position tracking filter
|
|
|
|
local trackcache = {}
|
|
local function gettracking(player, pname)
|
|
local tracking = {}
|
|
if minetest.check_player_privs(pname, modname) then
|
|
tracking = trackcache[pname]
|
|
if not tracking then
|
|
local s = player:get_meta():get_string(modname)
|
|
tracking = s and s ~= "" and minetest.deserialize(s) or {}
|
|
trackcache[pname] = tracking
|
|
end
|
|
end
|
|
return tracking
|
|
end
|
|
|
|
local function trackset(pname, param, value)
|
|
local player = minetest.get_player_by_name(pname)
|
|
if not player then return false, "Cannot track while offline" end
|
|
|
|
local set
|
|
if (not param) or (param == "") then
|
|
if value then
|
|
set = {}
|
|
for _, p in pairs(minetest.get_connected_players()) do
|
|
local n = p:get_player_name()
|
|
set[n] = {
|
|
name = n,
|
|
pos = p:get_pos(),
|
|
player = p
|
|
}
|
|
end
|
|
else
|
|
set = matchplayers(".")
|
|
end
|
|
else
|
|
set = matchplayers(param)
|
|
end
|
|
|
|
local tracking = gettracking(player, pname)
|
|
for k in pairs(set) do
|
|
if k ~= pname then tracking[k] = value end
|
|
end
|
|
for k in pairs(tracking) do
|
|
if not minetest.player_exists(k) then
|
|
tracking[k] = nil
|
|
end
|
|
end
|
|
|
|
trackcache[pname] = tracking
|
|
player:get_meta():set_string(modname, minetest.serialize(tracking))
|
|
|
|
local total = 0
|
|
for _ in pairs(tracking) do total = total + 1 end
|
|
return true, "now tracking " .. total .. " player(s)"
|
|
end
|
|
|
|
minetest.register_chatcommand("postrack", {
|
|
description = "Enable tracking position of players",
|
|
privs = {[modname] = true},
|
|
func = function(pname, param)
|
|
return trackset(pname, param, true)
|
|
end
|
|
})
|
|
minetest.register_chatcommand("posuntrack", {
|
|
description = "Disable tracking position of players",
|
|
privs = {[modname] = true},
|
|
func = function(pname, param)
|
|
return trackset(pname, param)
|
|
end
|
|
})
|
|
|
|
------------------------------------------------------------------------
|
|
-- Posiiton tracking HUDs
|
|
|
|
local huds = {}
|
|
|
|
minetest.register_on_leaveplayer(function(player)
|
|
huds[player:get_player_name()] = nil
|
|
end)
|
|
|
|
local hud_elem_type = minetest.features.hud_def_type_field and "type" or "hud_elem_type"
|
|
local function trackinghuds(player)
|
|
local pname = player:get_player_name()
|
|
|
|
local tracking = gettracking(player, pname)
|
|
|
|
local phuds = huds[pname]
|
|
if not phuds then
|
|
if not next(tracking) then return end
|
|
phuds = {}
|
|
huds[pname] = phuds
|
|
end
|
|
|
|
local show = {}
|
|
for k in pairs(tracking) do
|
|
local peer = minetest.get_player_by_name(k)
|
|
local ppos = peer and peer:get_pos() or cache[k]
|
|
if not ppos then
|
|
local s = modstore:get_string(k)
|
|
ppos = s and s ~= "" and minetest.string_to_pos(s)
|
|
cache[k] = ppos
|
|
end
|
|
if ppos then
|
|
show[k] = {
|
|
x = ppos.x,
|
|
y = ppos.y + 1.25,
|
|
z = ppos.z,
|
|
on = not not peer
|
|
}
|
|
end
|
|
end
|
|
|
|
for k, v in pairs(show) do
|
|
local old = phuds[k]
|
|
if old then
|
|
if not vector.equals(old.pos, v) then
|
|
player:hud_change(old.id, "world_pos", v)
|
|
old.pos = v
|
|
end
|
|
if old.on ~= v.on then
|
|
player:hud_change(old.id, "number", v.on
|
|
and 0xffff00 or 0x888800)
|
|
old.on = v.on
|
|
end
|
|
else
|
|
phuds[k] = {
|
|
pos = v,
|
|
on = v.on,
|
|
id = player:hud_add({
|
|
[hud_elem_type] = "waypoint",
|
|
world_pos = v,
|
|
name = k,
|
|
precision = 1,
|
|
number = v.on and 0xffff00 or 0x888800
|
|
})
|
|
}
|
|
end
|
|
end
|
|
|
|
for k, v in pairs(phuds) do
|
|
if not show[k] then
|
|
player:hud_remove(v.id)
|
|
phuds[k] = nil
|
|
end
|
|
end
|
|
if not pairs(phuds)(phuds) then
|
|
huds[pname] = nil
|
|
end
|
|
end
|
|
|
|
minetest.register_globalstep(function()
|
|
for _, player in pairs(minetest.get_connected_players()) do
|
|
trackinghuds(player)
|
|
end
|
|
end)
|
|
|
|
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 = 3600 * (math_random() + 0.5)
|
|
batch = modstore:get_keys()
|
|
end)
|
|
end
|