diff --git a/docs/plugins/channel-safely.rst b/docs/plugins/channel-safely.rst index 1fce8043d..4106aee04 100644 --- a/docs/plugins/channel-safely.rst +++ b/docs/plugins/channel-safely.rst @@ -2,12 +2,12 @@ channel-safely ============== .. dfhack-tool:: - :summary: Auto-manage channel designations to keep dwarves safe + :summary: Auto-manage channel designations to keep dwarves safe. :tags: fort auto Multi-level channel projects can be dangerous, and managing the safety of your dwarves throughout the completion of such projects can be difficult and time -consuming. This plugin keeps your dwarves safe (while channeling) so you don't +consuming. This plugin keeps your dwarves safe (at least while channeling) so you don't have to. Now you can focus on designing your dwarven cities with the deep chasms they were meant to have. @@ -18,7 +18,7 @@ Usage enable channel-safely channel-safely set channel-safely enable|disable - channel-safely runonce + channel-safely When enabled the map will be scanned for channel designations which will be grouped together based on adjacency and z-level. These groups will then be analyzed for safety @@ -38,13 +38,20 @@ Examples ``channel-safely disable require-vision`` Allows the plugin to read all tiles, including the ones your dwarves know nothing about. -``channel-safely enable monitor-active`` +``channel-safely enable monitor`` Enables monitoring active channel digging jobs. Meaning that if another unit it present or the tile below becomes open space the job will be paused or canceled (respectively). ``channel-safely set ignore-threshold 3`` Configures the plugin to ignore designations equal to or above priority 3 designations. +Commands +-------- + +:runonce: Run the safety procedures once to set the marker mode of designations. +:rebuild: Rebuild the designation group data. Intended for to be used in the event + the marker mode isn't being set correctly (mostly for debugging). + Features -------- diff --git a/plugins/channel-safely/CMakeLists.txt b/plugins/channel-safely/CMakeLists.txt index d660d2262..36c7307e4 100644 --- a/plugins/channel-safely/CMakeLists.txt +++ b/plugins/channel-safely/CMakeLists.txt @@ -2,7 +2,6 @@ project(channel-safely) include_directories(include) SET(SOURCES - channel-jobs.cpp channel-groups.cpp channel-manager.cpp channel-safely-plugin.cpp) diff --git a/plugins/channel-safely/channel-groups.cpp b/plugins/channel-safely/channel-groups.cpp index 1a7f81a13..52f7e6c40 100644 --- a/plugins/channel-safely/channel-groups.cpp +++ b/plugins/channel-safely/channel-groups.cpp @@ -6,7 +6,18 @@ #include - +// iterates the DF job list and adds channel jobs to the `jobs` container +void ChannelJobs::load_channel_jobs() { + locations.clear(); + df::job_list_link* node = df::global::world->jobs.list.next; + while (node) { + df::job* job = node->item; + node = node->next; + if (is_dig_job(job)) { + locations.emplace(job->pos); + } + } +} // adds map_pos to a group if an adjacent one exists, or creates one if none exist... if multiple exist they're merged into the first found void ChannelGroups::add(const df::coord &map_pos) { @@ -148,6 +159,7 @@ void ChannelGroups::scan() { // the tile, check if it has a channel designation df::coord map_pos((bx * 16) + lx, (by * 16) + ly, z); if (TileCache::Get().hasChanged(map_pos, block->tiletype[lx][ly])) { + TileCache::Get().uncache(map_pos); remove(map_pos); if (jobs.count(map_pos)) { jobs.erase(map_pos); @@ -165,6 +177,8 @@ void ChannelGroups::scan() { } TRACE(groups).print(" adding (" COORD ")\n", COORDARGS(map_pos)); add(map_pos); + } else if (groups_map.count(map_pos)) { + remove(map_pos); } } } @@ -252,21 +266,25 @@ size_t ChannelGroups::count(const df::coord &map_pos) const { // prints debug info about the groups stored, and their members void ChannelGroups::debug_groups() { - int idx = 0; - TRACE(groups).print(" debugging group data\n"); - for (auto &group : groups) { - TRACE(groups).print(" group %d (size: %zu)\n", idx, group.size()); - for (auto &pos : group) { - TRACE(groups).print(" (%d,%d,%d)\n", pos.x, pos.y, pos.z); + if (DFHack::debug_groups.isEnabled(DebugCategory::LTRACE)) { + int idx = 0; + TRACE(groups).print(" debugging group data\n"); + for (auto &group: groups) { + TRACE(groups).print(" group %d (size: %zu)\n", idx, group.size()); + for (auto &pos: group) { + TRACE(groups).print(" (%d,%d,%d)\n", pos.x, pos.y, pos.z); + } + idx++; } - idx++; } } // prints debug info group mappings void ChannelGroups::debug_map() { - INFO(groups).print("Group Mappings: %zu\n", groups_map.size()); - for (auto &pair : groups_map) { - DEBUG(groups).print(" map[" COORD "] = %d\n",COORDARGS(pair.first), pair.second); + if (DFHack::debug_groups.isEnabled(DebugCategory::LDEBUG)) { + INFO(groups).print("Group Mappings: %zu\n", groups_map.size()); + for (auto &pair: groups_map) { + DEBUG(groups).print(" map[" COORD "] = %d\n", COORDARGS(pair.first), pair.second); + } } } diff --git a/plugins/channel-safely/channel-jobs.cpp b/plugins/channel-safely/channel-jobs.cpp deleted file mode 100644 index 7a1c2f4be..000000000 --- a/plugins/channel-safely/channel-jobs.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include -#include -#include -#include - -// iterates the DF job list and adds channel jobs to the `jobs` container -void ChannelJobs::load_channel_jobs() { - jobs.clear(); - df::job_list_link* node = df::global::world->jobs.list.next; - while (node) { - df::job* job = node->item; - node = node->next; - if (is_dig_job(job)) { - jobs.emplace(job->pos); - } - } -} - -// clears the container -void ChannelJobs::clear() { - jobs.clear(); -} - -// finds and erases a job corresponding to a map position, then returns the iterator following the element removed -std::set::iterator ChannelJobs::erase(const df::coord &map_pos) { - auto iter = jobs.find(map_pos); - if (iter != jobs.end()) { - return jobs.erase(iter); - } - return iter; -} - -// finds a job corresponding to a map position if one exists -std::set::const_iterator ChannelJobs::find(const df::coord &map_pos) const { - return jobs.find(map_pos); -} - -// returns an iterator to the first element stored -std::set::const_iterator ChannelJobs::begin() const { - return jobs.begin(); -} - -// returns an iterator to after the last element stored -std::set::const_iterator ChannelJobs::end() const { - return jobs.end(); -} diff --git a/plugins/channel-safely/channel-manager.cpp b/plugins/channel-safely/channel-manager.cpp index 27d4c5153..e905f2cfb 100644 --- a/plugins/channel-safely/channel-manager.cpp +++ b/plugins/channel-safely/channel-manager.cpp @@ -5,11 +5,6 @@ #include //hash function for df::coord #include -/** -blocks[48][96][135]: -blocks[48][96][135].default_liquid.hidden: false -blocks[48][96][135].designation[10][0].hidden: false - * */ // sets mark flags as necessary, for all designations void ChannelManager::manage_groups() { diff --git a/plugins/channel-safely/channel-safely-plugin.cpp b/plugins/channel-safely/channel-safely-plugin.cpp index d4107bc56..e5c9e2760 100644 --- a/plugins/channel-safely/channel-safely-plugin.cpp +++ b/plugins/channel-safely/channel-safely-plugin.cpp @@ -93,7 +93,6 @@ PersistentDataItem psetting; PersistentDataItem pfeature; const std::string FCONFIG_KEY = std::string(plugin_name) + "/feature"; const std::string SCONFIG_KEY = std::string(plugin_name) + "/setting"; -//std::unordered_set active_jobs; enum FeatureConfigData { VISION, @@ -138,20 +137,22 @@ df::coord simulate_area_fall(const df::coord &pos) { // executes dig designations for the specified tile coordinates inline bool dig_now(color_ostream &out, const df::coord &map_pos) { - auto L = Lua::Core::State; - Lua::StackUnwinder top(L); - - if (!lua_checkstack(L, 2) || - !Lua::PushModulePublic(out, L, "plugins.dig-now", "dig_now_tile")) - return false; - - Lua::Push(L, map_pos); - - if (!Lua::SafeCall(out, L, 1, 1)) - return false; - - return lua_toboolean(L, -1); - + bool ret = false; + + lua_State* state = Lua::Core::State; + static const char* module_name = "plugins.dig-now"; + static const char* fn_name = "dig_now_tile"; + // the stack layout isn't likely to change, ever + static auto args_lambda = [&map_pos](lua_State* L) { + Lua::Push(L, map_pos); + }; + static auto res_lambda = [&ret](lua_State* L) { + ret = lua_toboolean(L, -1); + }; + + Lua::StackUnwinder top(state); + Lua::CallLuaModuleFunction(out, state, module_name, fn_name, 1, 1, args_lambda, res_lambda); + return ret; } // fully heals the unit specified, resurrecting if need be @@ -583,11 +584,14 @@ DFhackCExport command_result plugin_onupdate(color_ostream &out, state_change_ev command_result channel_safely(color_ostream &out, std::vector ¶meters) { if (!parameters.empty()) { + if (parameters[0] == "runonce") { + CSP::UnpauseEvent(); + return DFHack::CR_OK; + } else if (parameters[0] == "rebuild") { + ChannelManager::Get().destroy_groups(); + ChannelManager::Get().build_groups(); + } if (parameters.size() >= 2 && parameters.size() <= 3) { - if (parameters[0] == "runonce") { - CSP::UnpauseEvent(); - return DFHack::CR_OK; - } bool state = false; bool set = false; if (parameters[0] == "enable") { @@ -600,54 +604,7 @@ command_result channel_safely(color_ostream &out, std::vector ¶ return DFHack::CR_WRONG_USAGE; } try { - if (parameters[1] == "debug") { - auto level = std::abs(std::stol(parameters[2])); - config.debug = true; - switch (level) { - case 1: - DBG_NAME(manager).allowed(DFHack::DebugCategory::LDEBUG); - DBG_NAME(monitor).allowed(DFHack::DebugCategory::LINFO); - DBG_NAME(groups).allowed(DFHack::DebugCategory::LINFO); - DBG_NAME(jobs).allowed(DFHack::DebugCategory::LINFO); - break; - case 2: - DBG_NAME(manager).allowed(DFHack::DebugCategory::LTRACE); - DBG_NAME(monitor).allowed(DFHack::DebugCategory::LINFO); - DBG_NAME(groups).allowed(DFHack::DebugCategory::LDEBUG); - DBG_NAME(jobs).allowed(DFHack::DebugCategory::LDEBUG); - break; - case 3: - DBG_NAME(manager).allowed(DFHack::DebugCategory::LTRACE); - DBG_NAME(monitor).allowed(DFHack::DebugCategory::LINFO); - DBG_NAME(groups).allowed(DFHack::DebugCategory::LDEBUG); - DBG_NAME(jobs).allowed(DFHack::DebugCategory::LTRACE); - break; - case 4: - DBG_NAME(manager).allowed(DFHack::DebugCategory::LTRACE); - DBG_NAME(monitor).allowed(DFHack::DebugCategory::LINFO); - DBG_NAME(groups).allowed(DFHack::DebugCategory::LTRACE); - DBG_NAME(jobs).allowed(DFHack::DebugCategory::LTRACE); - break; - case 5: - DBG_NAME(manager).allowed(DFHack::DebugCategory::LTRACE); - DBG_NAME(monitor).allowed(DFHack::DebugCategory::LDEBUG); - DBG_NAME(groups).allowed(DFHack::DebugCategory::LTRACE); - DBG_NAME(jobs).allowed(DFHack::DebugCategory::LTRACE); - break; - case 6: - DBG_NAME(manager).allowed(DFHack::DebugCategory::LTRACE); - DBG_NAME(monitor).allowed(DFHack::DebugCategory::LTRACE); - DBG_NAME(groups).allowed(DFHack::DebugCategory::LTRACE); - DBG_NAME(jobs).allowed(DFHack::DebugCategory::LTRACE); - break; - case 0: - default: - DBG_NAME(monitor).allowed(DFHack::DebugCategory::LERROR); - DBG_NAME(manager).allowed(DFHack::DebugCategory::LERROR); - DBG_NAME(groups).allowed(DFHack::DebugCategory::LERROR); - DBG_NAME(jobs).allowed(DFHack::DebugCategory::LERROR); - } - } else if(parameters[1] == "monitor"){ + if(parameters[1] == "monitor"){ if (state != config.monitor_active) { config.monitor_active = state; // if this is a fresh start diff --git a/plugins/channel-safely/include/channel-groups.h b/plugins/channel-safely/include/channel-groups.h index abdbc56fc..7547e2564 100644 --- a/plugins/channel-safely/include/channel-groups.h +++ b/plugins/channel-safely/include/channel-groups.h @@ -4,14 +4,15 @@ #include #include +#include //hash functions (they should probably get moved at this point, the ones that aren't specifically for EM anyway) #include -#include -#include +#include +#include using namespace DFHack; -using Group = std::set; +using Group = std::unordered_set; using Groups = std::vector; /* Used to build groups of adjacent channel designations/jobs @@ -26,8 +27,8 @@ using Groups = std::vector; */ class ChannelGroups { private: - using GroupBlocks = std::set; - using GroupsMap = std::map; + using GroupBlocks = std::unordered_set; + using GroupsMap = std::unordered_map; GroupBlocks group_blocks; GroupsMap groups_map; Groups groups; diff --git a/plugins/channel-safely/include/channel-jobs.h b/plugins/channel-safely/include/channel-jobs.h index 0290baa19..3be704aeb 100644 --- a/plugins/channel-safely/include/channel-jobs.h +++ b/plugins/channel-safely/include/channel-jobs.h @@ -1,7 +1,11 @@ #pragma once #include #include -#include +#include //hash functions (they should probably get moved at this point, the ones that aren't specifically for EM anyway) +#include +#include + +#include using namespace DFHack; @@ -17,14 +21,23 @@ using namespace DFHack; class ChannelJobs { private: friend class ChannelGroup; - using Jobs = std::set; // job* will exist until it is complete, and likely beyond - Jobs jobs; + + using Jobs = std::unordered_set; // job* will exist until it is complete, and likely beyond + Jobs locations; public: void load_channel_jobs(); - void clear(); - int count(const df::coord &map_pos) const { return jobs.count(map_pos); } - Jobs::iterator erase(const df::coord &map_pos); - Jobs::const_iterator find(const df::coord &map_pos) const; - Jobs::const_iterator begin() const; - Jobs::const_iterator end() const; + void clear() { + locations.clear(); + } + int count(const df::coord &map_pos) const { return locations.count(map_pos); } + Jobs::iterator erase(const df::coord &map_pos) { + auto iter = locations.find(map_pos); + if (iter != locations.end()) { + return locations.erase(iter); + } + return iter; + } + Jobs::const_iterator find(const df::coord &map_pos) const { return locations.find(map_pos); } + Jobs::const_iterator begin() const { return locations.begin(); } + Jobs::const_iterator end() const { return locations.end(); } }; diff --git a/plugins/channel-safely/include/channel-manager.h b/plugins/channel-safely/include/channel-manager.h index d36e98ca3..0cd3abfac 100644 --- a/plugins/channel-safely/include/channel-manager.h +++ b/plugins/channel-safely/include/channel-manager.h @@ -33,9 +33,7 @@ public: bool exists(const df::coord &map_pos) const { return groups.count(map_pos); } void debug() { DEBUG(groups).print(" DEBUGGING GROUPS:\n"); - if (config.debug) { - groups.debug_groups(); - groups.debug_map(); - } + groups.debug_groups(); + groups.debug_map(); } }; diff --git a/plugins/channel-safely/include/inlines.h b/plugins/channel-safely/include/inlines.h index e22210f7a..8bd1de44d 100644 --- a/plugins/channel-safely/include/inlines.h +++ b/plugins/channel-safely/include/inlines.h @@ -64,7 +64,7 @@ inline bool is_safe_fall(const df::coord &map_pos) { return true; //we require vision, and we can't see below.. so we gotta assume it's safe } df::tiletype type = *Maps::getTileType(below); - if (!isOpenTerrain(type)) { + if (!DFHack::isOpenTerrain(type)) { return true; } } @@ -80,10 +80,10 @@ inline bool is_safe_to_dig_down(const df::coord &map_pos) { return true; } df::tiletype type = *Maps::getTileType(pos); - if (zi == 0 && isOpenTerrain(type)) { + if (zi == 0 && DFHack::isOpenTerrain(type)) { // the starting tile is open space, that's obviously not safe return false; - } else if (!isOpenTerrain(type)) { + } else if (!DFHack::isOpenTerrain(type)) { // a tile after the first one is not open space return true; } @@ -142,7 +142,9 @@ inline void cancel_job(df::job* job) { x = pos.x % 16; y = pos.y % 16; df::tile_designation &designation = job_block->designation[x][y]; - switch (job->job_type) { + auto type = job->job_type; + Job::removeJob(job); + switch (type) { case job_type::Dig: designation.bits.dig = df::tile_dig_designation::Default; break; @@ -165,21 +167,13 @@ inline void cancel_job(df::job* job) { designation.bits.dig = df::tile_dig_designation::No; break; } - Job::removeJob(job); } } template void set_difference(const Ctr1 &c1, const Ctr2 &c2, Ctr3 &c3) { for (const auto &a : c1) { - bool matched = false; - for (const auto &b : c2) { - if (a == b) { - matched = true; - break; - } - } - if (!matched) { + if (!c2.count(a)) { c3.emplace(a); } } diff --git a/plugins/channel-safely/include/plugin.h b/plugins/channel-safely/include/plugin.h index 2451033c5..23b2f8441 100644 --- a/plugins/channel-safely/include/plugin.h +++ b/plugins/channel-safely/include/plugin.h @@ -9,7 +9,6 @@ namespace DFHack { } struct Configuration { - bool debug = false; bool monitor_active = false; bool require_vision = true; bool insta_dig = false; diff --git a/plugins/channel-safely/include/tile-cache.h b/plugins/channel-safely/include/tile-cache.h index ddd37c34a..10e91cd46 100644 --- a/plugins/channel-safely/include/tile-cache.h +++ b/plugins/channel-safely/include/tile-cache.h @@ -3,13 +3,14 @@ #include #include #include +#include //hash functions (they should probably get moved at this point, the ones that aren't specifically for EM anyway) -#include +#include class TileCache { private: TileCache() = default; - std::map locations; + std::unordered_map locations; public: static TileCache& Get() { static TileCache instance; @@ -25,11 +26,6 @@ public: } bool hasChanged(const df::coord &pos, const df::tiletype &type) { - if (locations.count(pos)) { - if (type != locations.find(pos)->second){ - return true; - } - } - return false; + return locations.count(pos) && type != locations[pos]; } };