From cf847109ce84f87047dd1c8620b01edacd57b930 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 3 Apr 2023 15:59:42 -0700 Subject: [PATCH 1/9] read mods from all mod dirs and only use most recent versions --- docs/Core.rst | 37 ++++++++---- docs/changelog.txt | 2 + docs/guides/modding-guide.rst | 26 ++++---- library/Core.cpp | 58 +++--------------- library/LuaApi.cpp | 8 +-- library/lua/script-manager.lua | 106 +++++++++++++++++++++++++++++++++ 6 files changed, 154 insertions(+), 83 deletions(-) diff --git a/docs/Core.rst b/docs/Core.rst index c06a413da..5decd668d 100644 --- a/docs/Core.rst +++ b/docs/Core.rst @@ -243,18 +243,31 @@ For example, if ``teleport`` is run, these folders are searched in order for Scripts in installed mods ......................... -Script directories in installed mods are automatically added to the script path -according to the following rules: - -**If a world is not loaded**, then directories matching the pattern -``data/installed_mods/*/scripts_modinstalled/`` are added to the script path -in alphabetical order. - -**If a world is loaded**, then the ``scripts_modactive`` directories of active -mods are also added to the script path according to the active mod load order, -and scripts in active mods take precedence over scripts in -``scripts_modinstalled`` in non-active mods. For example, the search paths for -mods might look like this:: +Scripts in mods are automatically added to the script path. The following +directories are searched for mods:: + + ../../workshop/content/975370/ (the DF Steam workshop directory) + mods/ + data/installed_mods/ + +Each mod can have two directories that contain scripts: + +- ``scripts_modactive/`` is added to the script path if and only if the mod is + active in the loaded world. +- ``scripts_modinstalled/`` is added to the script path as long as the mod is + installed in one of the searched mod directories. + +Multiple versions of a mod may be installed at the same time. If a mod is +active in a loaded world, then the scripts for the version of the mod that is +active will be added to the script path. Otherwise, the latest version of each +mod is added to the script path. + +Scripts for active mods take precedence according to their load order when you +generated the current world. + +Scripts for non-active mods are ordered by their containing mod's ID. + +For example, the search paths for mods might look like this:: activemod_last_in_load_order/scripts_modactive activemod_last_in_load_order/scripts_modinstalled diff --git a/docs/changelog.txt b/docs/changelog.txt index b0b86c4cd..c63168dca 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -42,6 +42,8 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - `buildingplan`: items in the item selection dialog should now use the same item quality symbols as the base game +- Mods: scripts in mods that are only in the steam workshop directory are now accessible. this means that a script-only mod that you never mark as "active" when generating a world will still receive automatic updates and be usable from in-game +- Mods: scripts from only the most recent version of an installed mod are added to the script path ## Documentation diff --git a/docs/guides/modding-guide.rst b/docs/guides/modding-guide.rst index b386b48c3..0c9513fef 100644 --- a/docs/guides/modding-guide.rst +++ b/docs/guides/modding-guide.rst @@ -77,8 +77,11 @@ Let's go through that line by line. If you develop your mod using version control (recommended!), that :file:`README.md` file can also serve as your git repository documentation. -These files end up in a subdirectory under :file:`data/installed_mods/` when -the mod is selected as "active" for the first time. +These files end up in a subdirectory under :file:`mods/` when players copy them +in or install them from the +`Steam Workshop `__, and in +:file:`data/installed_mods/` when the mod is selected as "active" for the first +time. What if I just want to distribute a simple script? -------------------------------------------------- @@ -95,13 +98,6 @@ DFHack to find it and add your mod to the `script-paths`. Your script will be runnable from the title screen and in any loaded world, regardless of whether your mod is explicitly "active". -Be sure to remind players to mark your mod as "active" at least once so it gets -installed to the :file:`data/installed_mods/` folder. They may have to create a -new world just so they can mark the mod as "active". This is true both for -players who copied the mod into the :file:`mods/` folder manually and for -players who subscribed via -`Steam Workshop `__. - A mod-maker's development environment ------------------------------------- @@ -109,11 +105,11 @@ Create a folder for development somewhere outside your Dwarf Fortress installation directory (e.g. ``/path/to/mymods/``). If you work on multiple mods, you might want to make a subdirectory for each mod. -If you have changes to the raws, you'll have to copy them into DF's ``data/ -installed_mods/`` folder to have them take effect, but you can set things up so -that scripts are run directly from your dev directory. This way, you can edit -your scripts and have the changes available in the game immediately: no -copying, no restarting. +If you have changes to the raws, you'll have to copy them into DF's +``data/installed_mods/`` folder to have them take effect, but you can set +things up so that scripts are run directly from your dev directory. This way, +you can edit your scripts and have the changes available in the game +immediately: no copying, no restarting. How does this magic work? Just add a line like this to your ``dfhack-config/script-paths.txt`` file:: @@ -123,7 +119,7 @@ How does this magic work? Just add a line like this to your Then that directory will be searched when you run DFHack commands from inside the game. The ``+`` at the front of the path means to search that directory first, before any other script directory (like :file:`hack/scripts` or other -versions of your mod in ``data/installed_mods/``). +versions of your mod in the DF mod folders). The structure of the game ------------------------- diff --git a/library/Core.cpp b/library/Core.cpp index b5337b827..d13c82fa5 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -105,7 +105,6 @@ DBG_DECLARE(core,script,DebugCategory::LINFO); static const std::string CONFIG_PATH = "dfhack-config/"; static const std::string CONFIG_DEFAULTS_PATH = "hack/data/dfhack-config-defaults/"; -static const std::string MOD_PATH = "data/installed_mods/"; class MainThread { public: @@ -534,60 +533,19 @@ bool loadScriptPaths(color_ostream &out, bool silent = false) return true; } -bool loadModScriptPaths(color_ostream &out) { - std::map files; - Filesystem::listdir_recursive(MOD_PATH, files, 0); - - DEBUG(script,out).print("found %zd installed mods\n", files.size()); - if (!files.size()) - return true; - - for (auto & entry : files) { - DEBUG(script,out).print(" %s\n", entry.first.c_str()); - } - - std::vector mod_paths; - if (Core::getInstance().isWorldLoaded()) { - DEBUG(script,out).print("active load order:\n"); - for (auto & path : df::global::world->object_loader.object_load_order_src_dir) { - DEBUG(script,out).print(" %s\n", path->c_str()); - if (0 == path->find(MOD_PATH)) - mod_paths.emplace_back(*path); - } - } - +static void loadModScriptPaths(color_ostream &out) { + auto L = Lua::Core::State; + Lua::StackUnwinder top(L); std::vector mod_script_paths; - for (auto pathit = mod_paths.rbegin(); pathit != mod_paths.rend(); ++pathit) { - std::string active_path = *pathit + "scripts_modactive"; - std::string installed_path = *pathit + "scripts_modinstalled"; - DEBUG(script,out).print("checking active path: %s\n", pathit->c_str()); - if (Filesystem::isdir(active_path)) - mod_script_paths.emplace_back(active_path); - if (Filesystem::isdir(installed_path)) - mod_script_paths.emplace_back(installed_path); - std::string slashless = *pathit; - slashless.resize(slashless.size()-1); - if (0 == files.erase(slashless)) { - WARN(script,out).print("script path not found: '%s'\n", pathit->c_str()); - } - } - - for (auto & entry : files) { - if (!entry.second) - continue; - DEBUG(script,out).print("checking inactive path: %s\n", entry.first.c_str()); - std::string installed_path = entry.first + "/scripts_modinstalled"; - if (Filesystem::isdir(installed_path)) - mod_script_paths.emplace_back(installed_path); - } - + Lua::CallLuaModuleFunction(out, L, "script-manager", "get_mod_script_paths", 0, 1, + Lua::DEFAULT_LUA_LAMBDA, + [&](lua_State *L) { + Lua::GetVector(L, mod_script_paths); + }); DEBUG(script,out).print("final mod script paths:\n"); for (auto & path : mod_script_paths) DEBUG(script,out).print(" %s\n", path.c_str()); - Core::getInstance().setModScriptPaths(mod_script_paths); - - return true; } static std::map state_change_event_map; diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index c9bdc3021..60d533891 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -2728,13 +2728,9 @@ static int filesystem_listdir_recursive(lua_State *L) include_prefix = lua_toboolean(L, 3); std::map files; int err = DFHack::Filesystem::listdir_recursive(dir, files, depth, include_prefix); - if (err) - { + if (err != -1) { lua_pushnil(L); - if (err == -1) - lua_pushfstring(L, "max depth exceeded: %d", depth); - else - lua_pushstring(L, strerror(err)); + lua_pushstring(L, strerror(err)); lua_pushinteger(L, err); return 3; } diff --git a/library/lua/script-manager.lua b/library/lua/script-manager.lua index cc5dd9fe3..1a7161a10 100644 --- a/library/lua/script-manager.lua +++ b/library/lua/script-manager.lua @@ -2,6 +2,9 @@ local _ENV = mkmodule('script-manager') local utils = require('utils') +--------------------- +-- enabled API + -- for each script that can be loaded as a module, calls cb(script_name, env) function foreach_module_script(cb) for _,script_path in ipairs(dfhack.internal.getScriptPaths()) do @@ -57,4 +60,107 @@ function list() end end +--------------------- +-- mod script paths + +-- this perhaps could/should be queried from the Steam API +-- are there any installation configurations where this will be wrong, though? +local WORKSHOP_MODS_PATH = '../../workshop/content/975370/' +local MODS_PATH = 'mods/' +local INSTALLED_MODS_PATH = 'data/installed_mods/' + +-- last instance of the same version of the same mod wins, so read them in this +-- order (in increasing order of liklihood that players may have made custom +-- changes to the files) +local MOD_PATH_ROOTS = {WORKSHOP_MODS_PATH, MODS_PATH, INSTALLED_MODS_PATH} + +local function get_mod_id_and_version(path) + local idfile = path .. '/info.txt' + local ok, lines = pcall(io.lines, idfile) + if not ok then return end + local id, version + for line in lines do + if not id then + _,_,id = line:find('^%[ID:([^%]]+)%]') + end + if not version then + -- note this doesn't include the closing brace since some people put + -- non-number characters in here, and DF only reads the digits as the + -- numeric version + _,_,version = line:find('^%[NUMERIC_VERSION:(%d+)') + end + -- note that we do *not* want to break out of this loop early since + -- lines has to hit EOF to close the file + end + return id, version +end + +local function add_script_path(mod_script_paths, path) + if dfhack.filesystem.isdir(path) then + print('indexing scripts from mod script path: ' .. path) + table.insert(mod_script_paths, path) + end +end + +local function add_script_paths(mod_script_paths, base_path, include_modactive) + if not base_path:endswith('/') then + base_path = base_path .. '/' + end + if include_modactive then + add_script_path(mod_script_paths, base_path..'scripts_modactive') + end + add_script_path(mod_script_paths, base_path..'scripts_modinstalled') +end + +function get_mod_script_paths() + -- ordered map of mod id -> {handled=bool, versions=map of version -> path} + local mods = utils.OrderedTable() + local mod_script_paths = {} + + -- if a world is loaded, process active mods first, and lock to active version + if dfhack.isWorldLoaded() then + for _,path in ipairs(df.global.world.object_loader.object_load_order_src_dir) do + path = tostring(path) + if not path:startswith(INSTALLED_MODS_PATH) then goto continue end + local id = get_mod_id_and_version(path) + if not id then goto continue end + mods[id] = {handled=true} + add_script_paths(mod_script_paths, path, true) + ::continue:: + end + end + + -- assemble version -> path maps for all (non-handled) mod source dirs + for _,mod_path_root in ipairs(MOD_PATH_ROOTS) do + local files = dfhack.filesystem.listdir_recursive(mod_path_root, 0) + if not files then goto skip_path_root end + for _,f in ipairs(files) do + if not f.isdir then goto continue end + local id, version = get_mod_id_and_version(f.path) + if not id or not version then goto continue end + local mod = ensure_key(mods, id) + if mod.handled then goto continue end + ensure_key(mod, 'versions')[version] = f.path + ::continue:: + end + ::skip_path_root:: + end + + -- add script paths from most recent version of all not-yet-handled mods + for _,v in pairs(mods) do + if v.handled then goto continue end + local max_version, path + for version,mod_path in pairs(v.versions) do + if not max_version or max_version < version then + path = mod_path + max_version = version + end + end + add_script_paths(mod_script_paths, path) + ::continue:: + end + + return mod_script_paths +end + return _ENV From 1ed25cdd72c9e966f17b8d3d74ea97f5fe8e36a1 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Thu, 6 Apr 2023 07:12:58 +0000 Subject: [PATCH 2/9] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 53f0aedf9..e6216cc28 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 53f0aedf9f7df33a4f79246ba46de02794619d09 +Subproject commit e6216cc28e4315df5fb128411d0ca57fe78ccb2b From 8d40ca8be68725c11dcaadbc95cb20084bd93649 Mon Sep 17 00:00:00 2001 From: Quietust Date: Sat, 1 Apr 2023 12:37:36 -0600 Subject: [PATCH 3/9] Add "faststart" plugin to make DF start faster In particular, it makes the game's "Loading..." screen animate as quickly as possible, shortening it from around 10 seconds to slightly more than 1 second. A conditional build setting makes it skip the animation as well, making it slightly faster yet. Ideally, this should become part of the Tweak plugin, but we're not building that right now. --- docs/changelog.txt | 1 + docs/plugins/faststart.rst | 18 ++++++++++ plugins/CMakeLists.txt | 1 + plugins/faststart.cpp | 69 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 89 insertions(+) create mode 100644 docs/plugins/faststart.rst create mode 100644 plugins/faststart.cpp diff --git a/docs/changelog.txt b/docs/changelog.txt index 4be3e6a64..59c0dc2b1 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -34,6 +34,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: # Future ## New Plugins +- `faststart`: speeds up the "Loading..." screen so the Main Menu appears sooner ## Fixes - `hotkeys`: hotkey hints on menu popup will no longer get their last character cut off by the scrollbar diff --git a/docs/plugins/faststart.rst b/docs/plugins/faststart.rst new file mode 100644 index 000000000..3dc734183 --- /dev/null +++ b/docs/plugins/faststart.rst @@ -0,0 +1,18 @@ +faststart +========= + +.. dfhack-tool:: + :summary: Makes the main menu appear sooner. + :tags: interface + :no-command: + +This plugin accelerates the initial "Loading..." screen that appears when the +game first starts, so you don't have to wait as long before the Main Menu +appears and you can start playing. + +Usage +----- + +:: + + enable faststart diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index dc7fa9276..53d0296c7 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -112,6 +112,7 @@ dfhack_plugin(dig-now dig-now.cpp LINK_LIBRARIES lua) #dfhack_plugin(embark-tools embark-tools.cpp) dfhack_plugin(eventful eventful.cpp LINK_LIBRARIES lua) dfhack_plugin(fastdwarf fastdwarf.cpp) +dfhack_plugin(faststart faststart.cpp) dfhack_plugin(filltraffic filltraffic.cpp) #dfhack_plugin(fix-unit-occupancy fix-unit-occupancy.cpp) #dfhack_plugin(fixveins fixveins.cpp) diff --git a/plugins/faststart.cpp b/plugins/faststart.cpp new file mode 100644 index 000000000..d885726c0 --- /dev/null +++ b/plugins/faststart.cpp @@ -0,0 +1,69 @@ +// Fast Startup tweak + +#include "Core.h" +#include +#include +#include +#include +#include + +#include "df/viewscreen_initial_prepst.h" +#include + +using namespace DFHack; +using namespace df::enums; +using std::vector; + +// Uncomment this to make the Loading screen as fast as possible +// This has the side effect of removing the dwarf face animation +// (and briefly making the game become unresponsive) + +//#define REALLY_FAST + +DFHACK_PLUGIN("faststart"); +DFHACK_PLUGIN_IS_ENABLED(is_enabled); + +struct prep_hook : df::viewscreen_initial_prepst +{ + typedef df::viewscreen_initial_prepst interpose_base; + + DEFINE_VMETHOD_INTERPOSE(void, logic, ()) + { +#ifdef REALLY_FAST + while (breakdown_level != interface_breakdown_types::STOPSCREEN) + { + render_count++; + INTERPOSE_NEXT(logic)(); + } +#else + render_count = 4; + INTERPOSE_NEXT(logic)(); +#endif + } +}; + +IMPLEMENT_VMETHOD_INTERPOSE(prep_hook, logic); + +DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) +{ + if (enable != is_enabled) + { + if (!INTERPOSE_HOOK(prep_hook, logic).apply(enable)) + return CR_FAILURE; + + is_enabled = enable; + } + + return CR_OK; +} + +DFhackCExport command_result plugin_init ( color_ostream &out, vector &commands) +{ + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown ( color_ostream &out ) +{ + INTERPOSE_HOOK(prep_hook, logic).remove(); + return CR_OK; +} From 93962df3de15db1193438b520c52ef2af00aa93d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 7 Apr 2023 01:57:11 +0000 Subject: [PATCH 4/9] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- plugins/faststart.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/faststart.cpp b/plugins/faststart.cpp index d885726c0..de014801c 100644 --- a/plugins/faststart.cpp +++ b/plugins/faststart.cpp @@ -18,7 +18,7 @@ using std::vector; // This has the side effect of removing the dwarf face animation // (and briefly making the game become unresponsive) -//#define REALLY_FAST +//#define REALLY_FAST DFHACK_PLUGIN("faststart"); DFHACK_PLUGIN_IS_ENABLED(is_enabled); From 75bdc8904c920a5542c0ee2d28a67339f9b44c19 Mon Sep 17 00:00:00 2001 From: Myk Date: Thu, 6 Apr 2023 18:59:57 -0700 Subject: [PATCH 5/9] add dfhack tag to faststart --- docs/plugins/faststart.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/faststart.rst b/docs/plugins/faststart.rst index 3dc734183..b39269e01 100644 --- a/docs/plugins/faststart.rst +++ b/docs/plugins/faststart.rst @@ -3,7 +3,7 @@ faststart .. dfhack-tool:: :summary: Makes the main menu appear sooner. - :tags: interface + :tags: dfhack interface :no-command: This plugin accelerates the initial "Loading..." screen that appears when the From 2ac2817987ae2e8b9d426550c50406c2e1576b3f Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 6 Apr 2023 23:19:04 -0700 Subject: [PATCH 6/9] enable bugfix services by default --- data/dfhack-config/init/dfhack.control-panel-system.init | 4 ++++ .../dfhack-config/init/onMapLoad.control-panel-new-fort.init | 4 ++++ data/dfhack-config/init/onMapLoad.control-panel-repeats.init | 5 +++++ docs/changelog.txt | 1 + 4 files changed, 14 insertions(+) create mode 100644 data/dfhack-config/init/dfhack.control-panel-system.init create mode 100644 data/dfhack-config/init/onMapLoad.control-panel-new-fort.init create mode 100644 data/dfhack-config/init/onMapLoad.control-panel-repeats.init diff --git a/data/dfhack-config/init/dfhack.control-panel-system.init b/data/dfhack-config/init/dfhack.control-panel-system.init new file mode 100644 index 000000000..c13565c28 --- /dev/null +++ b/data/dfhack-config/init/dfhack.control-panel-system.init @@ -0,0 +1,4 @@ +# DO NOT EDIT THIS FILE +# Please use gui/control-panel to edit this file + +enable faststart diff --git a/data/dfhack-config/init/onMapLoad.control-panel-new-fort.init b/data/dfhack-config/init/onMapLoad.control-panel-new-fort.init new file mode 100644 index 000000000..8a159b0de --- /dev/null +++ b/data/dfhack-config/init/onMapLoad.control-panel-new-fort.init @@ -0,0 +1,4 @@ +# DO NOT EDIT THIS FILE +# Please use gui/control-panel to edit this file + +on-new-fortress enable fix/protect-nicks diff --git a/data/dfhack-config/init/onMapLoad.control-panel-repeats.init b/data/dfhack-config/init/onMapLoad.control-panel-repeats.init new file mode 100644 index 000000000..1463230aa --- /dev/null +++ b/data/dfhack-config/init/onMapLoad.control-panel-repeats.init @@ -0,0 +1,5 @@ +# DO NOT EDIT THIS FILE +# Please use gui/control-panel to edit this file + +repeat --name general-strike --time 1 --timeUnits days --command [ fix/general-strike -q ] +repeat --name warn-starving --time 10 --timeUnits days --command [ warn-starving ] diff --git a/docs/changelog.txt b/docs/changelog.txt index 59c0dc2b1..2ae96aa86 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -46,6 +46,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `buildingplan`: items in the item selection dialog should now use the same item quality symbols as the base game - Mods: scripts in mods that are only in the steam workshop directory are now accessible. this means that a script-only mod that you never mark as "active" when generating a world will still receive automatic updates and be usable from in-game - Mods: scripts from only the most recent version of an installed mod are added to the script path +- `gui/control-panel`: bugfix services are now enabled by default ## Documentation From 2923cf7d2114df2578227202a36746df7d9bff3a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 7 Apr 2023 06:22:02 +0000 Subject: [PATCH 7/9] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../init/dfhack.control-panel-system.init | 8 ++++---- .../init/onMapLoad.control-panel-new-fort.init | 8 ++++---- .../init/onMapLoad.control-panel-repeats.init | 10 +++++----- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/data/dfhack-config/init/dfhack.control-panel-system.init b/data/dfhack-config/init/dfhack.control-panel-system.init index c13565c28..8b1431373 100644 --- a/data/dfhack-config/init/dfhack.control-panel-system.init +++ b/data/dfhack-config/init/dfhack.control-panel-system.init @@ -1,4 +1,4 @@ -# DO NOT EDIT THIS FILE -# Please use gui/control-panel to edit this file - -enable faststart +# DO NOT EDIT THIS FILE +# Please use gui/control-panel to edit this file + +enable faststart diff --git a/data/dfhack-config/init/onMapLoad.control-panel-new-fort.init b/data/dfhack-config/init/onMapLoad.control-panel-new-fort.init index 8a159b0de..66a07d14f 100644 --- a/data/dfhack-config/init/onMapLoad.control-panel-new-fort.init +++ b/data/dfhack-config/init/onMapLoad.control-panel-new-fort.init @@ -1,4 +1,4 @@ -# DO NOT EDIT THIS FILE -# Please use gui/control-panel to edit this file - -on-new-fortress enable fix/protect-nicks +# DO NOT EDIT THIS FILE +# Please use gui/control-panel to edit this file + +on-new-fortress enable fix/protect-nicks diff --git a/data/dfhack-config/init/onMapLoad.control-panel-repeats.init b/data/dfhack-config/init/onMapLoad.control-panel-repeats.init index 1463230aa..cbed00b67 100644 --- a/data/dfhack-config/init/onMapLoad.control-panel-repeats.init +++ b/data/dfhack-config/init/onMapLoad.control-panel-repeats.init @@ -1,5 +1,5 @@ -# DO NOT EDIT THIS FILE -# Please use gui/control-panel to edit this file - -repeat --name general-strike --time 1 --timeUnits days --command [ fix/general-strike -q ] -repeat --name warn-starving --time 10 --timeUnits days --command [ warn-starving ] +# DO NOT EDIT THIS FILE +# Please use gui/control-panel to edit this file + +repeat --name general-strike --time 1 --timeUnits days --command [ fix/general-strike -q ] +repeat --name warn-starving --time 10 --timeUnits days --command [ warn-starving ] From 00445767c74eb21600971ffd0699921e2b43300d Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Fri, 7 Apr 2023 06:27:56 +0000 Subject: [PATCH 8/9] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index e6216cc28..a4e5d4514 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit e6216cc28e4315df5fb128411d0ca57fe78ccb2b +Subproject commit a4e5d4514ec33462ee0c0bd25e02d4a9c3e2ce01 From 83017e8b8f7fffb935fd72bee5e356f972c1e6dd Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 7 Apr 2023 00:48:04 -0700 Subject: [PATCH 9/9] give active mods a chance to reattach their hooks --- docs/changelog.txt | 1 + docs/guides/modding-guide.rst | 5 +++++ library/Core.cpp | 5 ++++- library/LuaApi.cpp | 2 +- library/lua/script-manager.lua | 41 ++++++++++++++++++++++------------ 5 files changed, 38 insertions(+), 16 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 2ae96aa86..a0919c602 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -46,6 +46,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `buildingplan`: items in the item selection dialog should now use the same item quality symbols as the base game - Mods: scripts in mods that are only in the steam workshop directory are now accessible. this means that a script-only mod that you never mark as "active" when generating a world will still receive automatic updates and be usable from in-game - Mods: scripts from only the most recent version of an installed mod are added to the script path +- Mods: give active mods a chance to reattach their load hooks when a world is reloaded - `gui/control-panel`: bugfix services are now enabled by default ## Documentation diff --git a/docs/guides/modding-guide.rst b/docs/guides/modding-guide.rst index 0c9513fef..38117503c 100644 --- a/docs/guides/modding-guide.rst +++ b/docs/guides/modding-guide.rst @@ -461,6 +461,11 @@ Ok, you're all set up! Now, let's take a look at an example dfhack.onStateChange[GLOBAL_KEY] = function(sc) if sc == SC_MAP_UNLOADED then dfhack.run_command('disable', 'example-mod') + + -- ensure our mod doesn't try to enable itself when a different + -- world is loaded where we are *not* active + dfhack.onStateChange[GLOBAL_KEY] = nil + return end diff --git a/library/Core.cpp b/library/Core.cpp index d13c82fa5..97c69946e 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -2148,7 +2148,10 @@ void Core::onStateChange(color_ostream &out, state_change_event event) loadModScriptPaths(out); auto L = Lua::Core::State; Lua::StackUnwinder top(L); - Lua::CallLuaModuleFunction(con, L, "script-manager", "reload"); + Lua::CallLuaModuleFunction(con, L, "script-manager", "reload", 1, 0, + [](lua_State* L) { + Lua::Push(L, true); + }); // fallthrough } case SC_WORLD_UNLOADED: diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index d30dc7f77..08903c7bd 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -2729,7 +2729,7 @@ static int filesystem_listdir_recursive(lua_State *L) include_prefix = lua_toboolean(L, 3); std::map files; int err = DFHack::Filesystem::listdir_recursive(dir, files, depth, include_prefix); - if (err != -1) { + if (err != 0 && err != -1) { lua_pushnil(L); lua_pushstring(L, strerror(err)); lua_pushinteger(L, err); diff --git a/library/lua/script-manager.lua b/library/lua/script-manager.lua index 1a7161a10..450012357 100644 --- a/library/lua/script-manager.lua +++ b/library/lua/script-manager.lua @@ -6,22 +6,27 @@ local utils = require('utils') -- enabled API -- for each script that can be loaded as a module, calls cb(script_name, env) -function foreach_module_script(cb) +function foreach_module_script(cb, preprocess_script_file_fn) for _,script_path in ipairs(dfhack.internal.getScriptPaths()) do local files = dfhack.filesystem.listdir_recursive( script_path, nil, false) if not files then goto skip_path end for _,f in ipairs(files) do - if not f.isdir and - f.path:endswith('.lua') and - not f.path:startswith('test/') and - not f.path:startswith('internal/') then - local script_name = f.path:sub(1, #f.path - 4) -- remove '.lua' - local ok, script_env = pcall(reqscript, script_name) - if ok then - cb(script_name, script_env) - end + if f.isdir or not f.path:endswith('.lua') or + f.path:startswith('.git') or + f.path:startswith('test/') or + f.path:startswith('internal/') then + goto continue end + if preprocess_script_file_fn then + preprocess_script_file_fn(script_path, f.path) + end + local script_name = f.path:sub(1, #f.path - 4) -- remove '.lua' + local ok, script_env = pcall(reqscript, script_name) + if ok then + cb(script_name, script_env) + end + ::continue:: end ::skip_path:: end @@ -42,9 +47,17 @@ local function process_script(env_name, env) enabled_map[env_name] = fn end -function reload() +function reload(refresh_active_mod_scripts) enabled_map = utils.OrderedTable() - foreach_module_script(process_script) + local force_refresh_fn = refresh_active_mod_scripts and function(script_path, script_name) + if script_path:find('scripts_modactive') then + internal_script = dfhack.internal.scripts[script_path..'/'..script_name] + if internal_script then + internal_script.env = nil + end + end + end or nil + foreach_module_script(process_script, force_refresh_fn) end local function ensure_loaded() @@ -97,7 +110,7 @@ end local function add_script_path(mod_script_paths, path) if dfhack.filesystem.isdir(path) then - print('indexing scripts from mod script path: ' .. path) + print('indexing mod scripts: ' .. path) table.insert(mod_script_paths, path) end end @@ -120,7 +133,7 @@ function get_mod_script_paths() -- if a world is loaded, process active mods first, and lock to active version if dfhack.isWorldLoaded() then for _,path in ipairs(df.global.world.object_loader.object_load_order_src_dir) do - path = tostring(path) + path = tostring(path.value) if not path:startswith(INSTALLED_MODS_PATH) then goto continue end local id = get_mod_id_and_version(path) if not id then goto continue end