mirror of
https://github.com/minetest/minetest.git
synced 2024-12-25 23:47:29 +01:00
8ed55b3aff
* Skip invalid objects in raycasts * Add `ObjectRef:is_valid` method * Add object inside radius / area iterators which skip invalid objects * Update docs to clarify object invalidation and how to deal with it --------- Co-authored-by: sfan5 <sfan5@live.de>
187 lines
4.8 KiB
Lua
187 lines
4.8 KiB
Lua
local log = {}
|
|
|
|
local function insert_log(...)
|
|
log[#log+1] = string.format(...)
|
|
end
|
|
|
|
local function objref_str(self, ref)
|
|
if ref and ref:is_player() then
|
|
return "player"
|
|
end
|
|
return self.object == ref and "self" or tostring(ref)
|
|
end
|
|
|
|
core.register_entity("unittests:callbacks", {
|
|
initial_properties = {
|
|
hp_max = 5,
|
|
visual = "upright_sprite",
|
|
textures = { "unittests_callback.png" },
|
|
static_save = false,
|
|
},
|
|
|
|
on_activate = function(self, staticdata, dtime_s)
|
|
self.object:set_armor_groups({test = 100})
|
|
assert(self.object:get_hp() == self.initial_properties.hp_max)
|
|
insert_log("on_activate(%d)", #staticdata)
|
|
end,
|
|
on_deactivate = function(self, removal)
|
|
insert_log("on_deactivate(%s)", tostring(removal))
|
|
end,
|
|
on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir, damage)
|
|
insert_log("on_punch(%s, %.1f, %d)", objref_str(self, puncher),
|
|
time_from_last_punch, damage)
|
|
end,
|
|
on_death = function(self, killer)
|
|
assert(self.object:get_hp() == 0)
|
|
insert_log("on_death(%s)", objref_str(self, killer))
|
|
end,
|
|
on_rightclick = function(self, clicker)
|
|
insert_log("on_rightclick(%s)", objref_str(self, clicker))
|
|
end,
|
|
on_attach_child = function(self, child)
|
|
insert_log("on_attach_child(%s)", objref_str(self, child))
|
|
end,
|
|
on_detach_child = function(self, child)
|
|
insert_log("on_detach_child(%s)", objref_str(self, child))
|
|
end,
|
|
on_detach = function(self, parent)
|
|
insert_log("on_detach(%s)", objref_str(self, parent))
|
|
end,
|
|
get_staticdata = function(self)
|
|
assert(false)
|
|
end,
|
|
})
|
|
|
|
--
|
|
|
|
local function check_log(expect)
|
|
if #expect ~= #log then
|
|
error("Log mismatch: " .. core.write_json(log))
|
|
end
|
|
for i, s in ipairs(expect) do
|
|
if log[i] ~= s then
|
|
error("Log mismatch at " .. i .. ": " .. core.write_json(log))
|
|
end
|
|
end
|
|
log = {} -- clear it for next time
|
|
end
|
|
|
|
local function test_entity_lifecycle(_, pos)
|
|
log = {}
|
|
|
|
-- with binary in staticdata
|
|
local obj = core.add_entity(pos, "unittests:callbacks", "abc\000def")
|
|
assert(obj and obj:is_valid())
|
|
check_log({"on_activate(7)"})
|
|
|
|
obj:set_hp(0)
|
|
check_log({"on_death(nil)", "on_deactivate(true)"})
|
|
|
|
assert(not obj:is_valid())
|
|
end
|
|
unittests.register("test_entity_lifecycle", test_entity_lifecycle, {map=true})
|
|
|
|
local function test_entity_interact(_, pos)
|
|
log = {}
|
|
|
|
local obj = core.add_entity(pos, "unittests:callbacks")
|
|
check_log({"on_activate(0)"})
|
|
|
|
-- rightclick
|
|
obj:right_click(obj)
|
|
check_log({"on_rightclick(self)"})
|
|
|
|
-- useless punch
|
|
obj:punch(obj, 0.5, {})
|
|
check_log({"on_punch(self, 0.5, 0)"})
|
|
|
|
-- fatal punch
|
|
obj:punch(obj, 1.9, {
|
|
full_punch_interval = 1.0,
|
|
damage_groups = { test = 10 },
|
|
})
|
|
check_log({
|
|
-- does 10 damage even though we only have 5 hp
|
|
"on_punch(self, 1.9, 10)",
|
|
"on_death(self)",
|
|
"on_deactivate(true)"
|
|
})
|
|
end
|
|
unittests.register("test_entity_interact", test_entity_interact, {map=true})
|
|
|
|
local function test_entity_attach(player, pos)
|
|
log = {}
|
|
|
|
local obj = core.add_entity(pos, "unittests:callbacks")
|
|
check_log({"on_activate(0)"})
|
|
|
|
-- attach player to entity
|
|
player:set_attach(obj)
|
|
check_log({"on_attach_child(player)"})
|
|
player:set_detach()
|
|
check_log({"on_detach_child(player)"})
|
|
|
|
-- attach entity to player
|
|
obj:set_attach(player)
|
|
check_log({})
|
|
obj:set_detach()
|
|
check_log({"on_detach(player)"})
|
|
|
|
obj:remove()
|
|
end
|
|
unittests.register("test_entity_attach", test_entity_attach, {player=true, map=true})
|
|
|
|
core.register_entity("unittests:dummy", {
|
|
initial_properties = {
|
|
hp_max = 1,
|
|
visual = "upright_sprite",
|
|
textures = { "no_texture.png" },
|
|
static_save = false,
|
|
},
|
|
})
|
|
|
|
local function test_entity_raycast(_, pos)
|
|
local obj1 = core.add_entity(pos, "unittests:dummy")
|
|
local obj2 = core.add_entity(pos:offset(1, 0, 0), "unittests:dummy")
|
|
local raycast = core.raycast(pos:offset(-1, 0, 0), pos:offset(2, 0, 0), true, false)
|
|
for pt in raycast do
|
|
if pt.type == "object" then
|
|
assert(pt.ref == obj1)
|
|
obj1:remove()
|
|
obj2:remove()
|
|
obj1 = nil -- object should be hit exactly one
|
|
end
|
|
end
|
|
assert(obj1 == nil)
|
|
end
|
|
unittests.register("test_entity_raycast", test_entity_raycast, {map=true})
|
|
|
|
local function test_object_iterator(pos, make_iterator)
|
|
local obj1 = core.add_entity(pos, "unittests:dummy")
|
|
local obj2 = core.add_entity(pos, "unittests:dummy")
|
|
assert(obj1 and obj2)
|
|
local found = false
|
|
-- As soon as we find one of the objects, we remove both, invalidating the other.
|
|
for obj in make_iterator() do
|
|
assert(obj:is_valid())
|
|
if obj == obj1 or obj == obj2 then
|
|
obj1:remove()
|
|
obj2:remove()
|
|
found = true
|
|
end
|
|
end
|
|
assert(found)
|
|
end
|
|
|
|
unittests.register("test_objects_inside_radius", function(_, pos)
|
|
test_object_iterator(pos, function()
|
|
return core.objects_inside_radius(pos, 1)
|
|
end)
|
|
end, {map=true})
|
|
|
|
unittests.register("test_objects_in_area", function(_, pos)
|
|
test_object_iterator(pos, function()
|
|
return core.objects_in_area(pos:offset(-1, -1, -1), pos:offset(1, 1, 1))
|
|
end)
|
|
end, {map=true})
|