Мы используем игровой движок Love2d Lua, который предоставляет графический API для Lua. Мы пытаемся сериализовать гигантскую хеш-таблицу, содержащую все данные сохранения для игрового мира. Этот хэш включает в себя некоторые функции, и некоторые из этих функций вызывают функции Love2d C.
Чтобы сериализовать функции в хеше, мы используем string.dump и загружаем их обратно с помощью loadstring. Это хорошо работает для чистых функций Lua, но когда мы пытаемся сериализовать, а затем загрузить обратно в функцию, которая вызывает обернутую функцию C, например, в API Love2d, loadstring возвращает nil.
Рассмотрим следующую простую программу, которая рисует «привет, мир» на экране с помощью графического движка Love2d:
function love.load()
draw = function()
love.graphics.print('hello, world', 10, 10)
end
end
function love.draw()
draw()
end
Мы хотели бы иметь возможность сделать это:
function love.load()
draw_before_serialize = function()
love.graphics.print('hello, world', 10, 10)
end
out = io.open("serialized.lua", "wb")
out:write('draw = load([[' .. string.dump(draw_before_serialize) .. ']])')
out:close()
require "serialized"
end
function love.draw()
draw()
end
Делая это, мы записываем в Lua-файл на диске, который содержит смесь некомпилированного байт-кода Lua и Lua, которая выглядит примерно так:
draw = load([[^[LJ^A^@
@main.lua2^@^@^B^@^B^@^D^E^B^B4^@^@^@%^A^A^@>^@^B^AG^@^A^@^Qhello, world
print^A^A^A^B^@^@]])
Этот метод отлично работает с функциями Lua, которые не вызывают модули C. Мы думаем, что проблема именно в этом, потому что этот пример работает:
function love.load()
draw_before_serialize = function()
print('hello, world')
end
out = io.open("serialized.lua", "wb")
out:write('draw = load([[' .. string.dump(draw_before_serialize) .. ']])')
out:close()
require "serialized"
end
function love.draw()
draw()
end
Вместо вызова графического метода Love2d он выводит на консоль.
После дополнительного тестирования мы были сбиты с толку, обнаружив, что этот пример действительно работает:
function love.load()
draw_before_serialize = function()
love.graphics.print('hello, world', 10, 10)
end
draw = load(string.dump(draw_before_serialize))
end
function love.draw()
draw()
end
Здесь мы на самом деле не записываем функцию на диск, а вместо этого просто выгружаем ее, а затем сразу же загружаем обратно. Мы подумали, что, возможно, виновата не запись данных с установленным флагом режима двоичной записи ( "wb"
), но, поскольку мы работаем в Linux, этот флаг не действует.
Есть идеи?