mirror of
https://github.com/luanti-org/luanti.git
synced 2025-08-01 17:38:41 +00:00
Add API for custom (de)serializers
This commit is contained in:
parent
738b3cfadc
commit
2a1a2b1f9d
2 changed files with 61 additions and 24 deletions
|
@ -1,14 +1,28 @@
|
||||||
-- Registered metatables, used by the C++ packer
|
-- Registered metatables, used by the C++ packer
|
||||||
|
local serializable_metatables = {}
|
||||||
local known_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(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(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,
|
assert(known_metatables[name] == nil or known_metatables[name] == mt,
|
||||||
("attempt to override metatable %s"):format(name))
|
("attempt to override metatable %s"):format(name))
|
||||||
known_metatables[name] = mt
|
known_metatables[name] = mt
|
||||||
known_metatables[mt] = name
|
known_metatables[mt] = name
|
||||||
|
serializable_metatables[mt] = serializer
|
||||||
|
serializable_metatables[name] = deserializer
|
||||||
end
|
end
|
||||||
|
|
||||||
core.known_metatables = known_metatables
|
core.known_metatables = known_metatables
|
||||||
|
core.serializable_metatables = serializable_metatables
|
||||||
|
|
||||||
function core.register_async_metatable(...)
|
function core.register_async_metatable(...)
|
||||||
core.log("deprecated", "core.register_async_metatable is deprecated. " ..
|
core.log("deprecated", "core.register_async_metatable is deprecated. " ..
|
||||||
|
|
|
@ -16,6 +16,10 @@ local function is_itemstack(x)
|
||||||
return itemstack_mt and getmetatable(x) == itemstack_mt
|
return itemstack_mt and getmetatable(x) == itemstack_mt
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function pack_args(...)
|
||||||
|
return {n = select("#", ...), ...}
|
||||||
|
end
|
||||||
|
|
||||||
-- Recursively
|
-- Recursively
|
||||||
-- (1) reads metatables from tables;
|
-- (1) reads metatables from tables;
|
||||||
-- (2) counts occurrences of objects (non-primitives including strings) in a table.
|
-- (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
|
counts[val] = (count or 0) + 1
|
||||||
end
|
end
|
||||||
local mt = (not count) and (type_ == "table" or type_ == "userdata") and getmetatable(val)
|
local mt = (not count) and (type_ == "table" or type_ == "userdata") and getmetatable(val)
|
||||||
if mt and core.known_metatables[mt] then
|
if mt and core.serializable_metatables[mt] then
|
||||||
type_lookup[val] = core.known_metatables[mt]
|
local args = pack_args(core.known_metatables[mt], core.serializable_metatables[mt](val))
|
||||||
count_values(val, true)
|
type_lookup[val] = args
|
||||||
|
for _, v in ipairs(args) do
|
||||||
|
count_values(v, rawequal(v, val))
|
||||||
|
end
|
||||||
elseif type_ == "table" then
|
elseif type_ == "table" then
|
||||||
if recount or not count then
|
if recount or not count then
|
||||||
for k, v in pairs(val) do
|
for k, v in pairs(val) do
|
||||||
|
@ -87,9 +94,13 @@ local function serialize(value, write)
|
||||||
local references = {}
|
local references = {}
|
||||||
-- Circular tables that must be filled using `table[key] = value` statements
|
-- Circular tables that must be filled using `table[key] = value` statements
|
||||||
local to_fill = {}
|
local to_fill = {}
|
||||||
local counts, typenames = prepare_objects(value)
|
local counts, typeinfo = prepare_objects(value)
|
||||||
if next(typenames) then
|
if next(typeinfo) then
|
||||||
write "if not setmetatable then core={known_metatables={}}; setmetatable = function(x) return x end; end;"
|
write [[
|
||||||
|
if not (core and core.serializable_metatables) then
|
||||||
|
core = { known_metatables = {}, serializable_metatables = {}}
|
||||||
|
end;
|
||||||
|
]]
|
||||||
end
|
end
|
||||||
for object, count in pairs(counts) do
|
for object, count in pairs(counts) do
|
||||||
local type_ = type(object)
|
local type_ = type(object)
|
||||||
|
@ -123,7 +134,22 @@ local function serialize(value, write)
|
||||||
local function use_short_key(key)
|
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_]*$")
|
return not references[key] and type(key) == "string" and (not keywords[key]) and string_match(key, "^[%a_][%a%d_]*$")
|
||||||
end
|
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
|
-- Primitive types
|
||||||
if value == nil then
|
if value == nil then
|
||||||
return write("nil")
|
return write("nil")
|
||||||
|
@ -153,12 +179,8 @@ local function serialize(value, write)
|
||||||
write(ref)
|
write(ref)
|
||||||
return write"]"
|
return write"]"
|
||||||
end
|
end
|
||||||
if (not skip_mt) and typenames[value] then
|
if (not skip_mt) and typeinfo[value] then
|
||||||
write "setmetatable("
|
dump_serialized(value)
|
||||||
dump(value, true)
|
|
||||||
write ",core.known_metatables["
|
|
||||||
dump(typenames[value])
|
|
||||||
write "] or {})"
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if type_ == "string" then
|
if type_ == "string" then
|
||||||
|
@ -206,9 +228,8 @@ local function serialize(value, write)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- Write the statements to fill circular tables
|
-- Write the statements to fill circular tables
|
||||||
for table, ref in pairs(to_fill) do
|
for tbl, ref in pairs(to_fill) do
|
||||||
local typename = typenames[table]
|
for k, v in pairs(tbl) do
|
||||||
for k, v in pairs(table) do
|
|
||||||
write("_[")
|
write("_[")
|
||||||
write(ref)
|
write(ref)
|
||||||
write("]")
|
write("]")
|
||||||
|
@ -224,12 +245,12 @@ local function serialize(value, write)
|
||||||
dump(v)
|
dump(v)
|
||||||
write(";")
|
write(";")
|
||||||
end
|
end
|
||||||
if typename then
|
if typeinfo[tbl] then
|
||||||
write("setmetatable(_[")
|
write("_[")
|
||||||
write(ref)
|
write(ref)
|
||||||
write("],core.known_metatables[")
|
write("]=")
|
||||||
dump(typename)
|
dump_serialized(tbl)
|
||||||
write("] or {})")
|
write(";")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
write("return ")
|
write("return ")
|
||||||
|
@ -266,8 +287,10 @@ function core.deserialize(str, safe)
|
||||||
inf = math_huge,
|
inf = math_huge,
|
||||||
nan = 0/0,
|
nan = 0/0,
|
||||||
ItemStack = ItemStack or function(str) return str end,
|
ItemStack = ItemStack or function(str) return str end,
|
||||||
setmetatable = setmetatable,
|
core = {
|
||||||
core = { known_metatables = core.known_metatables }
|
known_metatables = core.known_metatables,
|
||||||
|
serializable_metatables = core.serializable_metatables,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
if safe then
|
if safe then
|
||||||
env.loadstring = dummy_func
|
env.loadstring = dummy_func
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue