From bb3a491d6891b6c60b349328b98d5e5451b40214 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 21 Dec 2012 14:00:50 +0400 Subject: [PATCH] Implement a per-save lua init script. --- Lua API.html | 27 +++++++++++++++++++++++++- Lua API.rst | 25 ++++++++++++++++++++++++ library/LuaTools.cpp | 12 ++++++++++++ library/lua/dfhack.lua | 43 ++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 104 insertions(+), 3 deletions(-) diff --git a/Lua API.html b/Lua API.html index aa41b8b63..e5318165d 100644 --- a/Lua API.html +++ b/Lua API.html @@ -404,7 +404,10 @@ ul.auto-toc {
  • sort
  • -
  • Scripts
  • +
  • Scripts +
  • The current version of DFHack has extensive support for @@ -1034,6 +1037,9 @@ can be omitted.

  • dfhack.getHackPath()

    Returns the dfhack directory path, i.e. ".../df/hack/".

  • +
  • dfhack.getSavePath()

    +

    Returns the path to the current save directory, or nil if no save loaded.

    +
  • dfhack.getTickCount()

    Returns the tick count in ms, exactly as DF ui uses.

  • @@ -3064,6 +3070,25 @@ The name argument should be the name stem, as

    Note that this function lets errors propagate to the caller.

    +
    +

    Save init script

    +

    If a save directory contains a file called raw/init.lua, it is +automatically loaded and executed every time the save is loaded. It +can also define the following functions to be called by dfhack:

    + +

    Within the init script, the path to the save directory is available as SAVE_PATH.

    +
    diff --git a/Lua API.rst b/Lua API.rst index eaef74997..0ec464f76 100644 --- a/Lua API.rst +++ b/Lua API.rst @@ -741,6 +741,10 @@ can be omitted. Returns the dfhack directory path, i.e. ``".../df/hack/"``. +* ``dfhack.getSavePath()`` + + Returns the path to the current save directory, or *nil* if no save loaded. + * ``dfhack.getTickCount()`` Returns the tick count in ms, exactly as DF ui uses. @@ -2993,3 +2997,24 @@ from other scripts) in any context, via the same function the core uses: The ``name`` argument should be the name stem, as would be used on the command line. Note that this function lets errors propagate to the caller. + +Save init script +================ + +If a save directory contains a file called ``raw/init.lua``, it is +automatically loaded and executed every time the save is loaded. It +can also define the following functions to be called by dfhack: + +* ``function onStateChange(op) ... end`` + + Automatically called from the regular onStateChange event as long + as the save is still loaded. This avoids the need to install a hook + into the global ``dfhack.onStateChange`` table, with associated + cleanup concerns. + +* ``function onUnload() ... end`` + + Called when the save containing the script is unloaded. This function + should clean up any global hooks installed by the script. + +Within the init script, the path to the save directory is available as ``SAVE_PATH``. diff --git a/library/LuaTools.cpp b/library/LuaTools.cpp index c052b88aa..392659bc8 100644 --- a/library/LuaTools.cpp +++ b/library/LuaTools.cpp @@ -1551,6 +1551,10 @@ void DFHack::Lua::Notification::bind(lua_State *state, const char *name) void OpenDFHackApi(lua_State *state); +namespace DFHack { namespace Lua { namespace Core { + static void InitCoreContext(); +}}} + lua_State *DFHack::Lua::Open(color_ostream &out, lua_State *state) { if (!state) @@ -1654,6 +1658,10 @@ lua_State *DFHack::Lua::Open(color_ostream &out, lua_State *state) lua_dup(state); lua_rawseti(state, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS); + // Init core-context specific stuff before loading dfhack.lua + if (IsCoreContext(state)) + Lua::Core::InitCoreContext(); + // load dfhack.lua Require(out, state, "dfhack"); @@ -1829,8 +1837,12 @@ void DFHack::Lua::Core::Init(color_ostream &out) State = luaL_newstate(); + // Calls InitCoreContext after checking IsCoreContext Lua::Open(out, State); +} +static void Lua::Core::InitCoreContext() +{ lua_newtable(State); lua_rawsetp(State, LUA_REGISTRYINDEX, &DFHACK_TIMEOUTS_TOKEN); diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index 3f57e5722..1354701ff 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -328,9 +328,11 @@ end -- Command scripts -dfhack.internal.scripts = dfhack.internal.scripts or {} +local internal = dfhack.internal -local scripts = dfhack.internal.scripts +internal.scripts = internal.scripts or {} + +local scripts = internal.scripts local hack_path = dfhack.getHackPath() function dfhack.run_script(name,...) @@ -349,5 +351,42 @@ function dfhack.run_script(name,...) return f(...) end +-- Per-save init file + +function dfhack.getSavePath() + if dfhack.isWorldLoaded() then + return dfhack.getDFPath() .. '/data/save/' .. df.global.world.cur_savegame.save_dir + end +end + +if dfhack.is_core_context then + dfhack.onStateChange.DFHACK_PER_SAVE = function(op) + if op == SC_WORLD_LOADED or op == SC_WORLD_UNLOADED then + if internal.save_init then + if internal.save_init.onUnload then + safecall(internal.save_init.onUnload) + end + internal.save_init = nil + end + + local path = dfhack.getSavePath() + + if path and op == SC_WORLD_LOADED then + local env = setmetatable({ SAVE_PATH = path }, { __index = base_env }) + local f,perr = loadfile(path..'/raw/init.lua', 't', env) + if f == nil then + if not string.match(perr, 'No such file or directory') then + dfhack.printerr(perr) + end + elseif safecall(f) then + internal.save_init = env + end + end + elseif internal.save_init and internal.save_init.onStateChange then + safecall(internal.save_init.onStateChange, op) + end + end +end + -- Feed the table back to the require() mechanism. return dfhack