299 lines
7.2 KiB
Lua
299 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,
|
|
z_index = -300,
|
|
})
|
|
}
|
|
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
|