From 986e64aed06be13182f74dcbce8edd4babb0fb53 Mon Sep 17 00:00:00 2001 From: Najeeb Al-Shabibi Date: Fri, 29 Sep 2023 12:27:09 +0100 Subject: [PATCH 01/16] new plugin preserve-tombs ensures that units that die keep their tomb assignments in death --- plugins/CMakeLists.txt | 1 + plugins/preserve-tombs.cpp | 243 +++++++++++++++++++++++++++++++++++++ 2 files changed, 244 insertions(+) create mode 100644 plugins/preserve-tombs.cpp diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index b96606284..392a69dcc 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -144,6 +144,7 @@ if(BUILD_SUPPORTED) dfhack_plugin(pathable pathable.cpp LINK_LIBRARIES lua) #dfhack_plugin(petcapRemover petcapRemover.cpp) #dfhack_plugin(plants plants.cpp) + dfhack_plugin(preserve-tombs preserve-tombs.cpp) dfhack_plugin(probe probe.cpp) dfhack_plugin(prospector prospector.cpp LINK_LIBRARIES lua) #dfhack_plugin(power-meter power-meter.cpp LINK_LIBRARIES lua) diff --git a/plugins/preserve-tombs.cpp b/plugins/preserve-tombs.cpp new file mode 100644 index 000000000..857597d23 --- /dev/null +++ b/plugins/preserve-tombs.cpp @@ -0,0 +1,243 @@ +#include "Debug.h" +#include "PluginManager.h" +#include "MiscUtils.h" + +#include +#include +#include +#include +#include +#include + +#include "modules/Units.h" +#include "modules/Buildings.h" +#include "modules/Persistence.h" +#include "modules/EventManager.h" +#include "modules/World.h" + +#include "df/world.h" +#include "df/unit.h" +#include "df/building.h" +#include "df/building_civzonest.h" + +using namespace DFHack; +using namespace df::enums; + + +// +DFHACK_PLUGIN("preserve-tombs"); +DFHACK_PLUGIN_IS_ENABLED(is_enabled); + +REQUIRE_GLOBAL(world); + + +static const std::string CONFIG_KEY = std::string(plugin_name) + "/config"; +static PersistentDataItem config; + +static int32_t cycle_timestamp; +static int32_t cycle_freq; + +enum ConfigValues { + CONFIG_IS_ENABLED = 0, + CONFIG_CYCLES = 1 +}; + +static std::unordered_map tomb_assignments; + +namespace DFHack { + DBG_DECLARE(preservetombs, config, DebugCategory::LINFO); +} + + +static int get_config_val(PersistentDataItem &c, int index) { + if (!c.isValid()) + return -1; + return c.ival(index); +} +static bool get_config_bool(PersistentDataItem &c, int index) { + return get_config_val(c, index) == 1; +} +static void set_config_val(PersistentDataItem &c, int index, int value) { + if (c.isValid()) + c.ival(index) = value; +} +static void set_config_bool(PersistentDataItem &c, int index, bool value) { + set_config_val(c, index, value ? 1 : 0); +} + +static bool assign_to_tomb(int32_t unit_id, int32_t building_id); +static void update_tomb_assignments(color_ostream& out); +void onUnitDeath(color_ostream& out, void* ptr); + +DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { + return CR_OK; +} + +// event listener +EventManager::EventHandler assign_tomb_handler(onUnitDeath, 0); + +DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { + if (!Core::getInstance().isWorldLoaded()) { + out.printerr("Cannot enable %s without a loaded world.\n", plugin_name); + return CR_FAILURE; + } + + if (enable != is_enabled) { + is_enabled = enable; + DEBUG(config,out).print("%s from the API; persisting\n", + is_enabled ? "enabled" : "disabled"); + set_config_bool(config, CONFIG_IS_ENABLED, is_enabled); + EventManager::registerListener(EventManager::EventType::UNIT_DEATH, assign_tomb_handler, plugin_self); + if (enable) + update_tomb_assignments(out); + } else { + EventManager::unregisterAll(plugin_self); + DEBUG(config,out).print("%s from the API, but already %s; no action\n", + is_enabled ? "enabled" : "disabled", + is_enabled ? "enabled" : "disabled"); + } + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown (color_ostream &out) { + DEBUG(config,out).print("shutting down %s\n", plugin_name); + + return CR_OK; +} + +DFhackCExport command_result plugin_load_data (color_ostream &out) { + cycle_timestamp = 0; + config = World::GetPersistentData(CONFIG_KEY); + + if (!config.isValid()) { + DEBUG(config,out).print("no config found in this save; initializing\n"); + config = World::AddPersistentData(CONFIG_KEY); + set_config_bool(config, CONFIG_IS_ENABLED, is_enabled); + set_config_val(config, CONFIG_CYCLES, 25); + } + + is_enabled = get_config_bool(config, CONFIG_IS_ENABLED); + cycle_freq = get_config_val(config, CONFIG_CYCLES); + DEBUG(config,out).print("loading persisted enabled state: %s\n", + is_enabled ? "true" : "false"); + + return CR_OK; +} + +DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) { + if (event == DFHack::SC_WORLD_UNLOADED) { + if (is_enabled) { + DEBUG(config,out).print("world unloaded; disabling %s\n", + plugin_name); + is_enabled = false; + } + } + return CR_OK; +} + +DFhackCExport command_result plugin_onupdate(color_ostream &out) { + if (is_enabled && world->frame_counter - cycle_timestamp >= cycle_freq) + update_tomb_assignments(out); + return CR_OK; +} +// + + + + +// On unit death - check if we assigned them a tomb +// +// +void onUnitDeath(color_ostream& out, void* ptr) { + // input is void* that contains the unit id + int32_t unit_id = reinterpret_cast(ptr); + + // check if unit was assigned a tomb in life + auto it = tomb_assignments.find(unit_id); + if (it == tomb_assignments.end()) return; + + // assign that unit to their previously assigned tomb in life + int32_t building_id = it->second; + if (!assign_to_tomb(unit_id, building_id)) return; + + // success, print status update and remove assignment from our memo-list + out.print("Unit %d died - assigning them to tomb %d\n", unit_id, building_id); + tomb_assignments.erase(it); + +} + + +// Update tomb assignments +// +// +static void update_tomb_assignments(color_ostream &out) { + + // check tomb civzones for assigned units + for (auto* bld : world->buildings.other.ZONE_TOMB) { + + auto* tomb = virtual_cast(bld); + if (!tomb || !tomb->flags.bits.exists) continue; + if (!tomb->assigned_unit) continue; + if (Units::isDead(tomb->assigned_unit)) continue; // we only care about living units + + auto it = tomb_assignments.find(tomb->assigned_unit_id); + + if (it == tomb_assignments.end()) { + tomb_assignments.emplace(tomb->assigned_unit_id, tomb->id); + out.print("%s new tomb assignment, unit %d to tomb %d\n", plugin_name, tomb->assigned_unit_id, tomb->id); + } + + else { + if (it->second != tomb->id) { + out.print("%s tomb assignment to %d changed, (old: %d, new: %d)\n", plugin_name, tomb->assigned_unit_id, it->second, tomb->id); + } + it->second = tomb->id; + } + + } + + // now check our civzones for unassignment / deleted zone / + std::erase_if(tomb_assignments, [&](const std::pair& pair){ + const auto &[unit_id, building_id] = pair; + + const size_t tomb_idx = binsearch_index(world->buildings.other.ZONE_TOMB, building_id); + if (tomb_idx == -1) { + out.print("%s tomb missing: %d - removing\n", plugin_name, building_id); + return true; + } + const auto tomb = virtual_cast(world->buildings.other.ZONE_TOMB[tomb_idx]); + if (!tomb || !tomb->flags.bits.exists) { + out.print("%s tomb missing: %d - removing\n", plugin_name, building_id); + return true; + } + if (tomb->assigned_unit_id != unit_id) { + out.print("%s unassigned unit %d from tomb %d - removing\n", unit_id, building_id); + return true; + } + + return false; + }); + +} + + +// ASSIGN UNIT TO TOMB +// +// +static bool assign_to_tomb(int32_t unit_id, int32_t building_id) { + + size_t unit_idx = Units::findIndexById(unit_id); + if (unit_idx == -1) return false; + + df::unit* unit = world->units.all[unit_idx]; + if (!Units::isDead(unit)) return false; + + size_t tomb_idx = binsearch_index(world->buildings.other.ZONE_TOMB, building_id); + if (tomb_idx == -1) return false; + + df::building_civzonest* tomb = virtual_cast(world->buildings.other.ZONE_TOMB[tomb_idx]); + if (!tomb || tomb->assigned_unit) return false; // in the game we cannot reassign tombs - more research is required to see if reassignment is safe. + + Buildings::setOwner(tomb, unit); + return true; +} \ No newline at end of file From 2a145d06b622b273f4ff619620581e1dc35fd6b9 Mon Sep 17 00:00:00 2001 From: Najeeb Al-Shabibi Date: Fri, 29 Sep 2023 13:11:13 +0100 Subject: [PATCH 02/16] fixed crash on tomb unassignment (caused by incorrect params passed to formatted string) --- plugins/preserve-tombs.cpp | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/plugins/preserve-tombs.cpp b/plugins/preserve-tombs.cpp index 857597d23..245ce5949 100644 --- a/plugins/preserve-tombs.cpp +++ b/plugins/preserve-tombs.cpp @@ -70,6 +70,7 @@ static void update_tomb_assignments(color_ostream& out); void onUnitDeath(color_ostream& out, void* ptr); DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { + tomb_assignments.clear(); return CR_OK; } @@ -77,6 +78,7 @@ DFhackCExport command_result plugin_init(color_ostream &out, std::vector & pair){ - const auto &[unit_id, building_id] = pair; + for (auto it = tomb_assignments.begin(); it != tomb_assignments.end(); ++it){ + auto &[unit_id, building_id] = *it; const size_t tomb_idx = binsearch_index(world->buildings.other.ZONE_TOMB, building_id); if (tomb_idx == -1) { out.print("%s tomb missing: %d - removing\n", plugin_name, building_id); - return true; + it = tomb_assignments.erase(it); + continue; } const auto tomb = virtual_cast(world->buildings.other.ZONE_TOMB[tomb_idx]); if (!tomb || !tomb->flags.bits.exists) { out.print("%s tomb missing: %d - removing\n", plugin_name, building_id); - return true; + it = tomb_assignments.erase(it); + continue; } if (tomb->assigned_unit_id != unit_id) { - out.print("%s unassigned unit %d from tomb %d - removing\n", unit_id, building_id); - return true; + out.print("%s unassigned unit %d from tomb %d - removing\n", plugin_name, unit_id, building_id); + it = tomb_assignments.erase(it); + continue; } - - return false; - }); + } } From b0a15b2e8a5319fb896cd927ee2b63ad00ba3775 Mon Sep 17 00:00:00 2001 From: Najeeb Al-Shabibi Date: Fri, 29 Sep 2023 13:38:52 +0100 Subject: [PATCH 03/16] added command to show status of preservetombs (is enabled or disabled) --- plugins/preserve-tombs.cpp | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/plugins/preserve-tombs.cpp b/plugins/preserve-tombs.cpp index 245ce5949..d80ca3b03 100644 --- a/plugins/preserve-tombs.cpp +++ b/plugins/preserve-tombs.cpp @@ -68,12 +68,27 @@ static void set_config_bool(PersistentDataItem &c, int index, bool value) { static bool assign_to_tomb(int32_t unit_id, int32_t building_id); static void update_tomb_assignments(color_ostream& out); void onUnitDeath(color_ostream& out, void* ptr); +static command_result do_command(color_ostream& out, std::vector& params); DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { - tomb_assignments.clear(); + commands.push_back(PluginCommand( + plugin_name, + "Preserves tomb assignments to units when they die.", + do_command)); return CR_OK; } +static command_result do_command(color_ostream& out, std::vector& params) { + if (params.size() != 1 || params[0] != "status") { + out.print("%s wrong usage", plugin_name); + return CR_WRONG_USAGE; + } + else { + out.print("%s is currently %s", plugin_name, is_enabled ? "enabled" : "disabled"); + return CR_OK; + } +} + // event listener EventManager::EventHandler assign_tomb_handler(onUnitDeath, 0); @@ -154,7 +169,7 @@ DFhackCExport command_result plugin_onupdate(color_ostream &out) { void onUnitDeath(color_ostream& out, void* ptr) { // input is void* that contains the unit id int32_t unit_id = reinterpret_cast(ptr); - + // check if unit was assigned a tomb in life auto it = tomb_assignments.find(unit_id); if (it == tomb_assignments.end()) return; @@ -199,10 +214,10 @@ static void update_tomb_assignments(color_ostream &out) { } - // now check our civzones for unassignment / deleted zone / + // now check our civzones for unassignment / deleted zone / for (auto it = tomb_assignments.begin(); it != tomb_assignments.end(); ++it){ auto &[unit_id, building_id] = *it; - + const size_t tomb_idx = binsearch_index(world->buildings.other.ZONE_TOMB, building_id); if (tomb_idx == -1) { out.print("%s tomb missing: %d - removing\n", plugin_name, building_id); @@ -229,13 +244,13 @@ static void update_tomb_assignments(color_ostream &out) { // // static bool assign_to_tomb(int32_t unit_id, int32_t building_id) { - + size_t unit_idx = Units::findIndexById(unit_id); if (unit_idx == -1) return false; - + df::unit* unit = world->units.all[unit_idx]; if (!Units::isDead(unit)) return false; - + size_t tomb_idx = binsearch_index(world->buildings.other.ZONE_TOMB, building_id); if (tomb_idx == -1) return false; From 0b2989fb1508fe6291ccee57dd4077f59b1f720d Mon Sep 17 00:00:00 2001 From: Najeeb Al-Shabibi Date: Fri, 29 Sep 2023 14:08:49 +0100 Subject: [PATCH 04/16] preservetombs status now shows list of all tracked tomb assignments --- plugins/preserve-tombs.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/plugins/preserve-tombs.cpp b/plugins/preserve-tombs.cpp index d80ca3b03..e9964d2f1 100644 --- a/plugins/preserve-tombs.cpp +++ b/plugins/preserve-tombs.cpp @@ -84,7 +84,14 @@ static command_result do_command(color_ostream& out, std::vector& p return CR_WRONG_USAGE; } else { - out.print("%s is currently %s", plugin_name, is_enabled ? "enabled" : "disabled"); + out.print("%s is currently %s\n", plugin_name, is_enabled ? "enabled" : "disabled"); + if (is_enabled) { + out.print("tracked tomb assignments:\n"); + std::for_each(tomb_assignments.begin(), tomb_assignments.end(), [&out](const auto& p){ + auto& [unit_id, building_id] = p; + out.print("unit %d -> building %d\n", unit_id, building_id); + }); + } return CR_OK; } } @@ -138,6 +145,7 @@ DFhackCExport command_result plugin_load_data (color_ostream &out) { DEBUG(config,out).print("loading persisted enabled state: %s\n", is_enabled ? "true" : "false"); + if (is_enabled) update_tomb_assignments(out); return CR_OK; } @@ -255,7 +263,7 @@ static bool assign_to_tomb(int32_t unit_id, int32_t building_id) { if (tomb_idx == -1) return false; df::building_civzonest* tomb = virtual_cast(world->buildings.other.ZONE_TOMB[tomb_idx]); - if (!tomb || tomb->assigned_unit) return false; // in the game we cannot reassign tombs - more research is required to see if reassignment is safe. + if (!tomb || tomb->assigned_unit) return false; Buildings::setOwner(tomb, unit); return true; From df5de8b7ec26b6fbc9bd6a7beb3de0f82707d12a Mon Sep 17 00:00:00 2001 From: Najeeb Al-Shabibi Date: Fri, 29 Sep 2023 14:21:44 +0100 Subject: [PATCH 05/16] added doc and changelog entry for preserve-tombs plugin --- docs/changelog.txt | 1 + docs/plugins/preserve-tombs.rst | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 docs/plugins/preserve-tombs.rst diff --git a/docs/changelog.txt b/docs/changelog.txt index f819587de..07ed84ebf 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -52,6 +52,7 @@ Template for new versions: # Future ## New Tools +- `preserve-tombs`: tracks tomb assignments to living units and ensures that the tomb stays assigned to them when they die. ## New Features diff --git a/docs/plugins/preserve-tombs.rst b/docs/plugins/preserve-tombs.rst new file mode 100644 index 000000000..7c148e3ee --- /dev/null +++ b/docs/plugins/preserve-tombs.rst @@ -0,0 +1,22 @@ +preserve-tombs +============== + +.. dfhack-tool:: + :summary: Fix tombs being unassigned to units on death + :tags: fort bugfix + +If you find that the tombs you assign to units get unassigned from them when +they die (e.g. your nobles), this tool can help fix that. + +Usage +----- + +:: + + enable preserve-tombs + preserve-tombs status + +This tool runs in the background. You can check the status of the plugin +by running ``preserve-tombs status`` which will show whether the plugin +is enabled and if so, display a list of all tracked tomb assignments +to living units. From 579fe6ee766222e595b17ba1a5534ad42fb9638a Mon Sep 17 00:00:00 2001 From: Najeeb Al-Shabibi Date: Fri, 29 Sep 2023 14:24:42 +0100 Subject: [PATCH 06/16] fixed sign compare issue linux build --- plugins/preserve-tombs.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/preserve-tombs.cpp b/plugins/preserve-tombs.cpp index e9964d2f1..8d82146b2 100644 --- a/plugins/preserve-tombs.cpp +++ b/plugins/preserve-tombs.cpp @@ -226,7 +226,7 @@ static void update_tomb_assignments(color_ostream &out) { for (auto it = tomb_assignments.begin(); it != tomb_assignments.end(); ++it){ auto &[unit_id, building_id] = *it; - const size_t tomb_idx = binsearch_index(world->buildings.other.ZONE_TOMB, building_id); + const int tomb_idx = binsearch_index(world->buildings.other.ZONE_TOMB, building_id); if (tomb_idx == -1) { out.print("%s tomb missing: %d - removing\n", plugin_name, building_id); it = tomb_assignments.erase(it); @@ -253,13 +253,13 @@ static void update_tomb_assignments(color_ostream &out) { // static bool assign_to_tomb(int32_t unit_id, int32_t building_id) { - size_t unit_idx = Units::findIndexById(unit_id); + const int unit_idx = Units::findIndexById(unit_id); if (unit_idx == -1) return false; df::unit* unit = world->units.all[unit_idx]; if (!Units::isDead(unit)) return false; - size_t tomb_idx = binsearch_index(world->buildings.other.ZONE_TOMB, building_id); + const int tomb_idx = binsearch_index(world->buildings.other.ZONE_TOMB, building_id); if (tomb_idx == -1) return false; df::building_civzonest* tomb = virtual_cast(world->buildings.other.ZONE_TOMB[tomb_idx]); From 028fbc34ade99fc91e43cee3d35655e87c0e736b Mon Sep 17 00:00:00 2001 From: Najeeb Al-Shabibi Date: Fri, 29 Sep 2023 14:30:01 +0100 Subject: [PATCH 07/16] using std::erase_if instead of iterator loop --- plugins/preserve-tombs.cpp | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/plugins/preserve-tombs.cpp b/plugins/preserve-tombs.cpp index 8d82146b2..6730dee76 100644 --- a/plugins/preserve-tombs.cpp +++ b/plugins/preserve-tombs.cpp @@ -222,28 +222,27 @@ static void update_tomb_assignments(color_ostream &out) { } - // now check our civzones for unassignment / deleted zone / - for (auto it = tomb_assignments.begin(); it != tomb_assignments.end(); ++it){ - auto &[unit_id, building_id] = *it; + // now check our civzones for unassignment / deleted zone + std::erase_if(tomb_assignments,[&](const auto& p){ + auto &[unit_id, building_id] = p; const int tomb_idx = binsearch_index(world->buildings.other.ZONE_TOMB, building_id); if (tomb_idx == -1) { out.print("%s tomb missing: %d - removing\n", plugin_name, building_id); - it = tomb_assignments.erase(it); - continue; + return true; } const auto tomb = virtual_cast(world->buildings.other.ZONE_TOMB[tomb_idx]); if (!tomb || !tomb->flags.bits.exists) { out.print("%s tomb missing: %d - removing\n", plugin_name, building_id); - it = tomb_assignments.erase(it); - continue; + return true; } if (tomb->assigned_unit_id != unit_id) { out.print("%s unassigned unit %d from tomb %d - removing\n", plugin_name, unit_id, building_id); - it = tomb_assignments.erase(it); - continue; + return true; } - } + + return false; + }); } From 7933e291191e58eba0cdb9e6c2e66c8d25eed2c9 Mon Sep 17 00:00:00 2001 From: Najeeb Al-Shabibi Date: Fri, 29 Sep 2023 14:45:46 +0100 Subject: [PATCH 08/16] newline at eof - preservetombs.cpp --- plugins/preserve-tombs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/preserve-tombs.cpp b/plugins/preserve-tombs.cpp index 6730dee76..0fa9d5b36 100644 --- a/plugins/preserve-tombs.cpp +++ b/plugins/preserve-tombs.cpp @@ -266,4 +266,4 @@ static bool assign_to_tomb(int32_t unit_id, int32_t building_id) { Buildings::setOwner(tomb, unit); return true; -} \ No newline at end of file +} From 6be9de5e518aec7cd751273782f22653d608623c Mon Sep 17 00:00:00 2001 From: Najeeb Al-Shabibi Date: Sat, 30 Sep 2023 12:30:40 +0100 Subject: [PATCH 09/16] preserve-tombs added option to change tick rate, changed default update frequency, changed some print to debug statements, some other adjustments --- docs/plugins/preserve-tombs.rst | 19 ++++---- plugins/preserve-tombs.cpp | 77 +++++++++++++++++++++++---------- 2 files changed, 65 insertions(+), 31 deletions(-) diff --git a/docs/plugins/preserve-tombs.rst b/docs/plugins/preserve-tombs.rst index 7c148e3ee..e667e64b0 100644 --- a/docs/plugins/preserve-tombs.rst +++ b/docs/plugins/preserve-tombs.rst @@ -11,12 +11,15 @@ they die (e.g. your nobles), this tool can help fix that. Usage ----- -:: +``enable preserve-tombs`` + enable the plugin +``preserve-tombs status`` + check the status of the plugin, and if the plugin is enabled, + lists all tracked tomb assignments +``preserve-tombs update`` + forces an immediate update of the tomb assignments. +``preserve-tombs freq [val]`` + changes the rate at which the plugin rechecks and updates + tomb assignments, in ticks (default is ``100``) - enable preserve-tombs - preserve-tombs status - -This tool runs in the background. You can check the status of the plugin -by running ``preserve-tombs status`` which will show whether the plugin -is enabled and if so, display a list of all tracked tomb assignments -to living units. +This tool runs in the background. \ No newline at end of file diff --git a/plugins/preserve-tombs.cpp b/plugins/preserve-tombs.cpp index 0fa9d5b36..f75b7aeb7 100644 --- a/plugins/preserve-tombs.cpp +++ b/plugins/preserve-tombs.cpp @@ -14,6 +14,7 @@ #include "modules/Persistence.h" #include "modules/EventManager.h" #include "modules/World.h" +#include "modules/Translation.h" #include "df/world.h" #include "df/unit.h" @@ -46,6 +47,8 @@ static std::unordered_map tomb_assignments; namespace DFHack { DBG_DECLARE(preservetombs, config, DebugCategory::LINFO); + DBG_DECLARE(preservetombs, cycle, DebugCategory::LINFO); + DBG_DECLARE(preservetombs, event, DebugCategory::LINFO); } @@ -79,28 +82,50 @@ DFhackCExport command_result plugin_init(color_ostream &out, std::vector & params) { - if (params.size() != 1 || params[0] != "status") { - out.print("%s wrong usage", plugin_name); + if (params.size() == 0) { + out.print("%s wrong usage\n", plugin_name); return CR_WRONG_USAGE; } - else { + if (params[0] == "status") { out.print("%s is currently %s\n", plugin_name, is_enabled ? "enabled" : "disabled"); if (is_enabled) { + out.print("Update frequency: %d ticks", cycle_freq); out.print("tracked tomb assignments:\n"); std::for_each(tomb_assignments.begin(), tomb_assignments.end(), [&out](const auto& p){ auto& [unit_id, building_id] = p; - out.print("unit %d -> building %d\n", unit_id, building_id); + auto* unit = df::unit::find(unit_id); + std::string name = unit ? Translation::TranslateName(&unit->name) : "UNKNOWN UNIT" ; + out.print("%s (id %d) -> building %d\n", name.c_str(), unit_id, building_id); }); } return CR_OK; } + if (params[0] == "update") { + CoreSuspender suspend; + update_tomb_assignments(out); + out.print("Updated tomb assignments\n"); + return CR_OK; + } + if (params.size() < 2) { + out.print("%s wrong usage\n", plugin_name); + return CR_WRONG_USAGE; + } + if (params[0] == "ticks" || params[0] == "freq" || params[0] == "rate") { + int new_tickrate = std::stoi(params[1]); + if (new_tickrate <= 0) { + out.print("new tickrate (%d) cannot be <= 0\n", new_tickrate); + return CR_WRONG_USAGE; + } + cycle_freq = new_tickrate; + set_config_val(config, CONFIG_CYCLES, cycle_freq); + } + return CR_WRONG_USAGE; } // event listener EventManager::EventHandler assign_tomb_handler(onUnitDeath, 0); DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { - tomb_assignments.clear(); if (!Core::getInstance().isWorldLoaded()) { out.printerr("Cannot enable %s without a loaded world.\n", plugin_name); return CR_FAILURE; @@ -111,11 +136,15 @@ DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { DEBUG(config,out).print("%s from the API; persisting\n", is_enabled ? "enabled" : "disabled"); set_config_bool(config, CONFIG_IS_ENABLED, is_enabled); - EventManager::registerListener(EventManager::EventType::UNIT_DEATH, assign_tomb_handler, plugin_self); - if (enable) + if (enable) { + EventManager::registerListener(EventManager::EventType::UNIT_DEATH, assign_tomb_handler, plugin_self); update_tomb_assignments(out); + } + else { + tomb_assignments.clear(); + EventManager::unregisterAll(plugin_self); + } } else { - EventManager::unregisterAll(plugin_self); DEBUG(config,out).print("%s from the API, but already %s; no action\n", is_enabled ? "enabled" : "disabled", is_enabled ? "enabled" : "disabled"); @@ -137,7 +166,7 @@ DFhackCExport command_result plugin_load_data (color_ostream &out) { DEBUG(config,out).print("no config found in this save; initializing\n"); config = World::AddPersistentData(CONFIG_KEY); set_config_bool(config, CONFIG_IS_ENABLED, is_enabled); - set_config_val(config, CONFIG_CYCLES, 25); + set_config_val(config, CONFIG_CYCLES, 100); } is_enabled = get_config_bool(config, CONFIG_IS_ENABLED); @@ -145,7 +174,6 @@ DFhackCExport command_result plugin_load_data (color_ostream &out) { DEBUG(config,out).print("loading persisted enabled state: %s\n", is_enabled ? "true" : "false"); - if (is_enabled) update_tomb_assignments(out); return CR_OK; } @@ -157,6 +185,7 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan plugin_name); is_enabled = false; } + EventManager::unregisterAll(plugin_self); } return CR_OK; } @@ -184,10 +213,12 @@ void onUnitDeath(color_ostream& out, void* ptr) { // assign that unit to their previously assigned tomb in life int32_t building_id = it->second; - if (!assign_to_tomb(unit_id, building_id)) return; - + if (!assign_to_tomb(unit_id, building_id)) { + DEBUG(event, out).print("Unit %d died - but failed to assign them to tomb %d\n", unit_id, building_id); + return; + } // success, print status update and remove assignment from our memo-list - out.print("Unit %d died - assigning them to tomb %d\n", unit_id, building_id); + INFO(event, out).print("Unit %d died - assigning them to tomb %d\n", unit_id, building_id); tomb_assignments.erase(it); } @@ -197,7 +228,7 @@ void onUnitDeath(color_ostream& out, void* ptr) { // // static void update_tomb_assignments(color_ostream &out) { - + cycle_timestamp = world->frame_counter; // check tomb civzones for assigned units for (auto* bld : world->buildings.other.ZONE_TOMB) { @@ -210,12 +241,14 @@ static void update_tomb_assignments(color_ostream &out) { if (it == tomb_assignments.end()) { tomb_assignments.emplace(tomb->assigned_unit_id, tomb->id); - out.print("%s new tomb assignment, unit %d to tomb %d\n", plugin_name, tomb->assigned_unit_id, tomb->id); + DEBUG(cycle, out).print("%s new tomb assignment, unit %d to tomb %d\n", + plugin_name, tomb->assigned_unit_id, tomb->id); } else { if (it->second != tomb->id) { - out.print("%s tomb assignment to %d changed, (old: %d, new: %d)\n", plugin_name, tomb->assigned_unit_id, it->second, tomb->id); + DEBUG(cycle, out).print("%s tomb assignment to %d changed, (old: %d, new: %d)\n", + plugin_name, tomb->assigned_unit_id, it->second, tomb->id); } it->second = tomb->id; } @@ -228,16 +261,16 @@ static void update_tomb_assignments(color_ostream &out) { const int tomb_idx = binsearch_index(world->buildings.other.ZONE_TOMB, building_id); if (tomb_idx == -1) { - out.print("%s tomb missing: %d - removing\n", plugin_name, building_id); + DEBUG(cycle, out).print("%s tomb missing: %d - removing\n", plugin_name, building_id); return true; } const auto tomb = virtual_cast(world->buildings.other.ZONE_TOMB[tomb_idx]); if (!tomb || !tomb->flags.bits.exists) { - out.print("%s tomb missing: %d - removing\n", plugin_name, building_id); + DEBUG(cycle, out).print("%s tomb missing: %d - removing\n", plugin_name, building_id); return true; } if (tomb->assigned_unit_id != unit_id) { - out.print("%s unassigned unit %d from tomb %d - removing\n", plugin_name, unit_id, building_id); + DEBUG(cycle, out).print("%s unassigned unit %d from tomb %d - removing\n", plugin_name, unit_id, building_id); return true; } @@ -252,11 +285,9 @@ static void update_tomb_assignments(color_ostream &out) { // static bool assign_to_tomb(int32_t unit_id, int32_t building_id) { - const int unit_idx = Units::findIndexById(unit_id); - if (unit_idx == -1) return false; + df::unit* unit = df::unit::find(unit_id); - df::unit* unit = world->units.all[unit_idx]; - if (!Units::isDead(unit)) return false; + if (!unit || !Units::isDead(unit)) return false; const int tomb_idx = binsearch_index(world->buildings.other.ZONE_TOMB, building_id); if (tomb_idx == -1) return false; From 7e75fd6ebc10faf5237afa1f9fa2fec7639371b9 Mon Sep 17 00:00:00 2001 From: Najeeb Al-Shabibi Date: Sat, 30 Sep 2023 12:49:46 +0100 Subject: [PATCH 10/16] eof fix --- docs/plugins/preserve-tombs.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/preserve-tombs.rst b/docs/plugins/preserve-tombs.rst index e667e64b0..846c6c553 100644 --- a/docs/plugins/preserve-tombs.rst +++ b/docs/plugins/preserve-tombs.rst @@ -22,4 +22,4 @@ Usage changes the rate at which the plugin rechecks and updates tomb assignments, in ticks (default is ``100``) -This tool runs in the background. \ No newline at end of file +This tool runs in the background. From e2dcced8ef23861f64daa34e7960af1785c1497d Mon Sep 17 00:00:00 2001 From: Najeeb Al-Shabibi Date: Sat, 30 Sep 2023 13:21:04 +0100 Subject: [PATCH 11/16] preserve-tombs command guarded from using update argument when plugin not enabled --- plugins/preserve-tombs.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/preserve-tombs.cpp b/plugins/preserve-tombs.cpp index f75b7aeb7..9216a5de4 100644 --- a/plugins/preserve-tombs.cpp +++ b/plugins/preserve-tombs.cpp @@ -101,6 +101,10 @@ static command_result do_command(color_ostream& out, std::vector& p return CR_OK; } if (params[0] == "update") { + if (!is_enabled) { + out.printerr("Cannot update %s when not enabled", plugin_name); + return CR_FAILURE; + } CoreSuspender suspend; update_tomb_assignments(out); out.print("Updated tomb assignments\n"); From 7d3764d3ec1708e6b8d8feef3236914f60463983 Mon Sep 17 00:00:00 2001 From: Najeeb Al-Shabibi Date: Sat, 30 Sep 2023 14:42:29 +0100 Subject: [PATCH 12/16] removed option to set tickrate for preserve-tombs, fixed at 100 tick interval --- docs/plugins/preserve-tombs.rst | 12 +++++------- plugins/preserve-tombs.cpp | 27 +++++---------------------- 2 files changed, 10 insertions(+), 29 deletions(-) diff --git a/docs/plugins/preserve-tombs.rst b/docs/plugins/preserve-tombs.rst index 846c6c553..045d15a5a 100644 --- a/docs/plugins/preserve-tombs.rst +++ b/docs/plugins/preserve-tombs.rst @@ -13,13 +13,11 @@ Usage ``enable preserve-tombs`` enable the plugin -``preserve-tombs status`` +``preserve-tombs [status]`` check the status of the plugin, and if the plugin is enabled, - lists all tracked tomb assignments + lists all currently tracked tomb assignments ``preserve-tombs update`` - forces an immediate update of the tomb assignments. -``preserve-tombs freq [val]`` - changes the rate at which the plugin rechecks and updates - tomb assignments, in ticks (default is ``100``) + forces an immediate update of the tomb assignments. This plugin + automatically updates the tomb assignments once every 100 ticks. -This tool runs in the background. +This tool runs in the background. diff --git a/plugins/preserve-tombs.cpp b/plugins/preserve-tombs.cpp index 9216a5de4..3e5cea1d2 100644 --- a/plugins/preserve-tombs.cpp +++ b/plugins/preserve-tombs.cpp @@ -36,11 +36,10 @@ static const std::string CONFIG_KEY = std::string(plugin_name) + "/config"; static PersistentDataItem config; static int32_t cycle_timestamp; -static int32_t cycle_freq; +static constexpr int32_t cycle_freq = 100; enum ConfigValues { CONFIG_IS_ENABLED = 0, - CONFIG_CYCLES = 1 }; static std::unordered_map tomb_assignments; @@ -82,14 +81,13 @@ DFhackCExport command_result plugin_init(color_ostream &out, std::vector & params) { - if (params.size() == 0) { - out.print("%s wrong usage\n", plugin_name); - return CR_WRONG_USAGE; + if (!Core::getInstance().isWorldLoaded()) { + out.printerr("Cannot use %s without a loaded world.\n", plugin_name); + return CR_FAILURE; } - if (params[0] == "status") { + if (params.size() == 0 || params[0] == "status") { out.print("%s is currently %s\n", plugin_name, is_enabled ? "enabled" : "disabled"); if (is_enabled) { - out.print("Update frequency: %d ticks", cycle_freq); out.print("tracked tomb assignments:\n"); std::for_each(tomb_assignments.begin(), tomb_assignments.end(), [&out](const auto& p){ auto& [unit_id, building_id] = p; @@ -110,19 +108,6 @@ static command_result do_command(color_ostream& out, std::vector& p out.print("Updated tomb assignments\n"); return CR_OK; } - if (params.size() < 2) { - out.print("%s wrong usage\n", plugin_name); - return CR_WRONG_USAGE; - } - if (params[0] == "ticks" || params[0] == "freq" || params[0] == "rate") { - int new_tickrate = std::stoi(params[1]); - if (new_tickrate <= 0) { - out.print("new tickrate (%d) cannot be <= 0\n", new_tickrate); - return CR_WRONG_USAGE; - } - cycle_freq = new_tickrate; - set_config_val(config, CONFIG_CYCLES, cycle_freq); - } return CR_WRONG_USAGE; } @@ -170,11 +155,9 @@ DFhackCExport command_result plugin_load_data (color_ostream &out) { DEBUG(config,out).print("no config found in this save; initializing\n"); config = World::AddPersistentData(CONFIG_KEY); set_config_bool(config, CONFIG_IS_ENABLED, is_enabled); - set_config_val(config, CONFIG_CYCLES, 100); } is_enabled = get_config_bool(config, CONFIG_IS_ENABLED); - cycle_freq = get_config_val(config, CONFIG_CYCLES); DEBUG(config,out).print("loading persisted enabled state: %s\n", is_enabled ? "true" : "false"); From a8e09ac8d4e84f667e59f4bb260b8fad96304c9a Mon Sep 17 00:00:00 2001 From: Najeeb Al-Shabibi Date: Sat, 30 Sep 2023 14:55:22 +0100 Subject: [PATCH 13/16] removed trailing ws --- docs/plugins/preserve-tombs.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/preserve-tombs.rst b/docs/plugins/preserve-tombs.rst index 045d15a5a..67e7ceff6 100644 --- a/docs/plugins/preserve-tombs.rst +++ b/docs/plugins/preserve-tombs.rst @@ -20,4 +20,4 @@ Usage forces an immediate update of the tomb assignments. This plugin automatically updates the tomb assignments once every 100 ticks. -This tool runs in the background. +This tool runs in the background. From 0956c06341265180804564f3a22536bb4ac988ff Mon Sep 17 00:00:00 2001 From: Najeeb Al-Shabibi Date: Sun, 1 Oct 2023 17:33:53 +0100 Subject: [PATCH 14/16] update arg changed to 'now' for consistency --- docs/plugins/preserve-tombs.rst | 2 +- plugins/preserve-tombs.cpp | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/docs/plugins/preserve-tombs.rst b/docs/plugins/preserve-tombs.rst index 67e7ceff6..e1d0ba8ce 100644 --- a/docs/plugins/preserve-tombs.rst +++ b/docs/plugins/preserve-tombs.rst @@ -16,7 +16,7 @@ Usage ``preserve-tombs [status]`` check the status of the plugin, and if the plugin is enabled, lists all currently tracked tomb assignments -``preserve-tombs update`` +``preserve-tombs now`` forces an immediate update of the tomb assignments. This plugin automatically updates the tomb assignments once every 100 ticks. diff --git a/plugins/preserve-tombs.cpp b/plugins/preserve-tombs.cpp index 3e5cea1d2..976b6bd22 100644 --- a/plugins/preserve-tombs.cpp +++ b/plugins/preserve-tombs.cpp @@ -98,7 +98,7 @@ static command_result do_command(color_ostream& out, std::vector& p } return CR_OK; } - if (params[0] == "update") { + if (params[0] == "now") { if (!is_enabled) { out.printerr("Cannot update %s when not enabled", plugin_name); return CR_FAILURE; @@ -232,11 +232,9 @@ static void update_tomb_assignments(color_ostream &out) { plugin_name, tomb->assigned_unit_id, tomb->id); } - else { - if (it->second != tomb->id) { - DEBUG(cycle, out).print("%s tomb assignment to %d changed, (old: %d, new: %d)\n", - plugin_name, tomb->assigned_unit_id, it->second, tomb->id); - } + else if (it->second != tomb->id) { + DEBUG(cycle, out).print("%s tomb assignment to %d changed, (old: %d, new: %d)\n", + plugin_name, tomb->assigned_unit_id, it->second, tomb->id); it->second = tomb->id; } @@ -257,7 +255,7 @@ static void update_tomb_assignments(color_ostream &out) { return true; } if (tomb->assigned_unit_id != unit_id) { - DEBUG(cycle, out).print("%s unassigned unit %d from tomb %d - removing\n", plugin_name, unit_id, building_id); + DEBUG(cycle, out).print("%s unit %d unassigned from tomb %d - removing\n", plugin_name, unit_id, building_id); return true; } From 89be6f56ef60303dbc0b34e22f7d87a128913701 Mon Sep 17 00:00:00 2001 From: Myk Date: Thu, 5 Oct 2023 12:56:46 -0700 Subject: [PATCH 15/16] Apply suggestions from code review --- plugins/preserve-tombs.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/preserve-tombs.cpp b/plugins/preserve-tombs.cpp index 976b6bd22..be560e1ce 100644 --- a/plugins/preserve-tombs.cpp +++ b/plugins/preserve-tombs.cpp @@ -75,7 +75,7 @@ static command_result do_command(color_ostream& out, std::vector& p DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { commands.push_back(PluginCommand( plugin_name, - "Preserves tomb assignments to units when they die.", + "Preserve tomb assignments when assigned units die.", do_command)); return CR_OK; } @@ -144,6 +144,8 @@ DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { DFhackCExport command_result plugin_shutdown (color_ostream &out) { DEBUG(config,out).print("shutting down %s\n", plugin_name); +// PluginManager handles unregistering our handler from EventManager, +// so we don't have to do that here return CR_OK; } @@ -201,11 +203,11 @@ void onUnitDeath(color_ostream& out, void* ptr) { // assign that unit to their previously assigned tomb in life int32_t building_id = it->second; if (!assign_to_tomb(unit_id, building_id)) { - DEBUG(event, out).print("Unit %d died - but failed to assign them to tomb %d\n", unit_id, building_id); + WARN(event, out).print("Unit %d died - but failed to assign them back to their tomb %d\n", unit_id, building_id); return; } // success, print status update and remove assignment from our memo-list - INFO(event, out).print("Unit %d died - assigning them to tomb %d\n", unit_id, building_id); + INFO(event, out).print("Unit %d died - assigning them back to their tomb\n", unit_id); tomb_assignments.erase(it); } From bad0448d345bfbb7a7a3cffd4c682a7673ac485d Mon Sep 17 00:00:00 2001 From: Myk Date: Thu, 5 Oct 2023 12:57:05 -0700 Subject: [PATCH 16/16] Update docs/plugins/preserve-tombs.rst --- docs/plugins/preserve-tombs.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/preserve-tombs.rst b/docs/plugins/preserve-tombs.rst index e1d0ba8ce..2f01162c6 100644 --- a/docs/plugins/preserve-tombs.rst +++ b/docs/plugins/preserve-tombs.rst @@ -2,7 +2,7 @@ preserve-tombs ============== .. dfhack-tool:: - :summary: Fix tombs being unassigned to units on death + :summary: Preserve tomb assignments when assigned units die. :tags: fort bugfix If you find that the tombs you assign to units get unassigned from them when