9c9eb4b85a
Some time in the 5.3 dev stream (docs updated at 217f3a42), object refs started being invalidated immediately on calling obj:remove(), such that obj:get_pos() starts to return nil instead of the object's last known position. This can cause some crashes in NodeCore, where we assume that our object is still valid (or usable as if it were still valid) even though we're looping through handlers and any one of them may have remove()d the object before other handlers get a chance to fire. Instead, just watch for unexpected nil returns from functions we expect would never return nil (e.g. get_pos or get_properties) and return if we hit one. We can assume all other calls will be non-nil after that one, as long as we stay in the same function flow.
149 lines
4.3 KiB
Lua
149 lines
4.3 KiB
Lua
-- LUALOCALS < ---------------------------------------------------------
|
|
local ItemStack, math, minetest, nodecore, pairs, type, vector
|
|
= ItemStack, math, minetest, nodecore, pairs, type, vector
|
|
local math_floor, math_pi, math_random, math_sqrt
|
|
= math.floor, math.pi, math.random, math.sqrt
|
|
-- LUALOCALS > ---------------------------------------------------------
|
|
|
|
function minetest.spawn_falling_node(pos, node, meta)
|
|
node = node or minetest.get_node(pos)
|
|
if node.name == "air" or node.name == "ignore" then
|
|
return false
|
|
end
|
|
local obj = minetest.add_entity(pos, "__builtin:falling_node")
|
|
if obj then
|
|
obj:get_luaentity():set_node(node, meta or minetest.get_meta(pos):to_table())
|
|
minetest.remove_node(pos)
|
|
return obj
|
|
end
|
|
return false
|
|
end
|
|
|
|
function nodecore.stackentprops(stack, yaw, rotate, ss)
|
|
local props = {
|
|
hp_max = 1,
|
|
physical = false,
|
|
collide_with_objects = false,
|
|
collisionbox = {0, 0, 0, 0, 0, 0},
|
|
visual = "wielditem",
|
|
visual_size = {x = 0.4, y = 0.4},
|
|
textures = {""},
|
|
spritediv = {x = 1, y = 1},
|
|
initial_sprite_basepos = {x = 0, y = 0},
|
|
is_visible = false,
|
|
static_save = ss and true or false
|
|
}
|
|
local scale = 0
|
|
yaw = yaw or 0
|
|
if stack then
|
|
if type(stack) == "string" then stack = ItemStack(stack) end
|
|
props.is_visible = not stack:is_empty()
|
|
props.textures[1] = stack:get_name()
|
|
|
|
local ratio = stack:get_count() / stack:get_stack_max()
|
|
if ratio > 1 then ratio = 1 end
|
|
scale = math_sqrt(ratio) * 0.15 + 0.25
|
|
props.visual_size = {x = scale, y = scale}
|
|
|
|
props.automatic_rotate = rotate
|
|
and rotate * 2 / math_sqrt(math_sqrt(ratio)) or nil
|
|
|
|
if ratio == 1 then ratio = 1 - (stack:get_wear() / 65536) end
|
|
|
|
if ratio ~= 1 then yaw = yaw + 1/8 + 3/8 * (1 - ratio) end
|
|
yaw = yaw - 2 * math_floor(yaw / 2)
|
|
end
|
|
return props, scale, yaw * math_pi / 2
|
|
end
|
|
|
|
function nodecore.entity_staticdata_helpers(savedprops)
|
|
return function(self, data)
|
|
data = data and minetest.deserialize(data) or {}
|
|
for k in pairs(savedprops) do self[k] = data[k] end
|
|
end,
|
|
function(self)
|
|
local data = {}
|
|
for k in pairs(savedprops) do data[k] = self[k] end
|
|
return minetest.serialize(data)
|
|
end
|
|
end
|
|
|
|
local area_unloaded = {}
|
|
|
|
local function collides(pos)
|
|
if pos.y < nodecore.map_limit_min then return {name = "ignore"} end
|
|
local node = minetest.get_node_or_nil(pos)
|
|
if not node then return area_unloaded end
|
|
local def = minetest.registered_nodes[node.name]
|
|
if not def then return node end
|
|
if def.walkable then return node end
|
|
end
|
|
|
|
local oldcheck = minetest.check_single_for_falling
|
|
function minetest.check_single_for_falling(...)
|
|
local oldget = minetest.get_node_or_nil
|
|
function minetest.get_node_or_nil(pos, ...)
|
|
if pos.y < nodecore.map_limit_min then return end
|
|
return oldget(pos, ...)
|
|
end
|
|
local function helper(...)
|
|
minetest.get_node_or_nil = oldget
|
|
return ...
|
|
end
|
|
return helper(oldcheck(...))
|
|
end
|
|
|
|
function nodecore.entity_settle_check(on_settle, isnode)
|
|
return function(self)
|
|
local pos = self.object:get_pos()
|
|
if not pos then return end
|
|
if pos.y < nodecore.map_limit_min then
|
|
pos.y = nodecore.map_limit_min
|
|
self.object:set_pos(pos)
|
|
local vel = self.object:get_velocity()
|
|
vel.y = 0
|
|
self.object:set_velocity(vel)
|
|
end
|
|
|
|
if self.settle_oldpos and vector.distance(self.settle_oldpos, pos) < 1/16 then
|
|
local csize = self.collidesize or 0.5
|
|
pos.x = pos.x + (math_random() * 2 - 1) * csize
|
|
pos.z = pos.z + (math_random() * 2 - 1) * csize
|
|
else
|
|
self.settle_oldpos = pos
|
|
end
|
|
|
|
local yvel = self.object:get_velocity().y
|
|
local coll = (isnode or self.not_rising and yvel == 0)
|
|
and collides({x = pos.x, y = pos.y - 0.75, z = pos.z})
|
|
self.not_rising = yvel <= 0
|
|
if not coll then
|
|
if self.setvel then
|
|
self.object:set_velocity(self.vel)
|
|
self.setvel = nil
|
|
end
|
|
self.vel = self.object:get_velocity()
|
|
return nodecore.grav_air_accel_ent(self.object)
|
|
end
|
|
if coll == area_unloaded then
|
|
self.object:set_velocity({x = 0, y = 0, z = 0})
|
|
self.object:set_acceleration({x = 0, y = 0, z = 0})
|
|
self.setvel = true
|
|
return
|
|
end
|
|
pos = vector.round(pos)
|
|
|
|
if not on_settle(self, pos, collides) then return end
|
|
|
|
pos.y = pos.y + 1
|
|
for _, obj in pairs(nodecore.get_objects_at_pos(pos)) do
|
|
obj = obj.get_luaentity and obj:get_luaentity()
|
|
if obj and obj.settle_check then
|
|
obj:settle_check()
|
|
end
|
|
end
|
|
|
|
return nodecore.fallcheck(pos)
|
|
end
|
|
end
|