diff --git a/polymer/eduke32/Makefile b/polymer/eduke32/Makefile index 56f79475d..60e31c6b6 100644 --- a/polymer/eduke32/Makefile +++ b/polymer/eduke32/Makefile @@ -163,6 +163,7 @@ ifneq (0,$(LUNATIC)) $(OBJ)/luaJIT_BC_dump.$o \ $(OBJ)/luaJIT_BC_dis_x86.$o \ $(OBJ)/luaJIT_BC_dis_x64.$o \ + $(OBJ)/luaJIT_BC_savegame.$o \ # TODO: remove debugging modules from release build # now, take care of having the necessary symbols (sector, wall, etc.) in the diff --git a/polymer/eduke32/build/include/build.h b/polymer/eduke32/build/include/build.h index 849741737..3b7438cab 100644 --- a/polymer/eduke32/build/include/build.h +++ b/polymer/eduke32/build/include/build.h @@ -43,6 +43,8 @@ enum rendmode_t { #ifdef LUNATIC # define NEW_MAP_FORMAT +// Merely a marker for LuaJIT C function callbacks: +# define LUNATIC_CB #else # ifdef NEW_MAP_FORMAT # error "New map format can only be used with Lunatic" diff --git a/polymer/eduke32/build/src/engine.c b/polymer/eduke32/build/src/engine.c index a5438155d..ac1b43a3e 100644 --- a/polymer/eduke32/build/src/engine.c +++ b/polymer/eduke32/build/src/engine.c @@ -9795,7 +9795,7 @@ static void check_sprite(int32_t i) #ifdef NEW_MAP_FORMAT // 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 // flags: 1, 2: former parameter "fromwhere" @@ -10501,7 +10501,7 @@ int32_t loadmaphack(const char *filename) #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 // diff --git a/polymer/eduke32/source/lunatic/bitar.lua b/polymer/eduke32/source/lunatic/bitar.lua index 5cb4a3d39..f429a8bd8 100644 --- a/polymer/eduke32/source/lunatic/bitar.lua +++ b/polymer/eduke32/source/lunatic/bitar.lua @@ -217,6 +217,8 @@ local mt = { __gc = function(s) anchor[tostring(s.arptr)] = nil end, + + __metatable = true, } ffi.metatype(bitar_ct, mt) diff --git a/polymer/eduke32/source/lunatic/control.lua b/polymer/eduke32/source/lunatic/control.lua index 071c103ed..b5628066c 100644 --- a/polymer/eduke32/source/lunatic/control.lua +++ b/polymer/eduke32/source/lunatic/control.lua @@ -11,6 +11,8 @@ local CF = CF local bit = require("bit") local io = require("io") local math = require("math") +local table = require("table") + local geom = require("geom") local bcheck = require("bcheck") local con_lang = require("con_lang") @@ -21,6 +23,7 @@ local setmetatable = setmetatable local assert = assert local error = error local ipairs = ipairs +local pairs = pairs local print = print local rawget = rawget local rawset = rawset @@ -38,6 +41,9 @@ local inside = dc.inside local sector, wall, sprite = dc.sector, dc.wall, dc.sprite local spritesofsect, spritesofstat = dc.spritesofsect, dc.spritesofstat +local OUR_NAME = "_con" +local OUR_REQUIRE_STRING = "local "..OUR_NAME.."=require'con'" + module(...) @@ -1762,6 +1768,19 @@ local function kopen4load(fn, searchfirst) 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 --- local function moddir_filename(cstr_fn) @@ -1907,7 +1926,19 @@ local gamearray_methods = { f:write(GAR_FOOTER) 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 = { @@ -1935,15 +1966,29 @@ local gamearray_mt = { end end, - __metatable = true, + __metatable = "serializeable", } -function _gamearray(size) - return setmetatable({ _size=size }, gamearray_mt) +-- Common constructor helper for gamearray and peractorvar. +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. +-- : optional, a table of =value +function _gamearray(size, values) + local gar = setmetatable({ _size=size }, gamearray_mt) + return set_values_from_table(gar, values) end ---- Exported functions --- +--- More functions of the official API --- -- Non-local control flow. These ones call the original error(), not our -- redefinition in defs.ilua. @@ -1957,12 +2002,29 @@ function killit() end --- Per-actor variable --- TODO: serialization +-- Per-actor variable. +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 = { __index = function(acv, idx) - check_sprite_idx(idx) - return acv._defval + if (type(idx)=="number") then + check_sprite_idx(idx) + return acv._defval + else + return peractorvar_methods[idx] + end end, __newindex = function(acv, idx, val) @@ -1981,9 +2043,12 @@ local peractorvar_mt = { end end, - __metatable = true, + __metatable = "serializeable", } -function peractorvar(initval) - return setmetatable({ _defval=initval }, peractorvar_mt) +-- : default value for per-actor variable. +-- : optional, a table of =value +function peractorvar(initval, values) + local acv = setmetatable({ _defval=initval }, peractorvar_mt) + return set_values_from_table(acv, values) end diff --git a/polymer/eduke32/source/lunatic/defs.ilua b/polymer/eduke32/source/lunatic/defs.ilua index 6187fb9c6..71c426fc4 100644 --- a/polymer/eduke32/source/lunatic/defs.ilua +++ b/polymer/eduke32/source/lunatic/defs.ilua @@ -570,6 +570,8 @@ int32_t g_elCONSize; char *g_elCON; void El_SetCON(const char *conluacode); +const char *(*El_SerializeGamevars)(int32_t *slenptr); + const char *s_buildRev; const char *g_sizes_of_what[]; int32_t g_sizes_of[]; @@ -1273,7 +1275,9 @@ do end -local package_loaded = {} -- false/true/table +---=== Module stuff ===--- + +local package_loaded = {} -- [] = false/true/table local modname_stack = {} -- []=string local module_gamevars = {} -- [] = { , , ... } @@ -1722,6 +1726,64 @@ do set_tweak_traceback_internal(tweak_traceback_msg) 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 "" 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_ -- NOTE: all references to global variables from this point on -- (also in functions created after this point) refer to G_ ! @@ -1839,7 +1901,9 @@ if (concode) then if (confunc == nil) then error("Failure loading translated CON code: "..conerrmsg, 0) end - confunc() + + package_loaded[CON_MODULE_NAME] = confunc() + module_gamevars[CON_MODULE_NAME] = { "A", "V" } -- See CON_GAMEVARS is lunacon.lua. end -- When starting a map, load Lua modules given on the command line. diff --git a/polymer/eduke32/source/lunatic/dynsymlist b/polymer/eduke32/source/lunatic/dynsymlist index dbc6c33f7..56b534db4 100644 --- a/polymer/eduke32/source/lunatic/dynsymlist +++ b/polymer/eduke32/source/lunatic/dynsymlist @@ -90,6 +90,8 @@ g_elCONSize; g_elCON; El_SetCON; +El_SerializeGamevars; + s_buildRev; g_sizes_of_what; g_sizes_of; @@ -163,6 +165,7 @@ luaJIT_BC_v; luaJIT_BC_dump; luaJIT_BC_dis_x86; luaJIT_BC_dis_x64; +luaJIT_BC_savegame; rand_jkiss_u32; rand_jkiss_dbl; diff --git a/polymer/eduke32/source/lunatic/lunacon.lua b/polymer/eduke32/source/lunatic/lunacon.lua index a2ef23159..0d219c789 100644 --- a/polymer/eduke32/source/lunatic/lunacon.lua +++ b/polymer/eduke32/source/lunatic/lunacon.lua @@ -3139,6 +3139,9 @@ end -- : Get line info? 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 lineinfo = lineinfop and get_lineinfo(flatcode) return table.concat(flatcode, "\n"), lineinfo diff --git a/polymer/eduke32/source/savegame.c b/polymer/eduke32/source/savegame.c index 846a5f589..9f58b96d1 100644 --- a/polymer/eduke32/source/savegame.c +++ b/polymer/eduke32/source/savegame.c @@ -25,6 +25,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "menus.h" // menutext #include "prlights.h" #include "savegame.h" +#ifdef LUNATIC +# include "lunatic_game.h" +static int32_t g_savedOK; +const char *g_failedVarname; +#endif extern char *bitptr; @@ -354,7 +359,12 @@ int32_t G_SavePlayer(int32_t spot) 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); } @@ -1205,6 +1215,15 @@ int32_t sv_saveandmakesnapshot(FILE *fil, int8_t spot, int8_t recdiffsp, int8_t { // savegame 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 { @@ -1313,6 +1332,11 @@ int32_t sv_loadsnapshot(int32_t fil, int32_t spot, savehead_t *h) savegame_comprthres = h->comprthres; +#ifdef LUNATIC + El_CreateGameState(); + G_PostCreateGameState(); +#endif + if (spot >= 0) { // savegame @@ -1650,6 +1674,10 @@ static void sv_restload() # define PRINTSIZE(name) do { } while (0) #endif +#ifdef LUNATIC +LUNATIC_CB const char *(*El_SerializeGamevars)(int32_t *slenptr); +#endif + static uint8_t *dosaveplayer2(FILE *fil, uint8_t *mem) { #ifdef DEBUGGINGAIDS @@ -1669,6 +1697,27 @@ static uint8_t *dosaveplayer2(FILE *fil, uint8_t *mem) Gv_WriteSave(fil, 1); // gamevars mem=writespecdata(svgm_vars, 0, mem); 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 return mem; @@ -1708,6 +1757,57 @@ static int32_t doloadplayer2(int32_t fil, uint8_t **memptr) } } 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 if (memptr)