Implement a per-save lua init script.

develop
Alexander Gavrilov 2012-12-21 14:00:50 +04:00
parent bba96494f3
commit bb3a491d68
4 changed files with 104 additions and 3 deletions

@ -404,7 +404,10 @@ ul.auto-toc {
<li><a class="reference internal" href="#sort" id="id54">sort</a></li>
</ul>
</li>
<li><a class="reference internal" href="#scripts" id="id55">Scripts</a></li>
<li><a class="reference internal" href="#scripts" id="id55">Scripts</a><ul>
<li><a class="reference internal" href="#save-init-script" id="id56">Save init script</a></li>
</ul>
</li>
</ul>
</div>
<p>The current version of DFHack has extensive support for
@ -1034,6 +1037,9 @@ can be omitted.</p>
<li><p class="first"><tt class="docutils literal">dfhack.getHackPath()</tt></p>
<p>Returns the dfhack directory path, i.e. <tt class="docutils literal"><span class="pre">&quot;.../df/hack/&quot;</span></tt>.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.getSavePath()</tt></p>
<p>Returns the path to the current save directory, or <em>nil</em> if no save loaded.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.getTickCount()</tt></p>
<p>Returns the tick count in ms, exactly as DF ui uses.</p>
</li>
@ -3064,6 +3070,25 @@ The <tt class="docutils literal">name</tt> argument should be the name stem, as
</li>
</ul>
<p>Note that this function lets errors propagate to the caller.</p>
<div class="section" id="save-init-script">
<h2><a class="toc-backref" href="#id56">Save init script</a></h2>
<p>If a save directory contains a file called <tt class="docutils literal">raw/init.lua</tt>, it is
automatically loaded and executed every time the save is loaded. It
can also define the following functions to be called by dfhack:</p>
<ul>
<li><p class="first"><tt class="docutils literal">function onStateChange(op) ... end</tt></p>
<p>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 <tt class="docutils literal">dfhack.onStateChange</tt> table, with associated
cleanup concerns.</p>
</li>
<li><p class="first"><tt class="docutils literal">function onUnload() ... end</tt></p>
<p>Called when the save containing the script is unloaded. This function
should clean up any global hooks installed by the script.</p>
</li>
</ul>
<p>Within the init script, the path to the save directory is available as <tt class="docutils literal">SAVE_PATH</tt>.</p>
</div>
</div>
</div>
</body>

@ -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``.

@ -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);

@ -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