mirror of
https://github.com/ZDoom/Raze.git
synced 2024-11-16 17:32:13 +00:00
Lunatic: fix restoring of tables. Also add savegame.lua which I forgot!
git-svn-id: https://svn.eduke32.com/eduke32@3807 1a8010ca-5511-0410-912e-c29ae57300e0
This commit is contained in:
parent
4781eecdb4
commit
e3a85f4083
4 changed files with 205 additions and 31 deletions
|
@ -1768,14 +1768,26 @@ local function kopen4load(fn, searchfirst)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- Common serialization function for gamearray and actorvar.
|
local function serialize_value(strtab, i, v)
|
||||||
local function serialize_array(ar, strtab)
|
-- Save only user values (i.e. not 'meta-fields' like '_size').
|
||||||
for i,v in pairs(ar) do
|
if (type(i)=="number" and v~=nil) then
|
||||||
if (type(i)=="number") then
|
strtab[#strtab+1] = "["..i.."]="..tostring(v)..","
|
||||||
assert(type(v)=="number") -- XXX: not enforced by public API
|
|
||||||
strtab[#strtab+1] = "["..i.."]="..tostring(v)..","
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Common serialization function for gamearray and actorvar.
|
||||||
|
local function serialize_array(ar, strtab, maxnum)
|
||||||
|
-- if (ffiC._DEBUG_LUNATIC ~= 0) then
|
||||||
|
-- Iterate in numeric order. XXX: also for non-debug?
|
||||||
|
for i=0,maxnum-1 do
|
||||||
|
serialize_value(strtab, i, rawget(ar, i))
|
||||||
|
end
|
||||||
|
-- else
|
||||||
|
-- for i,v in pairs(ar) do
|
||||||
|
-- serialize_value(strtab, i, v)
|
||||||
|
-- end
|
||||||
|
-- end
|
||||||
|
|
||||||
strtab[#strtab+1] = "})"
|
strtab[#strtab+1] = "})"
|
||||||
|
|
||||||
return table.concat(strtab)
|
return table.concat(strtab)
|
||||||
|
@ -1929,6 +1941,18 @@ local gamearray_methods = {
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
|
||||||
|
--- Internal routines ---
|
||||||
|
|
||||||
|
-- * All values equal to the default one (0) are cleared.
|
||||||
|
_cleanup = function(gar)
|
||||||
|
for i=0,gar._size-1 do
|
||||||
|
if (rawget(gar, i)==0) then
|
||||||
|
rawset(gar, i, nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
|
||||||
--- Serialization ---
|
--- Serialization ---
|
||||||
|
|
||||||
_get_require = function(gar)
|
_get_require = function(gar)
|
||||||
|
@ -1937,7 +1961,8 @@ local gamearray_methods = {
|
||||||
|
|
||||||
_serialize = function(gar)
|
_serialize = function(gar)
|
||||||
local strtab = { OUR_NAME..".actorvar(", tostring(gar._size), ",{" }
|
local strtab = { OUR_NAME..".actorvar(", tostring(gar._size), ",{" }
|
||||||
return serialize_array(gar, strtab)
|
gar:_cleanup()
|
||||||
|
return serialize_array(gar, strtab, gar._size)
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1956,16 +1981,6 @@ local gamearray_mt = {
|
||||||
rawset(gar, idx, val)
|
rawset(gar, idx, val)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
-- Calling a gamearray causes its cleanup:
|
|
||||||
-- * All values equal to the default one (0) are cleared.
|
|
||||||
__call = function(gar)
|
|
||||||
for i=0,gar._size-1 do
|
|
||||||
if (rawget(gar, i)==0) then
|
|
||||||
rawset(gar, i, nil)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
__metatable = "serializeable",
|
__metatable = "serializeable",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2004,6 +2019,19 @@ end
|
||||||
|
|
||||||
-- Per-actor variable.
|
-- Per-actor variable.
|
||||||
local actorvar_methods = {
|
local actorvar_methods = {
|
||||||
|
--- Internal routines ---
|
||||||
|
|
||||||
|
-- * All values for sprite not in the game world are cleared.
|
||||||
|
-- * All values equal to the default one are cleared.
|
||||||
|
_cleanup = function(acv)
|
||||||
|
for i=0,ffiC.MAXSPRITES-1 do
|
||||||
|
if (ffiC.sprite[i].statnum == ffiC.MAXSTATUS or rawget(acv, i)==acv._defval) then
|
||||||
|
rawset(acv, i, nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
|
||||||
--- Serialization ---
|
--- Serialization ---
|
||||||
|
|
||||||
_get_require = function(acv)
|
_get_require = function(acv)
|
||||||
|
@ -2012,7 +2040,10 @@ local actorvar_methods = {
|
||||||
|
|
||||||
_serialize = function(acv)
|
_serialize = function(acv)
|
||||||
local strtab = { OUR_NAME..".actorvar(", tostring(acv._defval), ",{" }
|
local strtab = { OUR_NAME..".actorvar(", tostring(acv._defval), ",{" }
|
||||||
return serialize_array(acv, strtab)
|
-- TODO: Must clean up sometime if not saving, too. (That is, what is
|
||||||
|
-- A_ResetVars() in the C-CON build.)
|
||||||
|
acv:_cleanup()
|
||||||
|
return serialize_array(acv, strtab, ffiC.MAXSPRITES)
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2032,17 +2063,6 @@ local actorvar_mt = {
|
||||||
rawset(acv, idx, val)
|
rawset(acv, idx, val)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
-- Calling a per-actor variable causes its cleanup:
|
|
||||||
-- * All values for sprite not in the game world are cleared.
|
|
||||||
-- * All values equal to the default one are cleared.
|
|
||||||
__call = function(acv)
|
|
||||||
for i=0,ffiC.MAXSPRITES-1 do
|
|
||||||
if (ffiC.sprite[i].statnum == ffiC.MAXSTATUS or rawget(acv, i)==acv._defval) then
|
|
||||||
rawset(acv, i, nil)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
__metatable = "serializeable",
|
__metatable = "serializeable",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -81,6 +81,8 @@ getticks;
|
||||||
gethitickms;
|
gethitickms;
|
||||||
OSD_Printf;
|
OSD_Printf;
|
||||||
|
|
||||||
|
crc32once;
|
||||||
|
|
||||||
luaJIT_BC_defs_common;
|
luaJIT_BC_defs_common;
|
||||||
luaJIT_BC_engine_maptext;
|
luaJIT_BC_engine_maptext;
|
||||||
|
|
||||||
|
@ -169,7 +171,6 @@ luaJIT_BC_savegame;
|
||||||
|
|
||||||
rand_jkiss_u32;
|
rand_jkiss_u32;
|
||||||
rand_jkiss_dbl;
|
rand_jkiss_dbl;
|
||||||
md4once;
|
|
||||||
|
|
||||||
A_IncurDamage;
|
A_IncurDamage;
|
||||||
G_CheckActivatorMotion;
|
G_CheckActivatorMotion;
|
||||||
|
|
|
@ -81,6 +81,8 @@ getticks;
|
||||||
gethitickms;
|
gethitickms;
|
||||||
OSD_Printf;
|
OSD_Printf;
|
||||||
|
|
||||||
|
crc32once;
|
||||||
|
|
||||||
luaJIT_BC_defs_common;
|
luaJIT_BC_defs_common;
|
||||||
luaJIT_BC_engine_maptext;
|
luaJIT_BC_engine_maptext;
|
||||||
|
|
||||||
|
|
151
polymer/eduke32/source/lunatic/savegame.lua
Normal file
151
polymer/eduke32/source/lunatic/savegame.lua
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
|
||||||
|
-- Savegame facilities for Lunatic.
|
||||||
|
|
||||||
|
local string = require("string")
|
||||||
|
local table = require("table")
|
||||||
|
|
||||||
|
local getmetatable = getmetatable
|
||||||
|
local pairs = pairs
|
||||||
|
local setmetatable = setmetatable
|
||||||
|
local tostring = tostring
|
||||||
|
local type = type
|
||||||
|
|
||||||
|
|
||||||
|
module(...)
|
||||||
|
|
||||||
|
|
||||||
|
---=== Serialization, based on the idea from PiL ===---
|
||||||
|
|
||||||
|
-- Serialize a 'primitive' Lua value.
|
||||||
|
local function basicSerialize(o)
|
||||||
|
-- Compare with sb_get_initial_strbuf() below.
|
||||||
|
-- XXX: nil?
|
||||||
|
if (type(o) == "number") then
|
||||||
|
-- NOTE: NaN and infinity handled.
|
||||||
|
return tostring(o)
|
||||||
|
elseif (type(o) == "boolean") then
|
||||||
|
return o and "t" or "f"
|
||||||
|
elseif (type(o) == "string") then
|
||||||
|
-- TODO: return refname if it's shorter
|
||||||
|
return string.format("%q", o)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function isSerializeable(obj)
|
||||||
|
return (getmetatable(obj)=="serializeable")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 'Save buffer' class. Objects of this class keep track of a string buffer
|
||||||
|
-- contiaining Lua code to recreate the values of particular variables from a
|
||||||
|
-- user Lunatic environment.
|
||||||
|
local savebuffer_mt = {
|
||||||
|
__index = {
|
||||||
|
-- Add an entry of Lua object <value> that can be referenced by
|
||||||
|
-- <refcode> (which should be Lua code that can appear on both sides of
|
||||||
|
-- an assignment).
|
||||||
|
-- Returns 'true' if <value> cannot be serialized.
|
||||||
|
add = function(self, refcode, value)
|
||||||
|
local valcode = basicSerialize(value)
|
||||||
|
local havetab = false
|
||||||
|
|
||||||
|
if (valcode == nil) then
|
||||||
|
-- <value> is a not a 'basic' Lua object, but one passed by
|
||||||
|
-- reference.
|
||||||
|
if (not self.val2ref[value]) then
|
||||||
|
-- Object is being serialized for the first time.
|
||||||
|
|
||||||
|
if (isSerializeable(value)) then
|
||||||
|
-- We have a serializeable object from Lunatic
|
||||||
|
-- (e.g. actorvar).
|
||||||
|
-- TODO: clean up (e.g. clear default values for actorvar).
|
||||||
|
|
||||||
|
-- First, get the code necessary to create this object,
|
||||||
|
-- usually 'require'ing a module into a local variable.
|
||||||
|
local reqcode = value:_get_require()
|
||||||
|
if (self.havereq[reqcode] == nil) then
|
||||||
|
self.havereq[reqcode] = true
|
||||||
|
self.strbuf[#self.strbuf+1] = reqcode
|
||||||
|
end
|
||||||
|
|
||||||
|
valcode = value:_serialize()
|
||||||
|
|
||||||
|
elseif (type(value)=="table") then
|
||||||
|
-- We have a Lua table.
|
||||||
|
havetab = true
|
||||||
|
|
||||||
|
-- Clear the table instead of creating a new one.
|
||||||
|
-- This way, local references to it don't become stale.
|
||||||
|
self:addrawf("ct(%s)", refcode)
|
||||||
|
|
||||||
|
for k,v in pairs(value) do
|
||||||
|
local keystr = basicSerialize(k)
|
||||||
|
if (keystr == nil) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
if (type(v)=="table" and not isSerializeable(v)) then
|
||||||
|
-- nested tables: NYI
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Generate the name under which the table element
|
||||||
|
-- is referenced.
|
||||||
|
local refcode2 = string.format("%s[%s]", refcode, keystr)
|
||||||
|
|
||||||
|
-- Recurse!
|
||||||
|
self:add(refcode2, v)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- We have anything else: can't serialize.
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
self.val2ref[value] = refcode
|
||||||
|
else
|
||||||
|
valcode = self.val2ref[value]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if (not havetab) then
|
||||||
|
self:addraw(refcode.."="..valcode)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- Add a single string to the buffer.
|
||||||
|
addraw = function(self, str)
|
||||||
|
self.strbuf[#self.strbuf+1] = str
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- Add a single formatted string to the buffer.
|
||||||
|
addrawf = function(self, fmt, ...)
|
||||||
|
self:addraw(string.format(fmt, ...))
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- Get the Lua code recreating the values as a string.
|
||||||
|
getcode = function(self)
|
||||||
|
self.strbuf[#self.strbuf+1] = "" -- add newline at EOS
|
||||||
|
return table.concat(self.strbuf, "\n")
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
local function sb_get_initial_strbuf()
|
||||||
|
return {
|
||||||
|
"local nan, inf = 0/0, 1/0",
|
||||||
|
"local t, f = true, false",
|
||||||
|
-- Clear table function:
|
||||||
|
"local function ct(t)",
|
||||||
|
" for k in pairs(t) do t[k]=nil end",
|
||||||
|
"end",
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Create a new savebuffer object.
|
||||||
|
function savebuffer()
|
||||||
|
-- .val2ref: [<Lua object>] = <Lua code string>
|
||||||
|
-- .havereq = [<string>] = true
|
||||||
|
-- .strbuf: array of Lua code pieces
|
||||||
|
local sb = { val2ref={}, havereq={}, strbuf=sb_get_initial_strbuf() }
|
||||||
|
return setmetatable(sb, savebuffer_mt)
|
||||||
|
end
|
Loading…
Reference in a new issue