Aaron Suen b37d01107f Do the distance check before db check
The distance check is less complex and will probably
filter out players a lot faster on a largeish server, so
it's a better check to run first.
2022-09-24 16:36:55 -04:00

129 lines
3.7 KiB
Lua

-- LUALOCALS < ---------------------------------------------------------
local math, minetest, nodecore, pairs, table, type, vector
= math, minetest, nodecore, pairs, table, type, vector
local math_pi, table_remove
= math.pi, table.remove
-- LUALOCALS > ---------------------------------------------------------
local modname = minetest.get_current_modname()
local seethru = {}
minetest.after(0, function()
for name, def in pairs(minetest.registered_nodes) do
if (minetest.get_item_group(name, "witness_opaque") == 0)
and (
minetest.get_item_group(name, "witness_transparent") > 0
or def.sunlight_propagates
or (
def.paramtype == "light"
and def.alpha
and def.alpha > 0
and def.alpha < 255
)
) then
seethru[name] = true
end
end
end)
local metakey = modname .. "_witness"
local cache = {}
local function witnessdata(player)
local pname = player:get_player_name()
local meta = player:get_meta()
local data = cache[pname]
if not data then
data = meta:get_string(metakey) or ""
data = data and data ~= "" and minetest.deserialize(data)
or {queue = {}, lookup = {}}
cache[pname] = data
end
return data, function() return meta:set_string(metakey, minetest.serialize(data)) end
end
local function canwitnessnow(player, pos)
local ppos = player:get_pos()
ppos.y = ppos.y + player:get_properties().eye_height
local look = player:get_look_dir()
local targ = vector.normalize(vector.subtract(pos, ppos))
if vector.angle(look, targ) > math_pi / 8 then return end
local rp = vector.round(pos)
for pt in minetest.raycast(pos, ppos, false, true) do
if not pt.under then return end
if vector.equals(pt.under, rp) then return true end
local node = minetest.get_node(pt.under)
if not seethru[node.name] then return end
end
return true
end
local function witnesslater(player, pos, disc)
disc = nodecore.flatkeys(disc)
local data, save = witnessdata(player)
local newdata = {
node = minetest.get_node(pos).name,
stack = nodecore.stack_get(pos):get_name(),
disc = disc
}
local posstr = minetest.pos_to_string(pos)
local olddata = data.lookup[posstr]
if olddata and (olddata.node == newdata.node) and (olddata.stack
== newdata.stack) and (type(olddata.disc) == "table") then
for k in pairs(disc) do
olddata.disc[k] = true
end
else
data.queue[#data.queue + 1] = pos
while #data.queue > 100 do table_remove(data.queue, 1) end
data.lookup[posstr] = newdata
end
save()
end
local function witness_core(pos, disc, maxdist)
maxdist = maxdist or 16
for _, player in pairs(minetest.get_connected_players()) do
local ppos = player:get_pos()
if vector.distance(ppos, pos) <= maxdist then
local deferred = nodecore.player_discover_deferred(
player, disc, "witness:")
if deferred then
if canwitnessnow(player, pos) then
deferred()
else
witnesslater(player, pos, disc)
end
end
end
end
end
function nodecore.witness(pos, disc, maxdist)
if (not pos.x) and pos[1] and pos[1].x then
for i = 1, #pos do
witness_core(pos[i], disc, maxdist)
end
else
return witness_core(pos, disc, maxdist)
end
end
local function delayed_witness(pos, node, player)
local data, save = witnessdata(player)
local posstr = minetest.pos_to_string(pos)
local found = data.lookup[posstr]
if not found then return end
data.lookup[posstr] = nil
save()
node = node or minetest.get_node(pos)
if (found.node ~= node.name) or (nodecore.stack_get(pos):get_name()
~= found.stack) then return end
return nodecore.player_discover(player, found.disc, "witness:")
end
minetest.register_on_punchnode(delayed_witness)
nodecore.register_on_node_stare(function(player, pt)
delayed_witness(pt.under, minetest.get_node(pt.under), player)
end)