Lua: nhcore script with function callbacks

Adds possible callbacks for "start_new_game", "restore_old_game",
"moveloop_turn", and "game_exit" which when defined, will be called
from core code at the appropriate time.

Adds lua hooks for dump_fmtstr (only if DUMPLOG), dnum_name, u.moves,
u.uhave_amulet, and u.depth.
This commit is contained in:
Pasi Kallinen
2021-05-21 21:24:59 +03:00
parent 0e9bf2e03c
commit 29868036f1
12 changed files with 248 additions and 2 deletions
+74
View File
@@ -0,0 +1,74 @@
-- This file contains lua code used by NetHack core.
-- Is it loaded once, at game start, and kept in memory until game exit.
-- This is an example of generating an external file during gameplay,
-- which is updated periodically.
-- Intended for public servers using dgamelaunch as their login manager.
local prev_dgl_extrainfo = 0;
function mk_dgl_extrainfo()
if ((prev_dgl_extrainfo == 0) or (prev_dgl_extrainfo + 50 < u.moves)) then
local filename = nh.dump_fmtstr("/tmp/nethack.%n.%d.log");
local extrai, err = io.open(filename, "w");
if extrai then
local sortval = 0;
local dname = nh.dnum_name(u.dnum);
local dstr = "";
local astr = " ";
if u.uhave_amulet == 1 then
sortval = sortval + 1024;
astr = "A";
end
if dname == "Fort Ludios" then
dstr = "Knx";
sortval = sortval + 245;
elseif dname == "The Quest" then
dstr = "Q" .. u.dlevel;
sortval = sortval + 250 + u.dlevel;
elseif dname == "The Elemental Planes" then
dstr = "End";
sortval = sortval + 256;
elseif dname == "Vlad's Tower" then
dstr = "T" .. u.dlevel;
sortval = sortval + 235 + u.depth;
elseif dname == "Sokoban" then
dstr = "S" .. u.dlevel;
sortval = sortval + 225 + u.depth;
elseif dname == "The Gnomish Mines" then
dstr = "M" .. u.dlevel;
sortval = sortval + 215 + u.dlevel;
else
dstr = "D" .. u.depth;
sortval = sortval + u.depth;
end
local str = sortval .. "|" .. astr .. " " .. dstr;
extrai:write(str);
extrai:close();
else
-- failed to open the file.
nh.pline("Failed to open dgl extrainfo file: " .. err);
end
prev_dgl_extrainfo = u.moves;
end
end
-- Callback functions
nhcore = {
-- start_new_game called once, when starting a new game
-- after "Welcome to NetHack" message has been given.
-- start_new_game = function() nh.pline("NEW GAME!"); end,
-- restore_old_game called once, when restoring a saved game
-- after "Welcome back to NetHack" message has been given.
-- restore_old_game = function() nh.pline("RESTORED OLD GAME!"); end,
-- moveloop_turn is called once per turn.
-- moveloop_turn = mk_dgl_extrainfo,
-- game_exit is called when the game exits (quit, saved, ...)
-- game_exit = function() end,
};
+1
View File
@@ -890,6 +890,7 @@ split off some of the functionality that was in makedefs (compiled-in options
and accessed on the target platform and accessed on the target platform
replace quest.txt and associated conversion to quest.dat via makedefs with replace quest.txt and associated conversion to quest.dat via makedefs with
Lua quest texts loaded at runtime Lua quest texts loaded at runtime
callback lua functions from core at certain game actions
some altars are displayed in different colors (for tty and curses at least) some altars are displayed in different colors (for tty and curses at least)
add 'quick_farsight' option to provide some control over random clairvoyance add 'quick_farsight' option to provide some control over random clairvoyance
where pausing to be able to browse temporarily visible aspects of the where pausing to be able to browse temporarily visible aspects of the
+29
View File
@@ -14,6 +14,35 @@ Example:
local str = nh.an("unicorn"); local str = nh.an("unicorn");
=== dnum_name
Returns the full dungeon name (as defined in dungeon.lua) for the dungeon
number given as parameter.
Example:
local dungeon_name = nh.dnum_name(u.dnum);
=== dump_fmtstr
Returns a string replacing special format chars with game data.
Only available if NetHack was compiled with DUMPLOG.
|===
| %% | literal '%'
| %t | game start, timestamp
| %T | current time, timestamp
| %d | game start, YYYYMMDDhhmmss
| %D | current time, YYYYMMDDhhmmss
| %v | game version, eg. '3.7.0-0'
| %u | UID
| %n | player name
| %N | first character of player name
|===
Example:
local filename = nh.dump_fmtstr("/tmp/nethack.%n.%d.log");
=== getlin === getlin
+3
View File
@@ -995,6 +995,9 @@ struct instance_globals {
int lusername_size; int lusername_size;
#endif #endif
/* nhlua.c */
genericptr_t luacore; /* lua_State * */
/* o_init.c */ /* o_init.c */
short disco[NUM_OBJECTS]; short disco[NUM_OBJECTS];
+3
View File
@@ -1657,6 +1657,9 @@ extern int l_obj_register(lua_State *);
/* ### nhlua.c ### */ /* ### nhlua.c ### */
#if !defined(CROSSCOMPILE) || defined(CROSSCOMPILE_TARGET) #if !defined(CROSSCOMPILE) || defined(CROSSCOMPILE_TARGET)
extern void l_nhcore_init(void);
extern void l_nhcore_done(void);
extern void l_nhcore_call(int);
extern lua_State * nhl_init(void); extern lua_State * nhl_init(void);
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 *);
+10
View File
@@ -421,6 +421,16 @@ typedef struct sortloot_item Loot;
#define SUPPRESS_HISTORY 4 #define SUPPRESS_HISTORY 4
#define URGENT_MESSAGE 8 #define URGENT_MESSAGE 8
/* Lua callback functions */
enum nhcore_calls {
NHCORE_START_NEW_GAME = 0,
NHCORE_RESTORE_OLD_GAME,
NHCORE_MOVELOOP_TURN,
NHCORE_GAME_EXIT,
NUM_NHCORE_CALLS
};
/* Macros for messages referring to hands, eyes, feet, etc... */ /* Macros for messages referring to hands, eyes, feet, etc... */
enum bodypart_types { enum bodypart_types {
ARM = 0, ARM = 0,
+6
View File
@@ -201,6 +201,8 @@ moveloop(boolean resuming)
/* once-per-turn things go here */ /* once-per-turn things go here */
/********************************/ /********************************/
l_nhcore_call(NHCORE_MOVELOOP_TURN);
if (Glib) if (Glib)
glibr(); glibr();
nh_timeout(); nh_timeout();
@@ -633,6 +635,8 @@ newgame(void)
* any artifacts */ * any artifacts */
u_init(); u_init();
l_nhcore_init();
#ifndef NO_SIGNAL #ifndef NO_SIGNAL
(void) signal(SIGINT, (SIG_RET_TYPE) done1); (void) signal(SIGINT, (SIG_RET_TYPE) done1);
#endif #endif
@@ -709,6 +713,8 @@ welcome(boolean new_game) /* false => restoring an old game */
: "%s %s, the%s %s %s, welcome back to NetHack!", : "%s %s, the%s %s %s, welcome back to NetHack!",
Hello((struct monst *) 0), g.plname, buf, g.urace.adj, Hello((struct monst *) 0), g.plname, buf, g.urace.adj,
(currentgend && g.urole.name.f) ? g.urole.name.f : g.urole.name.m); (currentgend && g.urole.name.f) ? g.urole.name.f : g.urole.name.m);
l_nhcore_call(new_game ? NHCORE_START_NEW_GAME : NHCORE_RESTORE_OLD_GAME);
} }
#ifdef POSITIONBAR #ifdef POSITIONBAR
+3
View File
@@ -517,6 +517,9 @@ const struct instance_globals g_init = {
MAX_LAN_USERNAME, /* lusername_size */ MAX_LAN_USERNAME, /* lusername_size */
#endif /* MAX_LAN_USERNAME */ #endif /* MAX_LAN_USERNAME */
/* nhlua.c */
UNDEFINED_VALUE, /* luacore */
/* o_init.c */ /* o_init.c */
DUMMY, /* disco */ DUMMY, /* disco */
+3
View File
@@ -1753,6 +1753,8 @@ void
nh_terminate(int status) nh_terminate(int status)
{ {
g.program_state.in_moveloop = 0; /* won't be returning to normal play */ g.program_state.in_moveloop = 0; /* won't be returning to normal play */
l_nhcore_call(NHCORE_GAME_EXIT);
#ifdef MAC #ifdef MAC
getreturn("to exit"); getreturn("to exit");
#endif #endif
@@ -1761,6 +1763,7 @@ nh_terminate(int status)
if (!g.program_state.panicking) { if (!g.program_state.panicking) {
freedynamicdata(); freedynamicdata();
dlb_cleanup(); dlb_cleanup();
l_nhcore_done();
} }
#ifdef VMS #ifdef VMS
+113
View File
@@ -14,6 +14,10 @@
/* */ /* */
/* lua_CFunction prototypes */ /* lua_CFunction prototypes */
#ifdef DUMPLOG
static int nhl_dump_fmtstr(lua_State *);
#endif /* DUMPLOG */
static int nhl_dnum_name(lua_State *);
static int nhl_test(lua_State *); static int nhl_test(lua_State *);
static int nhl_getmap(lua_State *); static int nhl_getmap(lua_State *);
static void nhl_add_table_entry_bool(lua_State *, const char *, boolean); static void nhl_add_table_entry_bool(lua_State *, const char *, boolean);
@@ -46,6 +50,68 @@ static void init_u_data(lua_State *);
static int nhl_set_package_path(lua_State *, const char *); static int nhl_set_package_path(lua_State *, const char *);
static int traceback_handler(lua_State *); static int traceback_handler(lua_State *);
static const char *nhcore_call_names[NUM_NHCORE_CALLS] = {
"start_new_game",
"restore_old_game",
"moveloop_turn",
"game_exit",
};
static boolean nhcore_call_available[NUM_NHCORE_CALLS];
void
l_nhcore_init(void)
{
if ((g.luacore = nhl_init()) != 0) {
if (!nhl_loadlua(g.luacore, "nhcore.lua")) {
g.luacore = (lua_State *) 0;
} else {
int i;
for (i = 0; i < NUM_NHCORE_CALLS; i++)
nhcore_call_available[i] = TRUE;
}
}
}
void
l_nhcore_done(void)
{
if (g.luacore) {
nhl_done(g.luacore);
g.luacore = 0;
}
}
void
l_nhcore_call(int callidx)
{
int ltyp;
if (callidx < 0 || callidx >= NUM_NHCORE_CALLS
|| !g.luacore || !nhcore_call_available[callidx])
return;
lua_getglobal(g.luacore, "nhcore");
if (!lua_istable(g.luacore, -1)) {
/*impossible("nhcore is not a lua table");*/
nhl_done(g.luacore);
g.luacore = 0;
return;
}
lua_getfield(g.luacore, -1, nhcore_call_names[callidx]);
ltyp = lua_type(g.luacore, -1);
if (ltyp == LUA_TFUNCTION) {
lua_remove(g.luacore, -2); /* nhcore_call_names[callidx] */
lua_remove(g.luacore, -2); /* nhcore */
lua_call(g.luacore, 0, 1);
} else {
/*impossible("nhcore.%s is not a lua function",
nhcore_call_names[callidx]);*/
nhcore_call_available[callidx] = FALSE;
}
}
void void
nhl_error(lua_State *L, const char *msg) nhl_error(lua_State *L, const char *msg)
{ {
@@ -780,6 +846,40 @@ get_table_option(lua_State *L,
return ret; return ret;
} }
#ifdef DUMPLOG
/* local fname = dump_fmtstr("/tmp/nethack.%n.%d.log"); */
static int
nhl_dump_fmtstr(lua_State *L)
{
int argc = lua_gettop(L);
char buf[512];
if (argc == 1)
lua_pushstring(L, dump_fmtstr(luaL_checkstring(L, 1), buf, TRUE));
else
nhl_error(L, "Expected a string parameter");
return 1;
}
#endif /* DUMPLOG */
/* local dungeon_name = dnum_name(u.dnum); */
static int
nhl_dnum_name(lua_State *L)
{
int argc = lua_gettop(L);
if (argc == 1) {
int dnum = luaL_checkinteger(L, 1);
if (dnum >= 0 && dnum < g.n_dgns)
lua_pushstring(L, g.dungeons[dnum].dname);
else
lua_pushstring(L, "");
} else
nhl_error(L, "Expected an integer parameter");
return 1;
}
/* /*
test( { x = 123, y = 456 } ); test( { x = 123, y = 456 } );
*/ */
@@ -831,6 +931,10 @@ static const struct luaL_Reg nhl_functions[] = {
{"parse_config", nhl_parse_config}, {"parse_config", nhl_parse_config},
{"get_config", nhl_get_config}, {"get_config", nhl_get_config},
{"get_config_errors", l_get_config_errors}, {"get_config_errors", l_get_config_errors},
#ifdef DUMPLOG
{"dump_fmtstr", nhl_dump_fmtstr},
#endif /* DUMPLOG */
{"dnum_name", nhl_dnum_name},
{NULL, NULL} {NULL, NULL}
}; };
@@ -927,6 +1031,15 @@ nhl_meta_u_index(lua_State *L)
} else if (!strcmp(tkey, "role")) { } else if (!strcmp(tkey, "role")) {
lua_pushstring(L, g.urole.name.m); lua_pushstring(L, g.urole.name.m);
return 1; return 1;
} else if (!strcmp(tkey, "moves")) {
lua_pushinteger(L, g.moves);
return 1;
} else if (!strcmp(tkey, "uhave_amulet")) {
lua_pushinteger(L, u.uhave.amulet);
return 1;
} else if (!strcmp(tkey, "depth")) {
lua_pushinteger(L, depth(&u.uz));
return 1;
} }
nhl_error(L, "Unknown u table index"); nhl_error(L, "Unknown u table index");
+1 -1
View File
@@ -86,7 +86,7 @@ DATHELP = help hh cmdhelp keyhelp history opthelp wizhelp
SPEC_LEVS = asmodeus.lua baalz.lua bigrm-*.lua castle.lua fakewiz?.lua \ SPEC_LEVS = asmodeus.lua baalz.lua bigrm-*.lua castle.lua fakewiz?.lua \
juiblex.lua knox.lua medusa-?.lua minend-?.lua minefill.lua \ juiblex.lua knox.lua medusa-?.lua minend-?.lua minefill.lua \
minetn-?.lua oracle.lua orcus.lua sanctum.lua soko?-?.lua \ minetn-?.lua oracle.lua orcus.lua sanctum.lua soko?-?.lua \
tower?.lua valley.lua wizard?.lua nhlib.lua themerms.lua \ tower?.lua valley.lua wizard?.lua nhcore.lua nhlib.lua themerms.lua \
astral.lua air.lua earth.lua fire.lua water.lua astral.lua air.lua earth.lua fire.lua water.lua
QUEST_LEVS = ???-goal.lua ???-fil?.lua ???-loca.lua ???-strt.lua QUEST_LEVS = ???-goal.lua ???-fil?.lua ???-loca.lua ???-strt.lua
+2 -1
View File
@@ -1140,6 +1140,7 @@
"$(NH_DAT_DIR)/Mon-goal.lua", "$(NH_DAT_DIR)/Mon-goal.lua",
"$(NH_DAT_DIR)/Mon-loca.lua", "$(NH_DAT_DIR)/Mon-loca.lua",
"$(NH_DAT_DIR)/Mon-strt.lua", "$(NH_DAT_DIR)/Mon-strt.lua",
"$(NH_DAT_DIR)/nhcore.lua",
"$(NH_DAT_DIR)/nhlib.lua", "$(NH_DAT_DIR)/nhlib.lua",
"$(NH_DAT_DIR)/oracle.lua", "$(NH_DAT_DIR)/oracle.lua",
"$(NH_DAT_DIR)/orcus.lua", "$(NH_DAT_DIR)/orcus.lua",
@@ -1206,7 +1207,7 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "cd \"${NH_DAT_DIR}\"\n\"${NH_UTIL_DIR}\"/dlb cf nhdat help hh cmdhelp keyhelp history opthelp wizhelp dungeon.lua tribute asmodeus.lua baalz.lua bigrm-*.lua castle.lua fakewiz?.lua juiblex.lua knox.lua medusa-?.lua minend-?.lua minefill.lua minetn-?.lua oracle.lua orcus.lua sanctum.lua soko?-?.lua tower?.lua valley.lua wizard?.lua nhlib.lua themerms.lua astral.lua air.lua earth.lua fire.lua water.lua ???-goal.lua ???-fil?.lua ???-loca.lua ???-strt.lua bogusmon data engrave epitaph oracles options quest.lua rumors\n"; shellScript = "cd \"${NH_DAT_DIR}\"\n\"${NH_UTIL_DIR}\"/dlb cf nhdat help hh cmdhelp keyhelp history opthelp wizhelp dungeon.lua tribute asmodeus.lua baalz.lua bigrm-*.lua castle.lua fakewiz?.lua juiblex.lua knox.lua medusa-?.lua minend-?.lua minefill.lua minetn-?.lua oracle.lua orcus.lua sanctum.lua soko?-?.lua tower?.lua valley.lua wizard?.lua nhcore.lua nhlib.lua themerms.lua astral.lua air.lua earth.lua fire.lua water.lua ???-goal.lua ???-fil?.lua ???-loca.lua ???-strt.lua bogusmon data engrave epitaph oracles options quest.lua rumors\n";
}; };
3192867021A39F6A00325BEB /* Install */ = { 3192867021A39F6A00325BEB /* Install */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;