a9e99f921e
- Split "discovery" into 2 stages: a check (to see if there is even anything new to discover) and a commit. - When doing witness checks, check to see if there is anything to be discovered first, before doing the more expensive visibility checks. If the player already knows all the discoveries, then we don't need to do any of the raycasts. This should speed up especially pathological cases like door catapulting, which can cause witnessing to happen at multiple points, and causes major slowdowns when multiple players are within the distance range and facing the correct direction due to tons of raycasts happening.
169 lines
4.9 KiB
Lua
169 lines
4.9 KiB
Lua
-- LUALOCALS < ---------------------------------------------------------
|
|
local ipairs, minetest, nodecore, pairs, string, table, tostring, type,
|
|
vector
|
|
= ipairs, minetest, nodecore, pairs, string, table, tostring, type,
|
|
vector
|
|
local string_format, table_concat
|
|
= string.format, table.concat
|
|
-- LUALOCALS > ---------------------------------------------------------
|
|
|
|
local modname = minetest.get_current_modname()
|
|
|
|
nodecore.register_on_discover,
|
|
nodecore.registered_on_discovers
|
|
= nodecore.mkreg()
|
|
|
|
local cache = {}
|
|
|
|
local function loaddb(p)
|
|
if nodecore.hints_disabled(p) then return end
|
|
if not (p and nodecore.interact(p)) then return end
|
|
local player
|
|
local pname
|
|
if type(p) == "string" then
|
|
player, pname = minetest.get_player_by_name(p), p
|
|
else
|
|
player, pname = p, p:get_player_name()
|
|
end
|
|
if not (player and pname) then return end
|
|
|
|
local db = cache[pname]
|
|
if not db then
|
|
local s = player:get_meta():get_string(modname) or ""
|
|
db = s and minetest.deserialize(s) or {}
|
|
cache[pname] = db
|
|
end
|
|
|
|
return db, player, pname, function()
|
|
player:get_meta():set_string(modname, minetest.serialize(db))
|
|
end
|
|
|
|
end
|
|
nodecore.get_player_discovered = loaddb
|
|
|
|
local function discover_deferred(p, keys, prefix)
|
|
local db, player, pname, save = loaddb(p)
|
|
local new = {}
|
|
if not db then return end
|
|
for k in pairs(nodecore.flatkeys(keys)) do
|
|
k = (prefix or "") .. tostring(k)
|
|
if not db[k] then
|
|
new[#new + 1] = k
|
|
end
|
|
end
|
|
if #new < 1 then return end
|
|
return function()
|
|
for i = 1, #new do db[new[i]] = true end
|
|
minetest.log("action", string_format("player %s discovers %q", pname,
|
|
table_concat(new, ", ")))
|
|
for _, cb in pairs(nodecore.registered_on_discovers) do
|
|
cb(player, new, pname, db)
|
|
end
|
|
return save()
|
|
end
|
|
end
|
|
nodecore.player_discover_deferred = discover_deferred
|
|
local function discover(...)
|
|
local deferred = discover_deferred(...)
|
|
if deferred then return deferred() end
|
|
end
|
|
nodecore.player_discover = discover
|
|
|
|
------------------------------------------------------------------------
|
|
-- PLAYER EVENTS
|
|
|
|
local function reghook(func, stat, pwhom, npos, ppos)
|
|
return func("stat hook", function(...)
|
|
local t = {...}
|
|
local whom = t[pwhom]
|
|
if not (whom and whom:is_player()) then return end
|
|
local n = npos and t[npos].name or nil
|
|
if ppos then
|
|
local pos = t[ppos]
|
|
local stack = pos and nodecore.stack_get(pos)
|
|
if stack and not stack:is_empty() then
|
|
discover(whom, stat .. ":" .. stack:get_name())
|
|
end
|
|
end
|
|
return discover(whom, n and (stat .. ":" .. n) or stat)
|
|
end)
|
|
end
|
|
reghook(nodecore.register_on_punchnode, "punch", 3, 2, 1)
|
|
reghook(nodecore.register_on_dignode, "dig", 3, 2)
|
|
reghook(nodecore.register_on_placenode, "place", 3, 2)
|
|
reghook(nodecore.register_on_dieplayer, "die", 1)
|
|
reghook(nodecore.register_on_respawnplayer, "spawn", 1)
|
|
reghook(nodecore.register_on_joinplayer, "join", 1)
|
|
|
|
local function unpackreason(reason)
|
|
if type(reason) ~= "table" then return reason or "?" end
|
|
if reason.nc_type then return "nc", reason.nc_type end
|
|
if reason.from then return reason.from, reason.type or nil end
|
|
return reason.type or "?"
|
|
end
|
|
|
|
nodecore.register_on_player_hpchange("hurt/heal stats", function(whom, change, reason)
|
|
if change < 0 then
|
|
return discover(whom, "hurt:" .. unpackreason(reason))
|
|
else
|
|
return discover(whom, "heal:" .. unpackreason(reason))
|
|
end
|
|
end)
|
|
|
|
nodecore.register_on_cheat("cheat stats", function(player, reason)
|
|
discover(player, "cheat: " .. unpackreason(reason))
|
|
end)
|
|
|
|
nodecore.register_on_chat_message("chat message stats", function(name, msg)
|
|
discover(name, "chat:" .. ((msg:sub(1, 1) == "/") and "command" or "message"))
|
|
end)
|
|
|
|
------------------------------------------------------------------------
|
|
-- PLAYER SCAN
|
|
|
|
nodecore.register_on_node_stare,
|
|
nodecore.registered_on_node_stares = nodecore.mkreg()
|
|
|
|
nodecore.register_playerstep({
|
|
label = "inv",
|
|
action = function(player, data, dtime)
|
|
-- inventory
|
|
local inv = player:get_inventory()
|
|
local t = {}
|
|
for i = 1, inv:get_size("main") do
|
|
local stack = inv:get_stack("main", i)
|
|
if not stack:is_empty() then
|
|
t[stack:get_name()] = true
|
|
end
|
|
end
|
|
for k in pairs(t) do
|
|
discover(player, "inv:" .. k)
|
|
end
|
|
|
|
-- looking at
|
|
local pt = data.raycast()
|
|
if pt and pt.type == "node" and data.staring_pos
|
|
and vector.equals(data.staring_pos, pt.under) then
|
|
data.staring_time = data.staring_time + dtime
|
|
if data.staring_time >= 0.8 then
|
|
data.staring_time = 0
|
|
for _, f in ipairs(nodecore.registered_on_node_stares) do
|
|
f(player, pt, data)
|
|
end
|
|
end
|
|
else
|
|
data.staring_pos = pt and pt.under
|
|
data.staring_time = dtime
|
|
end
|
|
end
|
|
})
|
|
|
|
nodecore.register_on_node_stare(function(player, pt)
|
|
local nn = minetest.get_node(pt.under).name
|
|
discover(player, "look:" .. nn)
|
|
local stack = nodecore.stack_get(pt.under)
|
|
if stack and not stack:is_empty() then
|
|
discover(player, "look:" .. stack:get_name())
|
|
end
|
|
end)
|