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)
:resurrect: Toggle whether to resurrect units involved in cave-ins, and if monitor is enabled
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
--------
@ -80,10 +78,8 @@ Troubleshooting
---------------
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]
:filter1: ``debugfilter set Info channel manager``
:filter2: ``debugfilter set Debug channel plugin``
:filter3: ``debugfilter set Trace channel group``
debugfilter set Info channel manager
debugfilter set Debug channel plugin
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) {
for (auto iter = active.begin(); iter != active.end(); ++iter) {
if (*iter == job_pos) continue;
if (calc_distance(job_pos, *iter) <= 2) {
for (auto iter : active) {
if (iter == job_pos) continue;
if (calc_distance(job_pos, iter) <= 2) {
// find neighbours
df::coord n1[8];
df::coord n2[8];
get_neighbours(job_pos, n1);
get_neighbours(*iter, n2);
get_neighbours(iter, n2);
// find shared neighbours
for (int i = 0; i < 7; ++i) {
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");
// we create a brand-new group to use
group_index = groups.size();
groups.push_back(Group());
groups.emplace_back();
group = &groups[group_index];
}
}
@ -220,7 +220,7 @@ void ChannelGroups::scan(bool full_scan) {
jobs.erase(map_pos);
}
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 (df::map_block* block_above = Maps::getBlock(bx, by, z+1)) {
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");
}
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");
if (!set_marker_mode) {
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) {
manage_one(pos, true, false);
dig_now(DFHack::Core::getInstance().getConsole(), pos);
mark_done(pos);
} else {
// todo: engage dig management, swap channel designations for dig
}
@ -101,12 +100,11 @@ void ChannelManager::manage_group(const Group &group, bool set_marker_mode, bool
} else if (config.insta_dig && isEntombed(miner_pos, pos)) {
manage_one(pos, true, false);
dig_now(DFHack::Core::getInstance().getConsole(), pos);
mark_done(pos);
}
}
DEBUG(manager).print("cavein possible(%d)\n"
"%zu candidates\n"
"least access %d\n", cavein_possible, cavein_candidates.size(), least_access);
"%zu candidates\n"
"least access %d\n", cavein_possible, cavein_candidates.size(), least_access);
}
// managing designations
if (!group.empty()) {
@ -120,16 +118,18 @@ void ChannelManager::manage_group(const Group &group, bool set_marker_mode, bool
continue;
}
// 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 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) &&
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);
// we want to dig the cavein candidates first
TRACE(manager).print("cave-in evaluated true and either of dignow or (%d <= %d)\n", cavein_candidates[pos], least_access+OFFSET);
df::coord local(pos);
local.x %= 16;
local.y %= 16;
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) {
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
@ -142,8 +142,9 @@ void ChannelManager::manage_group(const Group &group, bool set_marker_mode, bool
assert(manage_one(pos, true, false));
continue;
}
// cavein possible, but we failed to meet the criteria for activation
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 {
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");
}
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)) {
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);
@ -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
if (map_pos.z < mapz - 3) {
// do we already know whether to set marker mode?
if (set_marker_mode) {
TRACE(manager).print(" -> setting marker mode\n");
// if enabling marker mode, just do it
if (marker_mode) {
goto markerMode;
}
// 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 {
// 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)) {
cancel_job(map_pos);
}
if (!marker_mode) {
// marker_mode is set to true if it is unsafe to dig
marker_mode = (!set_marker_mode &&
(has_group_above(groups, map_pos) || !is_safe_to_dig_down(map_pos))) ||
(set_marker_mode &&
is_channel_designation(block->designation[Coord(local)]) && !is_safe_to_dig_down(map_pos));
}
if (marker_mode) {
if (jobs.count(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 {
// if we are though, it should be totally safe to dig
tile_occupancy.bits.dig_marked = false;

@ -1,7 +1,10 @@
/* Prevent channeling down into known open space.
Author: Josh Cooper
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:
-> build groups
@ -160,7 +163,7 @@ namespace CSP {
try {
pfeature.ival(MONITOR) = config.monitoring;
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(RISKAVERSE) = config.riskaverse;
@ -186,7 +189,7 @@ namespace CSP {
try {
config.monitoring = pfeature.ival(MONITOR);
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.riskaverse = pfeature.ival(RISKAVERSE);
@ -303,9 +306,9 @@ namespace CSP {
switch (report->type) {
case announcement_type::CANCEL_JOB:
if (config.insta_dig) {
if (report->text.find("cancels Dig") != std::string::npos) {
dignow_queue.emplace(report->pos);
} else if (report->text.find("path") != std::string::npos) {
if (report->text.find("cancels Dig") != std::string::npos ||
report->text.find("path") != std::string::npos) {
dignow_queue.emplace(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;
// 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>(
be)) {
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") {
config.require_vision = state;
} else if (parameters[1] == "insta-dig") {
config.insta_dig = state;
//config.insta_dig = state;
config.insta_dig = false;
} else if (parameters[1] == "resurrect") {
if (state != config.resurrect) {
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", "monitoring: ", config.monitoring ? "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(" SETTINGS:\n");
out.print(" %-20s\t%" PRIi32 "\n", "refresh-freq: ", config.refresh_freq);

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

@ -21,8 +21,6 @@ using namespace DFHack;
*/
class ChannelJobs {
private:
friend class ChannelGroup;
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;
Jobs locations;

@ -27,8 +27,8 @@ public:
void destroy_groups() { groups.clear(); debug(); }
void manage_groups();
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);
bool manage_one(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) const;
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);
bool exists(const df::coord &map_pos) const { return groups.count(map_pos); }
void debug() {

@ -10,10 +10,11 @@
#include <cinttypes>
#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 COORDARGS(id) id.x, id.y, id.z
#define COORDARGS(id) (id).x, (id).y, (id).z
namespace CSP {
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];
get_neighbours(map_pos, neighbours);
for (auto n: neighbours) {
if (Maps::canWalkBetween(unit_pos, n)) {
return false;
}
}
return true;
return std::all_of(neighbours+0, neighbours+8, [&unit_pos](df::coord n) {
return !Maps::canWalkBetween(unit_pos, n);
});
}
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;
}
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) {
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
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.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;
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::CallLuaModuleFunction(out, state, module_name, fn_name, 1, 1, args_lambda, res_lambda);
return ret;
*/
}
// fully heals the unit specified, resurrecting if need be