1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-07-27 17:28:41 +00:00

Add API for custom (de)serializers

This commit is contained in:
y5nw 2025-02-22 22:40:34 +01:00
parent 738b3cfadc
commit 2a1a2b1f9d
2 changed files with 61 additions and 24 deletions

View file

@ -1,14 +1,28 @@
-- Registered metatables, used by the C++ packer
local serializable_metatables = {}
local known_metatables = {}
function core.register_portable_metatable(name, mt)
local function dummy_serializer(x)
return x
end
function core.register_portable_metatable(name, mt, serializer, deserializer)
serializer = serializer or dummy_serializer
deserializer = deserializer or function(x) return setmetatable(x, mt) end
assert(type(name) == "string", ("attempt to use %s value as metatable name"):format(type(name)))
assert(type(mt) == "table", ("attempt to register a %s value as metatable"):format(type(mt)))
assert(type(serializer), ("attempt to use a %s value as serializer"):format(type(serializer)))
assert(type(deserializer), ("attempt to use a %s value as serialier"):format(type(deserializer)))
assert(known_metatables[name] == nil or known_metatables[name] == mt,
("attempt to override metatable %s"):format(name))
known_metatables[name] = mt
known_metatables[mt] = name
serializable_metatables[mt] = serializer
serializable_metatables[name] = deserializer
end
core.known_metatables = known_metatables
core.serializable_metatables = serializable_metatables
function core.register_async_metatable(...)
core.log("deprecated", "core.register_async_metatable is deprecated. " ..

View file

@ -16,6 +16,10 @@ local function is_itemstack(x)
return itemstack_mt and getmetatable(x) == itemstack_mt
end
local function pack_args(...)
return {n = select("#", ...), ...}
end
-- Recursively
-- (1) reads metatables from tables;
-- (2) counts occurrences of objects (non-primitives including strings) in a table.
@ -36,9 +40,12 @@ local function prepare_objects(value)
counts[val] = (count or 0) + 1
end
local mt = (not count) and (type_ == "table" or type_ == "userdata") and getmetatable(val)
if mt and core.known_metatables[mt] then
type_lookup[val] = core.known_metatables[mt]
count_values(val, true)
if mt and core.serializable_metatables[mt] then
local args = pack_args(core.known_metatables[mt], core.serializable_metatables[mt](val))
type_lookup[val] = args
for _, v in ipairs(args) do
count_values(v, rawequal(v, val))
end
elseif type_ == "table" then
if recount or not count then
for k, v in pairs(val) do
@ -87,9 +94,13 @@ local function serialize(value, write)
local references = {}
-- Circular tables that must be filled using `table[key] = value` statements
local to_fill = {}
local counts, typenames = prepare_objects(value)
if next(typenames) then
write "if not setmetatable then core={known_metatables={}}; setmetatable = function(x) return x end; end;"
local counts, typeinfo = prepare_objects(value)
if next(typeinfo) then
write [[
if not (core and core.serializable_metatables) then
core = { known_metatables = {}, serializable_metatables = {}}
end;
]]
end
for object, count in pairs(counts) do
local type_ = type(object)
@ -123,7 +134,22 @@ local function serialize(value, write)
local function use_short_key(key)
return not references[key] and type(key) == "string" and (not keywords[key]) and string_match(key, "^[%a_][%a%d_]*$")
end
local function dump(value, skip_mt)
local dump
local function dump_serialized(value)
local serialized = assert(typeinfo[value])
write "(core.serializable_metatables["
dump(serialized[1])
write "])("
for k = 2, serialized.n do
if k ~= 2 then
write ","
end
local v = serialized[k]
dump(v, rawequal(v, value))
end
write ")"
end
dump = function(value, skip_mt)
-- Primitive types
if value == nil then
return write("nil")
@ -153,12 +179,8 @@ local function serialize(value, write)
write(ref)
return write"]"
end
if (not skip_mt) and typenames[value] then
write "setmetatable("
dump(value, true)
write ",core.known_metatables["
dump(typenames[value])
write "] or {})"
if (not skip_mt) and typeinfo[value] then
dump_serialized(value)
return
end
if type_ == "string" then
@ -206,9 +228,8 @@ local function serialize(value, write)
end
end
-- Write the statements to fill circular tables
for table, ref in pairs(to_fill) do
local typename = typenames[table]
for k, v in pairs(table) do
for tbl, ref in pairs(to_fill) do
for k, v in pairs(tbl) do
write("_[")
write(ref)
write("]")
@ -224,12 +245,12 @@ local function serialize(value, write)
dump(v)
write(";")
end
if typename then
write("setmetatable(_[")
if typeinfo[tbl] then
write("_[")
write(ref)
write("],core.known_metatables[")
dump(typename)
write("] or {})")
write("]=")
dump_serialized(tbl)
write(";")
end
end
write("return ")
@ -266,8 +287,10 @@ function core.deserialize(str, safe)
inf = math_huge,
nan = 0/0,
ItemStack = ItemStack or function(str) return str end,
setmetatable = setmetatable,
core = { known_metatables = core.known_metatables }
core = {
known_metatables = core.known_metatables,
serializable_metatables = core.serializable_metatables,
},
}
if safe then
env.loadstring = dummy_func