dynamically add mod scripts to the script path

develop
Myk Taylor 2023-03-11 02:07:59 -08:00
parent 8b7223d1e2
commit 76bacee238
No known key found for this signature in database
GPG Key ID: 8A39CA0FA0C16E78
3 changed files with 91 additions and 14 deletions

@ -55,6 +55,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
- `automelt`: now allows metal chests to be melted (workaround for DF bug 2493 is no longer needed) - `automelt`: now allows metal chests to be melted (workaround for DF bug 2493 is no longer needed)
- `orders`: add minimize button to overlay panel so you can get it out of the way to read long statue descriptions when choosing a subject in the details screen - `orders`: add minimize button to overlay panel so you can get it out of the way to read long statue descriptions when choosing a subject in the details screen
- `enable`: can now interpret aliases defined with the `alias` command - `enable`: can now interpret aliases defined with the `alias` command
- scripts in installed mods are now automatically added to the DFHack script path. DFHack recognizes two directories in a mod's folder: ``scripts_modinstalled/`` and ``scripts_modactive/``. ``scripts_modinstalled/`` folders will always be added the script path, regardless of whether the mod is active in a world. ``scripts_modactive/`` folders will only be added to the script path when the mod is active in the current loaded world.
## Documentation ## Documentation
- ``untested`` tag has been renamed to ``unavailable`` to better reflect the status of the remaining unavaialable tools. all of the simply "untested" tools have now been tested and marked as working. the remaining tools are known to need development work before they are available again. - ``untested`` tag has been renamed to ``unavailable`` to better reflect the status of the remaining unavaialable tools. all of the simply "untested" tools have now been tested and marked as working. the remaining tools are known to need development work before they are available again.

@ -105,6 +105,7 @@ DBG_DECLARE(core,script,DebugCategory::LINFO);
static const std::string CONFIG_PATH = "dfhack-config/"; static const std::string CONFIG_PATH = "dfhack-config/";
static const std::string CONFIG_DEFAULTS_PATH = "hack/data/dfhack-config-defaults/"; static const std::string CONFIG_DEFAULTS_PATH = "hack/data/dfhack-config-defaults/";
static const std::string MOD_PATH = "data/installed_mods/";
class MainThread { class MainThread {
public: public:
@ -440,6 +441,12 @@ bool Core::addScriptPath(std::string path, bool search_before)
return true; return true;
} }
bool Core::setModScriptPaths(const std::vector<std::string> &mod_script_paths) {
std::lock_guard<std::mutex> lock(script_path_mutex);
script_paths[2] = mod_script_paths;
return true;
}
bool Core::removeScriptPath(std::string path) bool Core::removeScriptPath(std::string path)
{ {
std::lock_guard<std::mutex> lock(script_path_mutex); std::lock_guard<std::mutex> lock(script_path_mutex);
@ -464,20 +471,21 @@ void Core::getScriptPaths(std::vector<std::string> *dest)
std::lock_guard<std::mutex> lock(script_path_mutex); std::lock_guard<std::mutex> lock(script_path_mutex);
dest->clear(); dest->clear();
std::string df_path = this->p->getPath() + "/"; std::string df_path = this->p->getPath() + "/";
for (auto it = script_paths[0].begin(); it != script_paths[0].end(); ++it) for (auto & path : script_paths[0])
dest->push_back(*it); dest->emplace_back(path);
dest->push_back(df_path + CONFIG_PATH + "scripts"); dest->push_back(df_path + CONFIG_PATH + "scripts");
if (df::global::world && isWorldLoaded()) { if (df::global::world && isWorldLoaded()) {
std::string save = World::ReadWorldFolder(); std::string save = World::ReadWorldFolder();
if (save.size()) if (save.size())
dest->push_back(df_path + "/save/" + save + "/scripts"); dest->emplace_back(df_path + "save/" + save + "/scripts");
} }
dest->push_back(df_path + "/hack/scripts"); dest->emplace_back(df_path + "hack/scripts");
for (auto it = script_paths[1].begin(); it != script_paths[1].end(); ++it) for (auto & path : script_paths[2])
dest->push_back(*it); dest->emplace_back(path);
for (auto & path : script_paths[1])
dest->emplace_back(path);
} }
std::string Core::findScript(std::string name) std::string Core::findScript(std::string name)
{ {
std::vector<std::string> paths; std::vector<std::string> paths;
@ -526,6 +534,61 @@ bool loadScriptPaths(color_ostream &out, bool silent = false)
return true; return true;
} }
bool loadModScriptPaths(color_ostream &out) {
std::map<std::string, bool> 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<std::string> 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);
}
}
std::vector<std::string> 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);
}
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<std::string, state_change_event> state_change_event_map; static std::map<std::string, state_change_event> state_change_event_map;
static void sc_event_map_init() { static void sc_event_map_init() {
if (!state_change_event_map.size()) if (!state_change_event_map.size())
@ -2114,13 +2177,21 @@ void Core::onStateChange(color_ostream &out, state_change_event event)
{ {
case SC_CORE_INITIALIZED: case SC_CORE_INITIALIZED:
{ {
loadModScriptPaths(out);
auto L = Lua::Core::State; auto L = Lua::Core::State;
Lua::StackUnwinder top(L); Lua::StackUnwinder top(L);
Lua::CallLuaModuleFunction(con, L, "helpdb", "refresh"); Lua::CallLuaModuleFunction(con, L, "helpdb", "refresh");
Lua::CallLuaModuleFunction(con, L, "script-manager", "reload"); Lua::CallLuaModuleFunction(con, L, "script-manager", "reload");
}
break; break;
}
case SC_WORLD_LOADED: case SC_WORLD_LOADED:
{
loadModScriptPaths(out);
auto L = Lua::Core::State;
Lua::StackUnwinder top(L);
Lua::CallLuaModuleFunction(con, L, "script-manager", "reload");
// fallthrough
}
case SC_WORLD_UNLOADED: case SC_WORLD_UNLOADED:
case SC_MAP_LOADED: case SC_MAP_LOADED:
case SC_MAP_UNLOADED: case SC_MAP_UNLOADED:
@ -2185,6 +2256,10 @@ void Core::onStateChange(color_ostream &out, state_change_event event)
if (event == SC_WORLD_UNLOADED) if (event == SC_WORLD_UNLOADED)
{ {
Persistence::Internal::clear(); Persistence::Internal::clear();
loadModScriptPaths(out);
auto L = Lua::Core::State;
Lua::StackUnwinder top(L);
Lua::CallLuaModuleFunction(con, L, "script-manager", "reload");
} }
} }

@ -158,6 +158,7 @@ namespace DFHack
bool loadScriptFile(color_ostream &out, std::string fname, bool silent = false); bool loadScriptFile(color_ostream &out, std::string fname, bool silent = false);
bool addScriptPath(std::string path, bool search_before = false); bool addScriptPath(std::string path, bool search_before = false);
bool setModScriptPaths(const std::vector<std::string> &mod_script_paths);
bool removeScriptPath(std::string path); bool removeScriptPath(std::string path);
std::string findScript(std::string name); std::string findScript(std::string name);
void getScriptPaths(std::vector<std::string> *dest); void getScriptPaths(std::vector<std::string> *dest);
@ -239,7 +240,7 @@ namespace DFHack
std::vector<std::unique_ptr<Module>> allModules; std::vector<std::unique_ptr<Module>> allModules;
DFHack::PluginManager * plug_mgr; DFHack::PluginManager * plug_mgr;
std::vector<std::string> script_paths[2]; std::vector<std::string> script_paths[3];
std::mutex script_path_mutex; std::mutex script_path_mutex;
// hotkey-related stuff // hotkey-related stuff