From 5bf3093139cf63b273b06dce014b6cb60001fb4f Mon Sep 17 00:00:00 2001 From: expwnent Date: Tue, 15 Sep 2015 07:27:42 -0400 Subject: [PATCH] Now it looks for dfhack*.init instead of just dfhack.init, onLoad*.init instead of just onLoad.init, etc. --- NEWS | 1 + Readme.rst | 25 +++++++++------ library/Core.cpp | 81 ++++++++++++++++++++++++++++++++++-------------- 3 files changed, 73 insertions(+), 34 deletions(-) diff --git a/NEWS b/NEWS index f0bb73189..660d61b1c 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,7 @@ DFHack Future Multiple contexts can now be specified when adding keybindings Keybindings can now use F10-F12 and 0-9 Plugin system is no longer restricted to plugins that exist on startup + dfhack.init file locations significantly generalized Lua Scripts can be enabled with the built-in enable/disable commands A new function, reqscript(), is available as a safer alternative to script_environment() diff --git a/Readme.rst b/Readme.rst index cfcf1609a..e9ec9f4fd 100644 --- a/Readme.rst +++ b/Readme.rst @@ -180,16 +180,21 @@ tracker on github, or visit the #dfhack IRC channel on freenode. ============= The init file ============= -If your DF folder contains a file named ``dfhack.init``, its contents will be -run every time you start DF. This allows keybindings and other settings to -persist across runs. An example file is provided as ``dfhack.init-example`` - -you can tweak it and rename to ``dfhack.init`` if you want to use this -functionality. If only the example init file is found, will be used and a -warning will be shown. - -When a savegame is loaded, an ``onLoad.init`` file in its raw folder is run, -as a save-portable alternative to ``dfhack.init``. It is recommended that -modders use this to improve mobility of save games and compatibility of mods. +DFHack allows users to automatically run commonly-used DFHack commands when DF is first loaded, when a game is loaded, and when a game is unloaded. +Init scripts function the same way they would if the user manually typed in their contents, but are much more convenient. +If your DF folder contains at least one file with a name following the format ``dfhack*.init`` where ``*`` is a placeholder for any string (including the empty string), then all such files are executed in alphabetical order as init scripts when DF is first loaded. +If your DF folder does not contain any such files, then DFHack will execute ``dfhack.init-example`` as an example of useful commands to be run automatically. +If you want DFHack to do nothing on its own, then create an empty ``dfhack.init`` file in the main DF directory, or delete ``dfhack.init-example``. +The file ``dfhack.init-example`` is included as an example for users to follow if they need DFHack command executed automatically. +We highly recommend modifying or deleting ``dfhack.init-example`` as its settings will not be optimal for all players. + +In order to facilitate savegave portability, mod merging, and general organization of init files, DFHack supports multiple init files both in the main DF directory and save-specific init files in the save folders. +DFHack looks for init files in three places. +It will look for them in the main DF directory, and in ``data/save/_____/raw`` and ``data/save/_____/raw/objects`` where ``_____`` is the name of the current savegame. +When a game is loaded, DFHack looks for files of the form ``onLoad*.init``, where ``*`` can be any string, including the empty string. +When a game is unloaded, DFHack looks for files of the form ``onUnload*.init``. +Again, these files may be in any of the above three places. +All matching init files will be sorted and executed in alphebetical order. Setting keybindings =================== diff --git a/library/Core.cpp b/library/Core.cpp index c5834875e..bbc07472d 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -85,6 +85,7 @@ using df::global::world; // FIXME: A lot of code in one file, all doing different things... there's something fishy about it. static bool parseKeySpec(std::string keyspec, int *psym, int *pmod, std::string *pfocus = NULL); +size_t loadScriptFiles(Core* core, color_ostream& out, const vector& prefix, const std::string& folder); struct Core::Cond { @@ -1209,8 +1210,10 @@ static void run_dfhack_init(color_ostream &out, Core *core) out.printerr("Key globals are missing, skipping loading dfhack.init.\n"); return; } - - if (!core->loadScriptFile(out, "dfhack.init", true)) + + std::vector prefixes(1, "dfhack"); + size_t count = loadScriptFiles(core, out, prefixes, "."); + if (!count) { core->runCommand(out, "gui/no-dfhack-init"); core->loadScriptFile(out, "dfhack.init-example", true); @@ -1829,7 +1832,54 @@ void Core::onUpdate(color_ostream &out) Lua::Core::onUpdate(out); } +void getFilesWithPrefixAndSuffix(const std::string& folder, const std::string& prefix, const std::string& suffix, std::vector& result) { + //DFHACK_EXPORT int listdir (std::string dir, std::vector &files); + std::vector files; + DFHack::Filesystem::listdir(folder, files); + for ( size_t a = 0; a < files.size(); a++ ) { + if ( prefix.length() > files[a].length() ) + continue; + if ( suffix.length() > files[a].length() ) + continue; + if ( files[a].compare(0, prefix.length(), prefix) != 0 ) + continue; + if ( files[a].compare(files[a].length()-suffix.length(), suffix.length(), suffix) != 0 ) + continue; + result.push_back(files[a]); + } + return; +} + +size_t loadScriptFiles(Core* core, color_ostream& out, const vector& prefix, const std::string& folder) { + vector scriptFiles; + for ( size_t a = 0; a < prefix.size(); a++ ) { + getFilesWithPrefixAndSuffix(folder, prefix[a], ".init", scriptFiles); + } + std::sort(scriptFiles.begin(), scriptFiles.end()); + size_t result = 0; + for ( size_t a = 0; a < scriptFiles.size(); a++ ) { + result++; + core->loadScriptFile(out, folder + scriptFiles[a], true); + } + return result; +} + void Core::handleLoadAndUnloadScripts(color_ostream& out, state_change_event event) { + static std::vector >* table; + if ( !table ) { + table = new std::vector >(1+SC_UNPAUSED); + (*table)[SC_WORLD_LOADED ].push_back("onLoad"); + (*table)[SC_WORLD_LOADED ].push_back("onLoadWorld"); + (*table)[SC_WORLD_LOADED ].push_back("onWorldLoad"); + (*table)[SC_WORLD_UNLOADED].push_back("onUnload"); + (*table)[SC_WORLD_UNLOADED].push_back("onUnloadWorld"); + (*table)[SC_WORLD_UNLOADED].push_back("onWorldUnload"); + (*table)[SC_MAP_LOADED ].push_back("onLoadMap"); + (*table)[SC_MAP_LOADED ].push_back("onMapLoad"); + (*table)[SC_MAP_UNLOADED].push_back("onMapUnload"); + (*table)[SC_MAP_UNLOADED].push_back("onUnloadMap"); + } + if (!df::global::world) return; //TODO: use different separators for windows @@ -1839,28 +1889,11 @@ void Core::handleLoadAndUnloadScripts(color_ostream& out, state_change_event eve static const std::string separator = "/"; #endif std::string rawFolder = "data" + separator + "save" + separator + (df::global::world->cur_savegame.save_dir) + separator + "raw" + separator; - switch(event) { - case SC_WORLD_LOADED: - loadScriptFile(out, "onLoadWorld.init", true); - loadScriptFile(out, rawFolder + "onLoadWorld.init", true); - loadScriptFile(out, rawFolder + "onLoad.init", true); - break; - case SC_WORLD_UNLOADED: - loadScriptFile(out, "onUnloadWorld.init", true); - loadScriptFile(out, rawFolder + "onUnloadWorld.init", true); - loadScriptFile(out, rawFolder + "onUnload.init", true); - break; - case SC_MAP_LOADED: - loadScriptFile(out, "onLoadMap.init", true); - loadScriptFile(out, rawFolder + "onLoadMap.init", true); - break; - case SC_MAP_UNLOADED: - loadScriptFile(out, "onUnloadMap.init", true); - loadScriptFile(out, rawFolder + "onUnloadMap.init", true); - break; - default: - break; - } + + const std::vector& set = (*table)[event]; + loadScriptFiles(this, out, set, "." ); + loadScriptFiles(this, out, set, rawFolder); + loadScriptFiles(this, out, set, rawFolder + "objects" + separator); for (auto it = state_change_scripts.begin(); it != state_change_scripts.end(); ++it) {