dfhack icon indicating copy to clipboard operation
dfhack copied to clipboard

Enabled state tracking and restoration across all tools

Open myk002 opened this issue 3 years ago • 4 comments

As part of the usability overhaul (#2225), we want to be able to make the enabled state of DFHack tools persistent so players can enable a tool once and it will stay that way across reloads until they turn it off. This will reduce the need to edit init scripts to manually maintain the desired state for DFHack tools. The DFHack control panel (#2213) depends on this kind of persistent behavior across DFHack tools.

Goals

  • The player should be able to configure DFHack tools in-game and to have that configuration persisted across program reloads (for globally scoped tools) or savegame reloads (for save-scoped tools). No init file editing should be required (though the player can choose to replace/override the system proposed in this document with init script configuration).
  • The player should not have to care whether a tool is a plugin or a Lua script. The player interface should be the same.

Challenges

  1. Whereas plugins have the plugin_init() hook that they could use to restore state, scripts have no such hook. We need to design a mechanism that will allow scripts to bootstrap themselves.
  2. In order to know when a tool should be re-enabled, we need to know whether the tool is global or per-save. Right now there is no way to programmatically determine that.
  3. Plugins export whether they are enabled but scripts do not. We need to design a way for scripts to report their enabled status.

myk002 avatar Oct 14 '22 22:10 myk002

most class (2) tools (UI enhancements) can be ported to the overlay framework and have their state managed by overlay. we still have to have a solution for class (3) (per-save, persistent config) scripts.

myk002 avatar Nov 30 '22 10:11 myk002

seedwatch immediately comes to mind (from a recent playthrough) as a plugin that does not properly save state, will open an issue

ab9rf avatar Nov 30 '22 13:11 ab9rf

Here's the solution I'm leaning towards:

  1. all scripts can declare a global function named onStateChange(sc) that will receive state change events (similar to what scripts in init.d can already do)
  2. all scripts are reqscript()ed (similar to how the overlay framework scans scripts for OVERLAY_WIDGETS) and those onStateChange() functions are discovered and hooked up to dfhack.onStateChange
  3. scripts load their state and re-enable themselves just as plugins do in the onStateChange callback

the code that scans scripts and connects their onStateChange methods can be a plugin to separate the functionality from core. script-manager. Scanning will be done in the plugin's own plugin_onStateChange() function for SC_CORE_INITIALIZED. Scripts can still react to SC_CORE_INITIALIZED since onStateChange hits plugins before scripts.

For scripts added/changed during gameplay, we can detect a script being (re)loaded in dfhack.lua and call out to require('plugins.script_manager').refresh(script_name).

myk002 avatar Dec 05 '22 18:12 myk002

in addition, scripts can declare a global function named isEnabled which will return the enabled status for the script. when the enable command is run, scripts are scanned for this global function and it is called to get the current status. Other scripts/plugins can also query the enabled status of the script via this function.

myk002 avatar Dec 05 '22 21:12 myk002