Aaron Suen f3bdf91dc6 More accurate entity settling
Under heavy lag conditions, physical entities may
land on the ground at any point during a time
step, but if they have a lot of horizontal velocity
then they may "slide" along the ground, and if we
only check for settling based on their pos at step
times, they may have already slid some distance
dependent on luck and the actual amount of
server lag.  This means that under heavy lag
conditions, items may spread out inconsistently
from their landing positions and make a messy
pile.

Using the MT 5.3+ moveresult parameter of
entity steps, we can easily find out when the
initial ground contact occured, and use that as
the settling position, which should be more
consistent under lag conditions.

This has the effect of making entities more
consistently "sticky" so they always tend to
stick at the point of initial contact and rarely
slide or glance along the ground, unless they
hit an edge/corner.
2021-12-12 10:01:24 -05:00

98 lines
2.9 KiB
Lua

-- LUALOCALS < ---------------------------------------------------------
local ItemStack, ipairs, math, minetest, nodecore, vector
= ItemStack, ipairs, math, minetest, nodecore, vector
local math_cos, math_pi, math_random, math_sin, math_sqrt
= math.cos, math.pi, math.random, math.sin, math.sqrt
-- LUALOCALS > ---------------------------------------------------------
nodecore.register_item_entity_step,
nodecore.registered_item_entity_steps
= nodecore.mkreg()
nodecore.register_item_entity_on_settle,
nodecore.registered_item_entity_on_settles
= nodecore.mkreg()
local function stub() end
local data_load, data_save = nodecore.entity_staticdata_helpers({
maxy = true,
itemstring = true,
spin = true,
vel = true,
setvel = true
})
minetest.register_entity(":__builtin:item", {
initial_properties = {
physical = true,
is_visible = false,
collide_with_objects = false,
collisionbox = {0, 0, 0, 0, 0, 0}
},
set_item = function(self, item)
local stack = ItemStack(item)
item = item and stack:to_string()
if item and item ~= self.itemstring then
self.itemstring = item
self.object:set_yaw(math_random() * math_pi * 2)
end
if nodecore.item_is_virtual(self.itemstring) then return self.object:remove() end
self.spin = self.spin or math_random(1, 2) * 2 - 3
local p, s = nodecore.stackentprops(self.itemstring, 0, self.spin, true)
s = s / math_sqrt(2)
self.collidesize = s
p.collisionbox = {-s, -s, -s, s, s, s}
p.physical = true
return self.object:set_properties(p)
end,
get_staticdata = data_save,
on_activate = function(self, data)
self.object:set_armor_groups({immortal = 1})
nodecore.entity_update_maxy(self)
data_load(self, data)
return self:set_item()
end,
on_punch = function(self, hitter)
local inv = hitter and hitter:get_inventory()
if inv then
local stack = ItemStack(self.itemstring)
stack = inv:add_item("main", stack)
if stack:is_empty() then return self.object:remove() end
self:set_item(stack)
end
local rho = math_random() * math_pi * 2
local y = math_sin(rho)
local xz = math_cos(rho)
local theta = math_random() * math_pi * 2
local x = math_cos(theta) * xz
local z = math_sin(theta) * xz
self.object:add_velocity(vector.multiply({x = x, y = y, z = z}, 5))
end,
try_merge_with = stub,
enable_physics = stub,
disable_physics = stub,
settle_check = nodecore.entity_settle_check(function(self, ...)
for _, func in ipairs(nodecore.registered_item_entity_on_settles) do
if func(self, ...) == true then return true end
end
end),
on_step = function(self, ...)
if not self.itemstring then return self.object:remove() end
nodecore.entity_update_maxy(self)
if self:settle_check(...) then return end
for _, func in ipairs(nodecore.registered_item_entity_steps) do
if func(self, ...) == true then return end
end
end,
})