Lunatic: prototypical gamevar serialization, currently very CON-centric.

git-svn-id: https://svn.eduke32.com/eduke32@3796 1a8010ca-5511-0410-912e-c29ae57300e0
This commit is contained in:
helixhorned 2013-05-20 19:31:42 +00:00
parent f6ea669613
commit 0d79372216
9 changed files with 257 additions and 17 deletions

View file

@ -163,6 +163,7 @@ ifneq (0,$(LUNATIC))
$(OBJ)/luaJIT_BC_dump.$o \ $(OBJ)/luaJIT_BC_dump.$o \
$(OBJ)/luaJIT_BC_dis_x86.$o \ $(OBJ)/luaJIT_BC_dis_x86.$o \
$(OBJ)/luaJIT_BC_dis_x64.$o \ $(OBJ)/luaJIT_BC_dis_x64.$o \
$(OBJ)/luaJIT_BC_savegame.$o \
# TODO: remove debugging modules from release build # TODO: remove debugging modules from release build
# now, take care of having the necessary symbols (sector, wall, etc.) in the # now, take care of having the necessary symbols (sector, wall, etc.) in the

View file

@ -43,6 +43,8 @@ enum rendmode_t {
#ifdef LUNATIC #ifdef LUNATIC
# define NEW_MAP_FORMAT # define NEW_MAP_FORMAT
// Merely a marker for LuaJIT C function callbacks:
# define LUNATIC_CB
#else #else
# ifdef NEW_MAP_FORMAT # ifdef NEW_MAP_FORMAT
# error "New map format can only be used with Lunatic" # error "New map format can only be used with Lunatic"

View file

@ -9795,7 +9795,7 @@ static void check_sprite(int32_t i)
#ifdef NEW_MAP_FORMAT #ifdef NEW_MAP_FORMAT
// Returns the number of sprites, or <0 on error. // Returns the number of sprites, or <0 on error.
int32_t (*loadboard_maptext)(int32_t fil, vec3_t *dapos, int16_t *daang, int16_t *dacursectnum); LUNATIC_CB int32_t (*loadboard_maptext)(int32_t fil, vec3_t *dapos, int16_t *daang, int16_t *dacursectnum);
#endif #endif
// flags: 1, 2: former parameter "fromwhere" // flags: 1, 2: former parameter "fromwhere"
@ -10501,7 +10501,7 @@ int32_t loadmaphack(const char *filename)
#ifdef NEW_MAP_FORMAT #ifdef NEW_MAP_FORMAT
int32_t (*saveboard_maptext)(const char *filename, const vec3_t *dapos, int16_t daang, int16_t dacursectnum); LUNATIC_CB int32_t (*saveboard_maptext)(const char *filename, const vec3_t *dapos, int16_t daang, int16_t dacursectnum);
#endif #endif
// //

View file

@ -217,6 +217,8 @@ local mt = {
__gc = function(s) __gc = function(s)
anchor[tostring(s.arptr)] = nil anchor[tostring(s.arptr)] = nil
end, end,
__metatable = true,
} }
ffi.metatype(bitar_ct, mt) ffi.metatype(bitar_ct, mt)

View file

@ -11,6 +11,8 @@ local CF = CF
local bit = require("bit") local bit = require("bit")
local io = require("io") local io = require("io")
local math = require("math") local math = require("math")
local table = require("table")
local geom = require("geom") local geom = require("geom")
local bcheck = require("bcheck") local bcheck = require("bcheck")
local con_lang = require("con_lang") local con_lang = require("con_lang")
@ -21,6 +23,7 @@ local setmetatable = setmetatable
local assert = assert local assert = assert
local error = error local error = error
local ipairs = ipairs local ipairs = ipairs
local pairs = pairs
local print = print local print = print
local rawget = rawget local rawget = rawget
local rawset = rawset local rawset = rawset
@ -38,6 +41,9 @@ local inside = dc.inside
local sector, wall, sprite = dc.sector, dc.wall, dc.sprite local sector, wall, sprite = dc.sector, dc.wall, dc.sprite
local spritesofsect, spritesofstat = dc.spritesofsect, dc.spritesofstat local spritesofsect, spritesofstat = dc.spritesofsect, dc.spritesofstat
local OUR_NAME = "_con"
local OUR_REQUIRE_STRING = "local "..OUR_NAME.."=require'con'"
module(...) module(...)
@ -1762,6 +1768,19 @@ local function kopen4load(fn, searchfirst)
end end
-- Common serialization function for gamearray and peractorvar.
local function serialize_array(ar, strtab)
for i,v in pairs(ar) do
if (type(i)=="number") then
assert(type(v)=="number") -- XXX: not enforced by public API
strtab[#strtab+1] = "["..i.."]="..tostring(v)..","
end
end
strtab[#strtab+1] = "})"
return table.concat(strtab)
end
--- Game arrays --- --- Game arrays ---
local function moddir_filename(cstr_fn) local function moddir_filename(cstr_fn)
@ -1907,7 +1926,19 @@ local gamearray_methods = {
f:write(GAR_FOOTER) f:write(GAR_FOOTER)
f:close() f:close()
end end,
--- Serialization ---
_get_require = function(gar)
return OUR_REQUIRE_STRING
end,
_serialize = function(gar)
local strtab = { OUR_NAME..".peractorvar(", tostring(gar._size), ",{" }
return serialize_array(gar, strtab)
end,
} }
local gamearray_mt = { local gamearray_mt = {
@ -1935,15 +1966,29 @@ local gamearray_mt = {
end end
end, end,
__metatable = true, __metatable = "serializeable",
} }
function _gamearray(size) -- Common constructor helper for gamearray and peractorvar.
return setmetatable({ _size=size }, gamearray_mt) local function set_values_from_table(ar, values)
if (values ~= nil) then
for i,v in pairs(values) do
ar[i] = v
end
end
return ar
end
-- NOTE: Gamearrays are internal because users are encouraged to use tables
-- from Lua code.
-- <values>: optional, a table of <index>=value
function _gamearray(size, values)
local gar = setmetatable({ _size=size }, gamearray_mt)
return set_values_from_table(gar, values)
end end
--- Exported functions --- --- More functions of the official API ---
-- Non-local control flow. These ones call the original error(), not our -- Non-local control flow. These ones call the original error(), not our
-- redefinition in defs.ilua. -- redefinition in defs.ilua.
@ -1957,12 +2002,29 @@ function killit()
end end
-- Per-actor variable -- Per-actor variable.
-- TODO: serialization local peractorvar_methods = {
--- Serialization ---
_get_require = function(acv)
return OUR_REQUIRE_STRING
end,
_serialize = function(acv)
local strtab = { OUR_NAME..".peractorvar(", tostring(acv._defval), ",{" }
return serialize_array(acv, strtab)
end,
}
-- XXX: How about types other than numbers?
local peractorvar_mt = { local peractorvar_mt = {
__index = function(acv, idx) __index = function(acv, idx)
check_sprite_idx(idx) if (type(idx)=="number") then
return acv._defval check_sprite_idx(idx)
return acv._defval
else
return peractorvar_methods[idx]
end
end, end,
__newindex = function(acv, idx, val) __newindex = function(acv, idx, val)
@ -1981,9 +2043,12 @@ local peractorvar_mt = {
end end
end, end,
__metatable = true, __metatable = "serializeable",
} }
function peractorvar(initval) -- <initval>: default value for per-actor variable.
return setmetatable({ _defval=initval }, peractorvar_mt) -- <values>: optional, a table of <spritenum>=value
function peractorvar(initval, values)
local acv = setmetatable({ _defval=initval }, peractorvar_mt)
return set_values_from_table(acv, values)
end end

View file

@ -570,6 +570,8 @@ int32_t g_elCONSize;
char *g_elCON; char *g_elCON;
void El_SetCON(const char *conluacode); void El_SetCON(const char *conluacode);
const char *(*El_SerializeGamevars)(int32_t *slenptr);
const char *s_buildRev; const char *s_buildRev;
const char *g_sizes_of_what[]; const char *g_sizes_of_what[];
int32_t g_sizes_of[]; int32_t g_sizes_of[];
@ -1273,7 +1275,9 @@ do
end end
local package_loaded = {} -- false/true/table ---=== Module stuff ===---
local package_loaded = {} -- [<modname>] = false/true/table
local modname_stack = {} -- [<depth>]=string local modname_stack = {} -- [<depth>]=string
local module_gamevars = {} -- [<modname>] = { <gvname1>, <gvname2>, ... } local module_gamevars = {} -- [<modname>] = { <gvname1>, <gvname2>, ... }
@ -1722,6 +1726,64 @@ do
set_tweak_traceback_internal(tweak_traceback_msg) set_tweak_traceback_internal(tweak_traceback_msg)
end end
-- XXX: May still be require'd from user code, we don't want that (at least not
-- under this name).
local CON_MODULE_NAME = "_CON\0"
-- Set up Lunatic gamevar serialization.
do
local savegame = require("savegame")
-- Callback for: const char *(int32_t *slenptr);
ffiC.El_SerializeGamevars = function(slenptr)
local sb = savegame.savebuffer()
sb:addraw("local M")
-- XXX: System gamevars?
for modname, modvars in pairs(module_gamevars) do
sb:addrawf("M=require%q", modname)
for i=1,#modvars do
local varname = modvars[i]
-- Serialize gamevar named 'varname' from module named 'modname'.
-- XXX: May error. This will terminate EDuke32 since this callback
-- is run unprotected.
if (sb:add("M."..varname, package_loaded[modname][varname])) then
-- We couldn't serialize that gamevar.
slenptr[0] = -1
-- Signal which gamevar that was.
return (modname==CON_MODULE_NAME and "<CON>" or modname).."."..varname
end
end
end
-- Get the whole code as a string.
local savecode = sb:getcode()
if (ffiC._DEBUG_LUNATIC ~= 0) then
-- Dump the code if Lunatic debugging is enabled and there is a
-- LUNATIC_SAVECODE_FN variable in the environment.
local os = require("os")
local fn = os.getenv("LUNATIC_SAVECODE_FN")
if (fn ~= nil) then
local io = require("io")
local f = io.open(fn, "w")
if (f ~= nil) then
f:write(savecode)
f:close()
printf("Wrote Lunatic gamevar restoration code to \"%s\".", fn)
end
end
end
-- Set the size of the code and return the code to C.
slenptr[0] = #savecode
return savecode
end
end
-- change the environment of this chunk to the table G_ -- change the environment of this chunk to the table G_
-- NOTE: all references to global variables from this point on -- NOTE: all references to global variables from this point on
-- (also in functions created after this point) refer to G_ ! -- (also in functions created after this point) refer to G_ !
@ -1839,7 +1901,9 @@ if (concode) then
if (confunc == nil) then if (confunc == nil) then
error("Failure loading translated CON code: "..conerrmsg, 0) error("Failure loading translated CON code: "..conerrmsg, 0)
end end
confunc()
package_loaded[CON_MODULE_NAME] = confunc()
module_gamevars[CON_MODULE_NAME] = { "A", "V" } -- See CON_GAMEVARS is lunacon.lua.
end end
-- When starting a map, load Lua modules given on the command line. -- When starting a map, load Lua modules given on the command line.

View file

@ -90,6 +90,8 @@ g_elCONSize;
g_elCON; g_elCON;
El_SetCON; El_SetCON;
El_SerializeGamevars;
s_buildRev; s_buildRev;
g_sizes_of_what; g_sizes_of_what;
g_sizes_of; g_sizes_of;
@ -163,6 +165,7 @@ luaJIT_BC_v;
luaJIT_BC_dump; luaJIT_BC_dump;
luaJIT_BC_dis_x86; luaJIT_BC_dis_x86;
luaJIT_BC_dis_x64; luaJIT_BC_dis_x64;
luaJIT_BC_savegame;
rand_jkiss_u32; rand_jkiss_u32;
rand_jkiss_dbl; rand_jkiss_dbl;

View file

@ -3139,6 +3139,9 @@ end
-- <lineinfop>: Get line info? -- <lineinfop>: Get line info?
local function get_code_string(codetab, lineinfop) local function get_code_string(codetab, lineinfop)
-- Finalize translated code: return table containing gamevar and gamearray
-- tables. CON_GAMEVARS.
codetab[#codetab+1] = "return { V=_V, A=_A }"
local flatcode = flatten_codetab(codetab) local flatcode = flatten_codetab(codetab)
local lineinfo = lineinfop and get_lineinfo(flatcode) local lineinfo = lineinfop and get_lineinfo(flatcode)
return table.concat(flatcode, "\n"), lineinfo return table.concat(flatcode, "\n"), lineinfo

View file

@ -25,6 +25,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "menus.h" // menutext #include "menus.h" // menutext
#include "prlights.h" #include "prlights.h"
#include "savegame.h" #include "savegame.h"
#ifdef LUNATIC
# include "lunatic_game.h"
static int32_t g_savedOK;
const char *g_failedVarname;
#endif
extern char *bitptr; extern char *bitptr;
@ -354,7 +359,12 @@ int32_t G_SavePlayer(int32_t spot)
if (!g_netServer && ud.multimode < 2) if (!g_netServer && ud.multimode < 2)
{ {
Bstrcpy(ScriptQuotes[QUOTE_RESERVED4], "Game Saved"); #ifdef LUNATIC
if (!g_savedOK)
Bstrcpy(ScriptQuotes[QUOTE_RESERVED4], "^10Failed Saving Game");
else
#endif
Bstrcpy(ScriptQuotes[QUOTE_RESERVED4], "Game Saved");
P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps); P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps);
} }
@ -1205,6 +1215,15 @@ int32_t sv_saveandmakesnapshot(FILE *fil, int8_t spot, int8_t recdiffsp, int8_t
{ {
// savegame // savegame
dosaveplayer2(fil, NULL); dosaveplayer2(fil, NULL);
#ifdef LUNATIC
if (!g_savedOK)
{
OSD_Printf("sv_saveandmakesnapshot: failed serializing Lunatic gamevar %s.\n",
g_failedVarname);
g_failedVarname = NULL;
return 1;
}
#endif
} }
else else
{ {
@ -1313,6 +1332,11 @@ int32_t sv_loadsnapshot(int32_t fil, int32_t spot, savehead_t *h)
savegame_comprthres = h->comprthres; savegame_comprthres = h->comprthres;
#ifdef LUNATIC
El_CreateGameState();
G_PostCreateGameState();
#endif
if (spot >= 0) if (spot >= 0)
{ {
// savegame // savegame
@ -1650,6 +1674,10 @@ static void sv_restload()
# define PRINTSIZE(name) do { } while (0) # define PRINTSIZE(name) do { } while (0)
#endif #endif
#ifdef LUNATIC
LUNATIC_CB const char *(*El_SerializeGamevars)(int32_t *slenptr);
#endif
static uint8_t *dosaveplayer2(FILE *fil, uint8_t *mem) static uint8_t *dosaveplayer2(FILE *fil, uint8_t *mem)
{ {
#ifdef DEBUGGINGAIDS #ifdef DEBUGGINGAIDS
@ -1669,6 +1697,27 @@ static uint8_t *dosaveplayer2(FILE *fil, uint8_t *mem)
Gv_WriteSave(fil, 1); // gamevars Gv_WriteSave(fil, 1); // gamevars
mem=writespecdata(svgm_vars, 0, mem); mem=writespecdata(svgm_vars, 0, mem);
PRINTSIZE("vars"); PRINTSIZE("vars");
#else
{
int32_t slen, slen_ext;
const char *svcode = El_SerializeGamevars(&slen);
if (slen < 0)
{
// Serialization failed.
g_savedOK = 0;
g_failedVarname = svcode;
return mem;
}
// TODO: compress text.
fwrite("\0\1LunaGVAR\3\4", 12, 1, fil);
slen_ext = B_LITTLE32(slen);
fwrite(&slen_ext, sizeof(slen_ext), 1, fil);
fwrite(svcode, slen, 1, fil);
g_savedOK = 1;
}
#endif #endif
return mem; return mem;
@ -1708,6 +1757,57 @@ static int32_t doloadplayer2(int32_t fil, uint8_t **memptr)
} }
} }
PRINTSIZE("vars"); PRINTSIZE("vars");
#else
{
// Read Lua code to restore gamevar values from the savegame and run it.
char header[12];
int32_t slen;
if (kread(fil, header, 12) != 12)
{
OSD_Printf("doloadplayer2: failed reading Lunatic gamevar header.\n");
return -100;
}
if (Bmemcmp(header, "\0\1LunaGVAR\3\4", 12))
{
OSD_Printf("doloadplayer2: Lunatic gamevar header doesn't match.\n");
return -101;
}
if (kread(fil, &slen, sizeof(slen)) != sizeof(slen))
{
OSD_Printf("doloadplayer2: failed reading Lunatic gamevar string size.\n");
return -102;
}
slen = B_LITTLE32(slen);
if (slen < 0)
{
OSD_Printf("doloadplayer2: invalid Lunatic gamevar string size %d.\n", slen);
return -103;
}
if (slen > 0)
{
char *svcode = Bmalloc(slen);
if (svcode == NULL)
G_GameExit("OUT OF MEMORY in doloadplayer2().");
if (kread(fil, svcode, slen) != slen)
{
OSD_Printf("doloadplayer2: failed reading Lunatic gamevar restoration code.\n");
return -104;
}
if (L_RunString(&g_ElState, svcode, 0, slen, "luaload"))
{
OSD_Printf("doloadplayer2: failed restoring Lunatic gamevars.\n");
return -105;
}
}
}
#endif #endif
if (memptr) if (memptr)