Lua: Persistent variables

Add a way for the lua scripts to set and retrieve variables
that are persistent - saved and restored with the game.

Invalidates saves.
This commit is contained in:
Pasi Kallinen
2023-01-15 10:32:31 +02:00
parent 18cc99150a
commit 6abb12aee0
8 changed files with 194 additions and 1 deletions

View File

@@ -4,6 +4,15 @@
-- This file contains lua code used by NetHack core. -- This file contains lua code used by NetHack core.
-- Is it loaded once, at game start, and kept in memory until game exit. -- Is it loaded once, at game start, and kept in memory until game exit.
-- Data in nh_lua_variables table can be set and queried with nh.variable()
-- This table is saved and restored along with the game.
nh_lua_variables = {
};
-- wrapper to simplify calling from nethack core
function get_variables_string()
return "nh_lua_variables=" .. table_stringify(nh_lua_variables) .. ";";
end
-- This is an example of generating an external file during gameplay, -- This is an example of generating an external file during gameplay,
-- which is updated periodically. -- which is updated periodically.

View File

@@ -107,3 +107,35 @@ end
function pline(fmt, ...) function pline(fmt, ...)
nh.pline(string.format(fmt, table.unpack({...}))); nh.pline(string.format(fmt, table.unpack({...})));
end end
-- wrapper to make calling from nethack core easier
function nh_set_variables_string(key, tbl)
return "nh_lua_variables[\"" .. key .. "\"]=" .. table_stringify(tbl) .. ";";
end
-- wrapper to make calling from nethack core easier
function nh_get_variables_string(tbl)
return "return " .. table_stringify(tbl) .. ";";
end
-- return the (simple) table tbl converted into a string
function table_stringify(tbl)
local str = "";
for key, value in pairs(tbl) do
local typ = type(value);
if (typ == "table") then
str = str .. "[\"" .. key .. "\"]=" .. table_stringify(value);
elseif (typ == "string") then
str = str .. "[\"" .. key .. "\"]=[[" .. value .. "]]";
elseif (typ == "boolean") then
str = str .. "[\"" .. key .. "\"]=" .. tostring(value);
elseif (typ == "number") then
str = str .. "[\"" .. key .. "\"]=" .. value;
elseif (typ == "nil") then
str = str .. "[\"" .. key .. "\"]=nil";
end
str = str .. ",";
end
-- pline("table_stringify:(%s)", str);
return "{" .. str .. "}";
end

View File

@@ -376,6 +376,19 @@ Example:
nh.stop_timer_at({x=5,y=6}, "melt-ice"); nh.stop_timer_at({x=5,y=6}, "melt-ice");
=== variable
Set or get a global variable. These are persistent, saved and restored along with the game.
Supports only strings, booleans, numbers, or tables.
Example:
nh.variable("test", 10);
local ten = nh.variable("test");
nh.variable("tbl", { a = 1, b = "foo" });
local tbl = nh.variable("tbl");
=== verbalize === verbalize
Show the text in the message area as if someone said it, obeying eg. hero's deafness. Show the text in the message area as if someone said it, obeying eg. hero's deafness.

View File

@@ -1839,6 +1839,9 @@ extern void l_nhcore_call(int);
extern lua_State * nhl_init(nhl_sandbox_info *); extern lua_State * nhl_init(nhl_sandbox_info *);
extern void nhl_done(lua_State *); extern void nhl_done(lua_State *);
extern boolean nhl_loadlua(lua_State *, const char *); extern boolean nhl_loadlua(lua_State *, const char *);
extern char *get_nh_lua_variables(void);
extern void save_luadata(NHFILE *);
extern void restore_luadata(NHFILE *);
extern int nhl_pcall(lua_State *, int, int); extern int nhl_pcall(lua_State *, int, int);
extern boolean load_lua(const char *, nhl_sandbox_info *); extern boolean load_lua(const char *, nhl_sandbox_info *);
ATTRNORETURN extern void nhl_error(lua_State *, const char *) NORETURN; ATTRNORETURN extern void nhl_error(lua_State *, const char *) NORETURN;

View File

@@ -17,7 +17,7 @@
* Incrementing EDITLEVEL can be used to force invalidation of old bones * Incrementing EDITLEVEL can be used to force invalidation of old bones
* and save files. * and save files.
*/ */
#define EDITLEVEL 68 #define EDITLEVEL 69
/* /*
* Development status possibilities. * Development status possibilities.

View File

@@ -1030,6 +1030,139 @@ nhl_dnum_name(lua_State *L)
return 1; return 1;
} }
/* set or get variables which are saved and restored along the game.
nh.variable("test", 10);
local ten = nh.variable("test"); */
static int
nhl_variable(lua_State *L)
{
int argc = lua_gettop(L);
int typ;
const char *key;
if (!gl.luacore) {
nhl_error(L, "nh luacore not inited");
return 0;
}
lua_getglobal(gl.luacore, "nh_lua_variables");
if (!lua_istable(gl.luacore, -1)) {
impossible("nh_lua_variables is not a lua table");
return 0;
}
if (argc == 1) {
key = luaL_checkstring(L, 1);
lua_getfield(gl.luacore, -1, key);
typ = lua_type(gl.luacore, -1);
if (typ == LUA_TSTRING)
lua_pushstring(L, lua_tostring(gl.luacore, -1));
else if (typ == LUA_TNIL)
lua_pushnil(L);
else if (typ == LUA_TBOOLEAN)
lua_pushboolean(L, lua_toboolean(gl.luacore, -1));
else if (typ == LUA_TNUMBER)
lua_pushinteger(L, lua_tointeger(gl.luacore, -1));
else if (typ == LUA_TTABLE) {
lua_getglobal(gl.luacore, "nh_get_variables_string");
lua_pushvalue(gl.luacore, -2);
nhl_pcall(gl.luacore, 1, 1);
luaL_loadstring(L, lua_tostring(gl.luacore, -1));
nhl_pcall(L, 0, 1);
} else
nhl_error(L, "Cannot get variable of that type");
return 1;
} else if (argc == 2) {
/* set nh_lua_variables[key] = value;
nh.variable("key", value); */
key = luaL_checkstring(L, 1);
//pline("SETVAR:%s", key);
typ = lua_type(L, -1);
if (typ == LUA_TSTRING) {
lua_pushstring(gl.luacore, lua_tostring(L, -1));
lua_setfield(gl.luacore, -2, key);
} else if (typ == LUA_TNIL) {
lua_pushnil(gl.luacore);
lua_setfield(gl.luacore, -2, key);
} else if (typ == LUA_TBOOLEAN) {
lua_pushboolean(gl.luacore, lua_toboolean(L, -1));
lua_setfield(gl.luacore, -2, key);
} else if (typ == LUA_TNUMBER) {
lua_pushinteger(gl.luacore, lua_tointeger(L, -1));
lua_setfield(gl.luacore, -2, key);
} else if (typ == LUA_TTABLE) {
lua_getglobal(L, "nh_set_variables_string");
lua_pushstring(L, key);
lua_pushvalue(L, -3); /* copy value to top */
nhl_pcall(L, 2, 1);
luaL_loadstring(gl.luacore, lua_tostring(L, -1));
nhl_pcall(gl.luacore, 0, 0);
} else
nhl_error(L, "Cannot set variable of that type");
return 0;
} else
nhl_error(L, "Wrong number of arguments");
return 1;
}
/* return nh_lua_variable lua table as a string */
char *
get_nh_lua_variables(void)
{
char *key = NULL;
if (!gl.luacore) {
nhl_error(gl.luacore, "nh luacore not inited");
return key;
}
lua_getglobal(gl.luacore, "nh_lua_variables");
if (!lua_istable(gl.luacore, -1)) {
impossible("nh_lua_variables is not a lua table");
return key;
}
lua_getglobal(gl.luacore, "get_variables_string");
if (lua_type(gl.luacore, -1) == LUA_TFUNCTION) {
if (nhl_pcall(gl.luacore, 0, 1)) {
impossible("Lua error: %s", lua_tostring(gl.luacore, -1));
return key;
}
key = dupstr(lua_tostring(gl.luacore, -1));
}
return key;
}
/* save nh_lua_variables table to file */
void
save_luadata(NHFILE *nhfp)
{
char *lua_data = get_nh_lua_variables();
long lua_data_len = strlen(lua_data) + 1;
bwrite(nhfp->fd, (genericptr_t) &lua_data_len, sizeof lua_data_len);
bwrite(nhfp->fd, (genericptr_t) lua_data, strlen(lua_data) + 1);
free(lua_data);
}
/* restore nh_lua_variables table from file */
void
restore_luadata(NHFILE *nhfp)
{
long lua_data_len;
char *lua_data;
mread(nhfp->fd, (genericptr_t) &lua_data_len, sizeof lua_data_len);
lua_data = (char *) alloc(lua_data_len);
mread(nhfp->fd, (genericptr_t) lua_data, lua_data_len);
if (!gl.luacore)
l_nhcore_init();
luaL_loadstring(gl.luacore, lua_data);
nhl_pcall(gl.luacore, 0, 0);
}
/* local stairs = stairways(); */ /* local stairs = stairways(); */
static int static int
nhl_stairways(lua_State *L) nhl_stairways(lua_State *L)
@@ -1319,6 +1452,7 @@ static const struct luaL_Reg nhl_functions[] = {
{"dump_fmtstr", nhl_dump_fmtstr}, {"dump_fmtstr", nhl_dump_fmtstr},
#endif /* DUMPLOG */ #endif /* DUMPLOG */
{"dnum_name", nhl_dnum_name}, {"dnum_name", nhl_dnum_name},
{"variable", nhl_variable},
{"stairways", nhl_stairways}, {"stairways", nhl_stairways},
{"pushkey", nhl_pushkey}, {"pushkey", nhl_pushkey},
{"doturn", nhl_doturn}, {"doturn", nhl_doturn},

View File

@@ -688,6 +688,7 @@ restgamestate(NHFILE* nhfp, unsigned int* stuckid, unsigned int* steedid)
restnames(nhfp); restnames(nhfp);
restore_msghistory(nhfp); restore_msghistory(nhfp);
restore_gamelog(nhfp); restore_gamelog(nhfp);
restore_luadata(nhfp);
/* must come after all mons & objs are restored */ /* must come after all mons & objs are restored */
relink_timers(FALSE); relink_timers(FALSE);
relink_light_sources(FALSE); relink_light_sources(FALSE);

View File

@@ -344,6 +344,7 @@ savegamestate(NHFILE *nhfp)
savenames(nhfp); savenames(nhfp);
save_msghistory(nhfp); save_msghistory(nhfp);
save_gamelog(nhfp); save_gamelog(nhfp);
save_luadata(nhfp);
if (nhfp->structlevel) if (nhfp->structlevel)
bflush(nhfp->fd); bflush(nhfp->fd);
gp.program_state.saving--; gp.program_state.saving--;