Aaron Suen ee99c24d52 More aggressive priv caching strategy
Instead of caching privs only for the current tick, and only for
NodeCore itself, change the base API to use caching universally,
including for naive mod code.  Privs are cached until invalidated
by a change; it's assumed here that the "auth_reload" command
exists in the first place because this kind of caching is allowed
and accounted for, even if it's not actually done by the engine
or builtin.
2022-10-07 06:59:04 -04:00

110 lines
3.2 KiB
Lua

-- LUALOCALS < ---------------------------------------------------------
local math, minetest, nodecore, pairs, string, type, vector
= math, minetest, nodecore, pairs, string, type, vector
local math_random, string_format
= math.random, string.format
-- LUALOCALS > ---------------------------------------------------------
local modname = minetest.get_current_modname()
local mintime = nodecore.setting_float(modname .. "_time", 2,
"Push out of solid time", [[The amount of time a player
needs to be trapped in a solid node before being pushed out.]])
local stepdist = nodecore.setting_float(modname .. "_stepdist", 5,
"Push out of solid distance", [[The maximum distance in
nodes that a player will be pushed out of solids.]])
local function normalbox(box)
if not box then return true end
if type(box) ~= "table" then return end
if box.fixed then return normalbox(box.fixed) end
if #box == 1 then return normalbox(box[1]) end
return box[1] == -0.5 and box[2] == -0.5 and box[3] == -0.5
and box[4] == 0.5 and box[5] == 0.5 and box[6] == 0.5
end
local function ispushout(def)
if not def.walkable then return end
if def.liquidtype ~= "none" then return end
if def.groups and def.groups.is_stack_only then return end
return normalbox(def.collision_box)
end
local solids = {}
minetest.after(0, function()
for k, v in pairs(minetest.registered_nodes) do
if ispushout(v) then
solids[k] = true
end
end
solids.ignore = nil
end)
local function isroom(pos)
return not (solids[minetest.get_node(pos).name]
or solids[minetest.get_node({
x = pos.x,
y = pos.y + 1,
z = pos.z
}).name])
end
nodecore.player_pushout_disable = nodecore.player_pushout_disable or function() end
nodecore.register_playerstep({
label = "push player out of solids",
action = function(player, data, dtime)
local function reset() data.pushout = nil end
if minetest.get_player_privs(player).noclip
or nodecore.player_pushout_disable(player, data)
then return reset() end
local pos = vector.round(player:get_pos())
if isroom(pos) then return reset() end
local podata = data.pushout or {}
local oldpos = podata.pos or pos
if not vector.equals(pos, oldpos) then return reset() end
local pt = (podata.time or 0) + dtime
if pt < mintime then
data.pushout = {time = pt, pos = pos}
return
end
local function pushto(newpos)
local dist = vector.distance(pos, newpos)
if dist > 1 then
nodecore.addphealth(player, -dist + 1, {
nc_type = "pushout"
})
end
newpos.y = newpos.y - 0.49
nodecore.log("action", string_format("%s pushed out of"
.. " solid from %s to %s",
data.pname,
minetest.pos_to_string(pos),
minetest.pos_to_string(newpos)))
newpos.keepinv = true
player:set_pos(newpos)
return reset()
end
for rel in nodecore.settlescan() do
local p = vector.add(pos, rel)
if isroom(p) then
return pushto(p)
end
end
local function bias(n)
return n + ((n > 0) and math_random(-stepdist - 1, stepdist - 1)
or math_random(-stepdist + 1, stepdist + 1))
end
return pushto({
x = bias(pos.x),
y = bias(pos.y),
z = bias(pos.z)
})
end
})