Implements plugin: channel-safely v1.2.1

- Removes insta-dig feature (too many problems, rarely worked)
- Fixes a segmentation fault introduced in v1.2
- Improves manage_group readability/nesting
- Improves manage_one readability/nesting

Update docs/plugins/channel-safely.rst

Co-authored-by: Myk <myk002@yahoo.com>

Fixes some formatting consistency
develop
Josh Cooper 2022-12-08 14:59:09 -08:00
parent a4bf266770
commit ae8291b952
8 changed files with 113 additions and 96 deletions

@ -63,8 +63,6 @@ Features
:monitoring: Toggle whether to monitor the conditions of active digs. (default: disabled) :monitoring: Toggle whether to monitor the conditions of active digs. (default: disabled)
:resurrect: Toggle whether to resurrect units involved in cave-ins, and if monitor is enabled :resurrect: Toggle whether to resurrect units involved in cave-ins, and if monitor is enabled
units who die while digging. (default: disabled) units who die while digging. (default: disabled)
:insta-dig: Toggle whether to use insta-digging on unreachable designations.
Runs on the refresh cycles. Use with caution. (default: disabled)
Settings Settings
-------- --------
@ -80,10 +78,8 @@ Troubleshooting
--------------- ---------------
If designations aren't switching correctly, try putting the designations into marker mode. If designations aren't switching correctly, try putting the designations into marker mode.
Then press . (next) or resume. If you're debugging code you'll want these: Then press . (next) or resume. If you're debugging code you'll want these::
[todo: make this a block of code] debugfilter set Info channel manager
debugfilter set Debug channel plugin
:filter1: ``debugfilter set Info channel manager`` debugfilter set Trace channel group
:filter2: ``debugfilter set Debug channel plugin``
:filter3: ``debugfilter set Trace channel group``

@ -46,14 +46,14 @@ bool ChannelJobs::has_cavein_conditions(const df::coord &map_pos) {
} }
bool ChannelJobs::possible_cavein(const df::coord &job_pos) { bool ChannelJobs::possible_cavein(const df::coord &job_pos) {
for (auto iter = active.begin(); iter != active.end(); ++iter) { for (auto iter : active) {
if (*iter == job_pos) continue; if (iter == job_pos) continue;
if (calc_distance(job_pos, *iter) <= 2) { if (calc_distance(job_pos, iter) <= 2) {
// find neighbours // find neighbours
df::coord n1[8]; df::coord n1[8];
df::coord n2[8]; df::coord n2[8];
get_neighbours(job_pos, n1); get_neighbours(job_pos, n1);
get_neighbours(*iter, n2); get_neighbours(iter, n2);
// find shared neighbours // find shared neighbours
for (int i = 0; i < 7; ++i) { for (int i = 0; i < 7; ++i) {
for (int j = i + 1; j < 8; ++j) { for (int j = i + 1; j < 8; ++j) {
@ -133,7 +133,7 @@ void ChannelGroups::add(const df::coord &map_pos) {
TRACE(groups).print(" -> brand new group\n"); TRACE(groups).print(" -> brand new group\n");
// we create a brand-new group to use // we create a brand-new group to use
group_index = groups.size(); group_index = groups.size();
groups.push_back(Group()); groups.emplace_back();
group = &groups[group_index]; group = &groups[group_index];
} }
} }
@ -220,7 +220,7 @@ void ChannelGroups::scan(bool full_scan) {
jobs.erase(map_pos); jobs.erase(map_pos);
} }
block->designation[lx][ly].bits.dig = df::tile_dig_designation::No; block->designation[lx][ly].bits.dig = df::tile_dig_designation::No;
} else if (is_dig_designation(block->designation[lx][ly]) || block->occupancy[lx][ly].bits.dig_marked ) { } else if (is_dig_designation(block->designation[lx][ly]) || block->occupancy[lx][ly].bits.dig_marked) {
if (!is_channel_designation(block->designation[lx][ly])) { if (!is_channel_designation(block->designation[lx][ly])) {
if (df::map_block* block_above = Maps::getBlock(bx, by, z+1)) { if (df::map_block* block_above = Maps::getBlock(bx, by, z+1)) {
if (!is_channel_designation(block_above->designation[lx][ly])) { if (!is_channel_designation(block_above->designation[lx][ly])) {

@ -52,7 +52,7 @@ void ChannelManager::manage_group(const df::coord &map_pos, bool set_marker_mode
INFO(manager).print("manage_group() is done\n"); INFO(manager).print("manage_group() is done\n");
} }
void ChannelManager::manage_group(const Group &group, bool set_marker_mode, bool marker_mode) { void ChannelManager::manage_group(const Group &group, bool set_marker_mode, bool marker_mode) const {
INFO(manager).print("manage_group()\n"); INFO(manager).print("manage_group()\n");
if (!set_marker_mode) { if (!set_marker_mode) {
marker_mode = has_any_groups_above(groups, group); marker_mode = has_any_groups_above(groups, group);
@ -88,7 +88,6 @@ void ChannelManager::manage_group(const Group &group, bool set_marker_mode, bool
if (config.insta_dig) { if (config.insta_dig) {
manage_one(pos, true, false); manage_one(pos, true, false);
dig_now(DFHack::Core::getInstance().getConsole(), pos); dig_now(DFHack::Core::getInstance().getConsole(), pos);
mark_done(pos);
} else { } else {
// todo: engage dig management, swap channel designations for dig // todo: engage dig management, swap channel designations for dig
} }
@ -101,7 +100,6 @@ void ChannelManager::manage_group(const Group &group, bool set_marker_mode, bool
} else if (config.insta_dig && isEntombed(miner_pos, pos)) { } else if (config.insta_dig && isEntombed(miner_pos, pos)) {
manage_one(pos, true, false); manage_one(pos, true, false);
dig_now(DFHack::Core::getInstance().getConsole(), pos); dig_now(DFHack::Core::getInstance().getConsole(), pos);
mark_done(pos);
} }
} }
DEBUG(manager).print("cavein possible(%d)\n" DEBUG(manager).print("cavein possible(%d)\n"
@ -120,16 +118,18 @@ void ChannelManager::manage_group(const Group &group, bool set_marker_mode, bool
continue; continue;
} }
// cavein is only possible if marker_mode is false // cavein is only possible if marker_mode is false
// we want to dig the cavein candidates first, the least accessible ones specifically
const static uint8_t MAX = 84; //arbitrary number that indicates the value has changed const static uint8_t MAX = 84; //arbitrary number that indicates the value has changed
const static uint8_t OFFSET = 2; //value has been tweaked to avoid cave-ins whilst activating as many designations as possible
if (CSP::dignow_queue.count(pos) || (cavein_candidates.count(pos) && if (CSP::dignow_queue.count(pos) || (cavein_candidates.count(pos) &&
least_access < MAX && cavein_candidates[pos] <= least_access+2)) { least_access < MAX && cavein_candidates[pos] <= least_access+OFFSET)) {
TRACE(manager).print("cave-in evaluated true and either of dignow or (%d <= %d)\n", cavein_candidates[pos], least_access+2); TRACE(manager).print("cave-in evaluated true and either of dignow or (%d <= %d)\n", cavein_candidates[pos], least_access+OFFSET);
// we want to dig the cavein candidates first
df::coord local(pos); df::coord local(pos);
local.x %= 16; local.x %= 16;
local.y %= 16; local.y %= 16;
auto block = Maps::ensureTileBlock(pos); auto block = Maps::ensureTileBlock(pos);
// if we don't find the priority in block_events, it probably means bad things
for (df::block_square_event* event: block->block_events) { for (df::block_square_event* event: block->block_events) {
if (auto evT = virtual_cast<df::block_square_event_designation_priorityst>(event)) { if (auto evT = virtual_cast<df::block_square_event_designation_priorityst>(event)) {
// we want to let the user keep some designations free of being managed // we want to let the user keep some designations free of being managed
@ -142,8 +142,9 @@ void ChannelManager::manage_group(const Group &group, bool set_marker_mode, bool
assert(manage_one(pos, true, false)); assert(manage_one(pos, true, false));
continue; continue;
} }
// cavein possible, but we failed to meet the criteria for activation
if (cavein_candidates.count(pos)) { if (cavein_candidates.count(pos)) {
DEBUG(manager).print("cave-in evaluated true and no dignow and (%d > %d)\n", cavein_candidates[pos], least_access + 2); DEBUG(manager).print("cave-in evaluated true and no dignow and (%d > %d)\n", cavein_candidates[pos], least_access+OFFSET);
} else { } else {
DEBUG(manager).print("cave-in evaluated true and no dignow and pos is not a candidate\n"); DEBUG(manager).print("cave-in evaluated true and no dignow and pos is not a candidate\n");
} }
@ -152,7 +153,7 @@ void ChannelManager::manage_group(const Group &group, bool set_marker_mode, bool
INFO(manager).print("manage_group() is done\n"); INFO(manager).print("manage_group() is done\n");
} }
bool ChannelManager::manage_one(const df::coord &map_pos, bool set_marker_mode, bool marker_mode) { bool ChannelManager::manage_one(const df::coord &map_pos, bool set_marker_mode, bool marker_mode) const {
if (Maps::isValidTilePos(map_pos)) { if (Maps::isValidTilePos(map_pos)) {
TRACE(manager).print("manage_one((" COORD "), %d, %d)\n", COORDARGS(map_pos), set_marker_mode, marker_mode); TRACE(manager).print("manage_one((" COORD "), %d, %d)\n", COORDARGS(map_pos), set_marker_mode, marker_mode);
df::map_block* block = Maps::getTileBlock(map_pos); df::map_block* block = Maps::getTileBlock(map_pos);
@ -164,41 +165,22 @@ bool ChannelManager::manage_one(const df::coord &map_pos, bool set_marker_mode,
// ensure that we aren't on the top-most layers // ensure that we aren't on the top-most layers
if (map_pos.z < mapz - 3) { if (map_pos.z < mapz - 3) {
// do we already know whether to set marker mode? // do we already know whether to set marker mode?
if (set_marker_mode) { if (!marker_mode) {
TRACE(manager).print(" -> setting marker mode\n"); // marker_mode is set to true if it is unsafe to dig
// if enabling marker mode, just do it marker_mode = (!set_marker_mode &&
if (marker_mode) { (has_group_above(groups, map_pos) || !is_safe_to_dig_down(map_pos))) ||
goto markerMode; (set_marker_mode &&
} is_channel_designation(block->designation[Coord(local)]) && !is_safe_to_dig_down(map_pos));
// otherwise we check for some safety
if (!is_channel_designation(block->designation[Coord(local)]) || is_safe_to_dig_down(map_pos)) {
// not a channel designation, or it is safe to dig down if it is
if (!block->flags.bits.designated) {
block->flags.bits.designated = true;
}
// we need to cache the tile now that we're activating the designation
TileCache::Get().cache(map_pos, block->tiletype[Coord(local)]);
TRACE(manager).print("marker mode DISABLED\n");
tile_occupancy.bits.dig_marked = false;
} else {
// we want to activate the designation, that's not what we get
goto markerMode;
} }
} else { if (marker_mode) {
// not set_marker_mode
TRACE(manager).print(" if(has_groups_above())\n");
// check that the group has no incomplete groups directly above it
if (has_group_above(groups, map_pos) || !is_safe_to_dig_down(map_pos)) {
markerMode:
TRACE(manager).print("marker mode ENABLED\n");
tile_occupancy.bits.dig_marked = true;
if (jobs.count(map_pos)) { if (jobs.count(map_pos)) {
cancel_job(map_pos); cancel_job(map_pos);
} }
} else if (!block->flags.bits.designated) {
block->flags.bits.designated = true;
} }
} tile_occupancy.bits.dig_marked = marker_mode;
TRACE(manager).print("marker mode %s\n", marker_mode ? "ENABLED" : "DISABLED");
} else { } else {
// if we are though, it should be totally safe to dig // if we are though, it should be totally safe to dig
tile_occupancy.bits.dig_marked = false; tile_occupancy.bits.dig_marked = false;

@ -1,7 +1,10 @@
/* Prevent channeling down into known open space. /* Prevent channeling down into known open space.
Author: Josh Cooper Author: Josh Cooper
Created: Aug. 4 2020 Created: Aug. 4 2020
Updated: Dec. 5 2022 Updated: Dec. 8 2022
*/
/*
This skeletal logic has not been kept up-to-date since ~v0.5
Enable plugin: Enable plugin:
-> build groups -> build groups
@ -160,7 +163,7 @@ namespace CSP {
try { try {
pfeature.ival(MONITOR) = config.monitoring; pfeature.ival(MONITOR) = config.monitoring;
pfeature.ival(VISION) = config.require_vision; pfeature.ival(VISION) = config.require_vision;
pfeature.ival(INSTADIG) = config.insta_dig; pfeature.ival(INSTADIG) = false; //config.insta_dig;
pfeature.ival(RESURRECT) = config.resurrect; pfeature.ival(RESURRECT) = config.resurrect;
pfeature.ival(RISKAVERSE) = config.riskaverse; pfeature.ival(RISKAVERSE) = config.riskaverse;
@ -186,7 +189,7 @@ namespace CSP {
try { try {
config.monitoring = pfeature.ival(MONITOR); config.monitoring = pfeature.ival(MONITOR);
config.require_vision = pfeature.ival(VISION); config.require_vision = pfeature.ival(VISION);
config.insta_dig = pfeature.ival(INSTADIG); config.insta_dig = false; //pfeature.ival(INSTADIG);
config.resurrect = pfeature.ival(RESURRECT); config.resurrect = pfeature.ival(RESURRECT);
config.riskaverse = pfeature.ival(RISKAVERSE); config.riskaverse = pfeature.ival(RISKAVERSE);
@ -303,9 +306,9 @@ namespace CSP {
switch (report->type) { switch (report->type) {
case announcement_type::CANCEL_JOB: case announcement_type::CANCEL_JOB:
if (config.insta_dig) { if (config.insta_dig) {
if (report->text.find("cancels Dig") != std::string::npos) { if (report->text.find("cancels Dig") != std::string::npos ||
dignow_queue.emplace(report->pos); report->text.find("path") != std::string::npos) {
} else if (report->text.find("path") != std::string::npos) {
dignow_queue.emplace(report->pos); dignow_queue.emplace(report->pos);
} }
DEBUG(plugin).print("%d, pos: " COORD ", pos2: " COORD "\n%s\n", report_id, COORDARGS(report->pos), DEBUG(plugin).print("%d, pos: " COORD ", pos2: " COORD "\n%s\n", report_id, COORDARGS(report->pos),
@ -439,7 +442,7 @@ namespace CSP {
Maps::getTileOccupancy(job->pos)->bits.dig_marked = true; Maps::getTileOccupancy(job->pos)->bits.dig_marked = true;
// prevent algorithm from re-enabling designation // prevent algorithm from re-enabling designation
for (auto &be: Maps::getBlock(job->pos)->block_events) { ; for (auto &be: Maps::getBlock(job->pos)->block_events) {
if (auto bsedp = virtual_cast<df::block_square_event_designation_priorityst>( if (auto bsedp = virtual_cast<df::block_square_event_designation_priorityst>(
be)) { be)) {
df::coord local(job->pos); df::coord local(job->pos);
@ -603,7 +606,8 @@ command_result channel_safely(color_ostream &out, std::vector<std::string> &para
} else if (parameters[1] == "require-vision") { } else if (parameters[1] == "require-vision") {
config.require_vision = state; config.require_vision = state;
} else if (parameters[1] == "insta-dig") { } else if (parameters[1] == "insta-dig") {
config.insta_dig = state; //config.insta_dig = state;
config.insta_dig = false;
} else if (parameters[1] == "resurrect") { } else if (parameters[1] == "resurrect") {
if (state != config.resurrect) { if (state != config.resurrect) {
config.resurrect = state; config.resurrect = state;
@ -641,7 +645,7 @@ command_result channel_safely(color_ostream &out, std::vector<std::string> &para
out.print(" %-20s\t%s\n", "risk-averse: ", config.riskaverse ? "on." : "off."); out.print(" %-20s\t%s\n", "risk-averse: ", config.riskaverse ? "on." : "off.");
out.print(" %-20s\t%s\n", "monitoring: ", config.monitoring ? "on." : "off."); out.print(" %-20s\t%s\n", "monitoring: ", config.monitoring ? "on." : "off.");
out.print(" %-20s\t%s\n", "require-vision: ", config.require_vision ? "on." : "off."); out.print(" %-20s\t%s\n", "require-vision: ", config.require_vision ? "on." : "off.");
out.print(" %-20s\t%s\n", "insta-dig: ", config.insta_dig ? "on." : "off."); //out.print(" %-20s\t%s\n", "insta-dig: ", config.insta_dig ? "on." : "off.");
out.print(" %-20s\t%s\n", "resurrect: ", config.resurrect ? "on." : "off."); out.print(" %-20s\t%s\n", "resurrect: ", config.resurrect ? "on." : "off.");
out.print(" SETTINGS:\n"); out.print(" SETTINGS:\n");
out.print(" %-20s\t%" PRIi32 "\n", "refresh-freq: ", config.refresh_freq); out.print(" %-20s\t%" PRIi32 "\n", "refresh-freq: ", config.refresh_freq);

@ -37,9 +37,9 @@ private:
protected: protected:
void add(const df::coord &map_pos); void add(const df::coord &map_pos);
public: public:
int debugGIndex(const df::coord &map_pos) { int debugGIndex(const df::coord &map_pos) const {
if (groups_map.count(map_pos)) { if (groups_map.count(map_pos)) {
return groups_map[map_pos]; return groups_map.find(map_pos)->second;
} }
return -1; return -1;
} }

@ -21,8 +21,6 @@ using namespace DFHack;
*/ */
class ChannelJobs { class ChannelJobs {
private: private:
friend class ChannelGroup;
using Jobs = std::unordered_set<df::coord>; // job* will exist until it is complete, and likely beyond using Jobs = std::unordered_set<df::coord>; // job* will exist until it is complete, and likely beyond
std::unordered_map<df::coord, df::job*> jobs; std::unordered_map<df::coord, df::job*> jobs;
Jobs locations; Jobs locations;

@ -27,8 +27,8 @@ public:
void destroy_groups() { groups.clear(); debug(); } void destroy_groups() { groups.clear(); debug(); }
void manage_groups(); void manage_groups();
void manage_group(const df::coord &map_pos, bool set_marker_mode = false, bool marker_mode = false); void manage_group(const df::coord &map_pos, bool set_marker_mode = false, bool marker_mode = false);
void manage_group(const Group &group, bool set_marker_mode = false, bool marker_mode = false); void manage_group(const Group &group, bool set_marker_mode = false, bool marker_mode = false) const;
bool manage_one(const df::coord &map_pos, bool set_marker_mode = false, bool marker_mode = false); bool manage_one(const df::coord &map_pos, bool set_marker_mode = false, bool marker_mode = false) const;
void mark_done(const df::coord &map_pos); void mark_done(const df::coord &map_pos);
bool exists(const df::coord &map_pos) const { return groups.count(map_pos); } bool exists(const df::coord &map_pos) const { return groups.count(map_pos); }
void debug() { void debug() {

@ -10,10 +10,11 @@
#include <cinttypes> #include <cinttypes>
#include <unordered_set> #include <unordered_set>
#include <random>
#define Coord(id) id.x][id.y #define Coord(id) (id).x][(id).y
#define COORD "%" PRIi16 ",%" PRIi16 ",%" PRIi16 #define COORD "%" PRIi16 ",%" PRIi16 ",%" PRIi16
#define COORDARGS(id) id.x, id.y, id.z #define COORDARGS(id) (id).x, (id).y, (id).z
namespace CSP { namespace CSP {
extern std::unordered_set<df::coord> dignow_queue; extern std::unordered_set<df::coord> dignow_queue;
@ -81,12 +82,9 @@ inline bool isEntombed(const df::coord &unit_pos, const df::coord &map_pos) {
} }
df::coord neighbours[8]; df::coord neighbours[8];
get_neighbours(map_pos, neighbours); get_neighbours(map_pos, neighbours);
for (auto n: neighbours) { return std::all_of(neighbours+0, neighbours+8, [&unit_pos](df::coord n) {
if (Maps::canWalkBetween(unit_pos, n)) { return !Maps::canWalkBetween(unit_pos, n);
return false; });
}
}
return true;
} }
inline bool is_dig_job(const df::job* job) { inline bool is_dig_job(const df::job* job) {
@ -145,22 +143,6 @@ inline bool is_safe_to_dig_down(const df::coord &map_pos) {
return false; return false;
} }
inline bool can_reach_designation(const df::coord &start, const df::coord &end) {
if (start != end) {
if (!Maps::canWalkBetween(start, end)) {
df::coord neighbours[8];
get_neighbours(end, neighbours);
for (auto &pos: neighbours) {
if (Maps::isValidTilePos(pos) && Maps::canWalkBetween(start, pos)) {
return true;
}
}
return false;
}
}
return true;
}
inline bool has_unit(const df::tile_occupancy* occupancy) { inline bool has_unit(const df::tile_occupancy* occupancy) {
return occupancy->bits.unit || occupancy->bits.unit_grounded; return occupancy->bits.unit || occupancy->bits.unit_grounded;
} }
@ -235,8 +217,62 @@ inline void cancel_job(const df::coord &map_pos) {
// executes dig designations for the specified tile coordinates // executes dig designations for the specified tile coordinates
inline bool dig_now(color_ostream &out, const df::coord &map_pos) { inline bool dig_now(color_ostream &out, const df::coord &map_pos) {
static std::default_random_engine rng;
std::uniform_int_distribution<> dist(0,5);
out.color(color_value::COLOR_YELLOW); out.color(color_value::COLOR_YELLOW);
out.print("channel-safely: insta-dig: digging (" COORD ")<\n", COORDARGS(map_pos)); out.print("channel-safely: insta-dig: digging (" COORD ")<\n", COORDARGS(map_pos));
df::coord below(map_pos);
below.z--;
auto ttype_below = *Maps::getTileType(below);
if (isOpenTerrain(ttype_below) || isFloorTerrain(ttype_below)) {
*Maps::getTileType(map_pos) = tiletype::OpenSpace;
} else {
auto ttype_p = Maps::getTileType(map_pos);
if (isSoilMaterial(*ttype_p)) {
switch(dist(rng)) {
case 0:
*ttype_p = tiletype::SoilFloor1;
break;
case 1:
*ttype_p = tiletype::SoilFloor2;
break;
case 2:
*ttype_p = tiletype::SoilFloor3;
break;
case 3:
*ttype_p = tiletype::SoilFloor4;
break;
default:
*ttype_p = tiletype::SoilFloor1;
break;
}
} else if (isStoneMaterial(*ttype_p)) {
switch(dist(rng)) {
case 0:
*ttype_p = tiletype::FeatureFloor1;
break;
case 1:
*ttype_p = tiletype::FeatureFloor2;
break;
case 2:
*ttype_p = tiletype::FeatureFloor3;
break;
case 3:
*ttype_p = tiletype::FeatureFloor4;
break;
default:
*ttype_p = tiletype::MineralFloor1;
break;
}
} else {
out.print("Unknown type\n");
return false;
}
}
return true;
/*
bool ret = false; bool ret = false;
lua_State* state = Lua::Core::State; lua_State* state = Lua::Core::State;
@ -253,6 +289,7 @@ inline bool dig_now(color_ostream &out, const df::coord &map_pos) {
Lua::StackUnwinder top(state); Lua::StackUnwinder top(state);
Lua::CallLuaModuleFunction(out, state, module_name, fn_name, 1, 1, args_lambda, res_lambda); Lua::CallLuaModuleFunction(out, state, module_name, fn_name, 1, 1, args_lambda, res_lambda);
return ret; return ret;
*/
} }
// fully heals the unit specified, resurrecting if need be // fully heals the unit specified, resurrecting if need be