Merge pull request 'ENTITIES/mcl_item_entity: Fix non-serializable item entity unload crash' (#132) from fix-overlong-meta-item-crash-2 into master

Reviewed-on: https://git.minetest.land/Mineclonia/Mineclonia/pulls/132
Reviewed-by: cora <cora@noreply.git.minetest.land>
This commit is contained in:
cora 2021-08-01 01:23:43 +00:00
commit 3cd4ad5591
1 changed files with 61 additions and 1 deletions

View File

@ -1,3 +1,4 @@
local S = minetest.get_translator("mcl_item_entity")
--basic settings
local item_drop_settings = {} --settings table
item_drop_settings.age = 1.0 --how old a dropped item (_insta_collect==false) has to be before collecting
@ -448,7 +449,7 @@ minetest.register_entity(":__builtin:item", {
end,
get_staticdata = function(self)
return minetest.serialize({
local data = minetest.serialize({
itemstring = self.itemstring,
always_collect = self.always_collect,
age = self.age,
@ -456,6 +457,39 @@ minetest.register_entity(":__builtin:item", {
_flowing = self._flowing,
_removed = self._removed,
})
-- sfan5 guessed that the biggest serializable item
-- entity would have a size of 65530 bytes. This has
-- been experimentally verified to be still too large.
--
-- anon5 has calculated that the biggest serializable
-- item entity has a size of exactly 65487 bytes:
--
-- 1. serializeString16 can handle max. 65535 bytes.
-- 2. The following engine metadata is always saved:
-- • 1 byte (version)
-- • 2 byte (length prefix)
-- • 14 byte “__builtin:item”
-- • 4 byte (length prefix)
-- • 2 byte (health)
-- • 3 × 4 byte = 12 byte (position)
-- • 4 byte (yaw)
-- • 1 byte (version 2)
-- • 2 × 4 byte = 8 byte (pitch and roll)
-- 3. This leaves 65487 bytes for the serialization.
if #data > 65487 then -- would crash the engine
local stack = ItemStack(self.itemstring)
stack:get_meta():from_table(nil)
self.itemstring = stack:to_string()
minetest.log(
"warning",
"Overlong item entity metadata removed: “" ..
self.itemstring ..
"” had serialized length of " ..
#data
)
return self:get_staticdata()
end
return data
end,
on_activate = function(self, staticdata, dtime_s)
@ -748,3 +782,29 @@ minetest.register_entity(":__builtin:item", {
-- Note: on_punch intentionally left out. The player should *not* be able to collect items by punching
})
-- The “getwrittenbook” command was added as a debug aid. It can help
-- reproducing situations in which items with lots of metadata trigger
-- issues like heavy lag or server crashes. Do not remove this command
-- unless another easy way of getting items with overlong meta exists!
--
-- “/getwrittenbook 65323” creates an item that creates the largest
-- possible serializable written book item entity when dropped.
--
-- “/getwrittenbook 65324” creates an item that creates the smallest
-- possible non-serializable written book item entity when dropped.
minetest.register_chatcommand("getwrittenbook", {
params = S("<Count>"),
description = S("Get a written book with a configurable amount of characters."),
privs = {debug=true},
func = function(name, param)
local count = tonumber(param)
local itemstack = ItemStack("mcl_books:written_book")
local meta = itemstack:get_meta()
meta:set_string("description", "")
meta:set_string("text", string.rep("x", count))
local player = minetest.get_player_by_name(name)
local inv = player:get_inventory()
inv:add_item("main", itemstack)
end
})