fix: 主世界岩浆卡死 + 下界挖矿多项修复 (v2.2.3)

主世界:目标点岩浆卡死——同目标不再重复下发、无进展20秒放弃跳过、绕道仅空闲时低频重发,避免玩家被拉回岩浆边

下界:垃圾清单补灵魂沙/灵魂土/岩浆块/沙砾/下界砖;进食不再被切工具打断;挖矿中不再每秒重发mineByName(解决灵魂沙抽搐/来回折返);寻路失败不再误判完成、自动转移新矿区续挖
这个提交包含在:
lyizai
2026-09-01 13:41:42 +08:00
父节点 248505deee
当前提交 e84ac7cf41
共修改 7 个文件,包含 158 行新增和 30 行删除
+1
查看文件
@@ -42,6 +42,7 @@
## 版本记录
- **v2.2.3**:修复下界多个问题——垃圾清单补充灵魂沙/灵魂土/岩浆块/沙砾/下界砖;进食时不再被工具切换打断(镐子/食物来回切换);下界挖掘重构:挖矿中不再每秒重发指令(解决灵魂沙抽搐/来回折返不挖),寻路失败不再误判"任务完成"回主世界,而是空闲一段时间后自动转移到新矿区继续找矿。
- **v2.2.2**:新增无路自动搭方块(可开关):Baritone 放置方块搭桥/垫脚,自动补给快捷栏搭路方块并喂给 Baritone 可放置物品清单;AutoTrash 不再丢弃快捷栏物品。
- **v2.2.1**:移除自动火把;新增主世界矿物绕道挖掘(附近 10 格目标矿物自动挖掉,不漏矿);新增工具适用性检查(远古残骸等自动换钻石镐);支巷长度可配置(默认 10);矿物 id 兼容 `minecraft:` 前缀。
- **v2.2.0**:新增实时状态 HUD、`/autodig status` 命令;自动进食跳过特殊食物;背包接近满提前丢垃圾防误判返程;杀戮光环不再误伤中立生物;GUI 补全进食/保命/攻击范围等设置。
+1 -1
查看文件
@@ -7,7 +7,7 @@ yarn_mappings=1.21.4+build.8
loader_version=0.19.3
# Mod Properties
mod_version=2.2.2
mod_version=2.2.3
maven_group=autodig
archives_base_name=autodig
@@ -87,7 +87,7 @@ public final class ConfigStore {
}
c.trashReserve = clamp(c.trashReserve, 0, 2304);
if (c.trashItems == null || c.trashItems.isBlank()) {
c.trashItems = "cobblestone,cobbled_deepslate,deepslate,tuff,dirt,granite,diorite,andesite,netherrack,blackstone,basalt";
c.trashItems = "cobblestone,cobbled_deepslate,deepslate,tuff,dirt,granite,diorite,andesite,netherrack,blackstone,basalt,soul_sand,soul_soil,magma_block,gravel,nether_bricks";
}
if (c.scaffoldBlocks == null || c.scaffoldBlocks.isBlank()) {
c.scaffoldBlocks = "cobblestone,cobbled_deepslate,deepslate,netherrack,dirt,granite,diorite,andesite,tuff,blackstone,basalt";
@@ -41,7 +41,7 @@ public class DigConfig {
/** 背包满时自动丢弃垃圾物品(按 trashItems 清单) */
public boolean autoTrash = true;
/** 垃圾物品清单(逗号分隔的物品 id,如 cobblestone,tuff,deepslate) */
public String trashItems = "cobblestone,cobbled_deepslate,deepslate,tuff,dirt,granite,diorite,andesite,netherrack,blackstone,basalt";
public String trashItems = "cobblestone,cobbled_deepslate,deepslate,tuff,dirt,granite,diorite,andesite,netherrack,blackstone,basalt,soul_sand,soul_soil,magma_block,gravel,nether_bricks";
/** 方块类垃圾保留数量(供 Baritone 搭路/垫脚),0 为不保留 */
public int trashReserve = 64;
@@ -6,11 +6,13 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
import net.minecraft.client.MinecraftClient;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.registry.Registries;
import net.minecraft.registry.RegistryKey;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
/**
@@ -41,6 +43,12 @@ public final class NetherMiner {
/** 背包满去抖阈值(tick):连续这么久都满才认定为真满 */
private static final int FULL_DEBOUNCE_TICKS = 20;
/** Baritone 空闲(附近矿已挖完/寻路失败)连续这么久后,转移到新区域继续找(tick) */
private static final int RELOCATE_TICKS = 60;
/** 转移的随机水平距离(方块) */
private static final int RELOCATE_DISTANCE = 16;
/** 转移寻路超时(毫秒):超过则放弃转移,原地继续挖 */
private static final long RELOCATE_TIMEOUT_MS = 15_000;
/** 常见矿物方块 id -> 掉落物品 id(挖掘产物与方块本身不同的情况) */
private static final Map<String, String> ORE_TO_DROP = Map.ofEntries(
@@ -85,6 +93,14 @@ public final class NetherMiner {
private boolean baselineSet;
/** 背包满连续持续的 tick 数(去抖) */
private int fullTicks;
/** Baritone 空闲连续持续的 tick 数(用于触发转移新区域) */
private int idleTicks;
/** 是否正在转移到新矿区 */
private boolean relocating;
/** 转移目标点 */
private BlockPos relocateTarget;
/** 转移开始时间戳 */
private long relocateStart;
public NetherMiner(BaritoneBridge bridge) {
this.bridge = bridge;
@@ -103,6 +119,10 @@ public final class NetherMiner {
this.baselineCount = 0;
this.baselineSet = false;
this.fullTicks = 0;
this.idleTicks = 0;
this.relocating = false;
this.relocateTarget = null;
this.relocateStart = 0;
}
/**
@@ -189,30 +209,15 @@ public final class NetherMiner {
} else if (this.minedEnough(config, player)) {
// 已挖够目标数量
this.miningStarted = false;
this.relocating = false;
if (config.netherAutoReturn) {
this.stage = Stage.RETURN_PORTAL;
} else {
this.done = true;
}
} else if (this.miningStarted && !this.bridge.isActive()) {
// 挖矿进程已跑完(挖够数量或附近无矿可挖)
this.miningStarted = false;
if (config.netherAutoReturn) {
this.stage = Stage.RETURN_PORTAL;
} else {
this.stage = Stage.GO_COMMIT; // 停在下界继续挖(不返回)
}
} else {
// 先派发挖矿指令;派发成功后 miningStarted 置位,
// 之后才能用 isActive()==false 判定「挖完了」
this.issue(() -> {
List<String> ores = config.getNetherOreList();
if (!ores.isEmpty()) {
int count = config.netherTargetCount > 0 ? config.netherTargetCount : -1;
this.bridge.mineByName(count, ores.toArray(String[]::new));
this.miningStarted = true;
}
});
// 未挖够目标数量:持续挖矿,绝不因 Baritone 空闲误判"任务完成"
this.mineTick(config, player);
}
}
case RETURN_PORTAL -> {
@@ -228,13 +233,76 @@ public final class NetherMiner {
/** 判断是否已挖够目标矿物数量(按背包产物计数,扣除任务开始时的基线)。 */
private boolean minedEnough(DigConfig config, PlayerEntity player) {
this.minedCount = this.countProducts(config, player) - this.baselineCount;
if (config.netherTargetCount <= 0) {
return false; // -1 或 0 表示无限挖
}
this.minedCount = this.countProducts(config, player) - this.baselineCount;
return this.minedCount >= config.netherTargetCount;
}
/**
* 未挖够目标时的挖矿驱动:空闲转移到新矿区、持续派发挖矿指令。
*
* <p>关键:Baritone 空闲(附近矿挖完或寻路失败)时,不再把它当成"任务完成"
* 回主世界,而是转移到附近新位置继续找矿;无限目标(-1)下只要没背包满/
* 血量低/装备耐久低,就一直在下界挖。</p>
*/
private void mineTick(DigConfig config, PlayerEntity player) {
if (this.relocating) {
double d = this.distanceSq(player, this.relocateTarget);
if (d <= 4.0) {
// 已到新矿区
this.relocating = false;
this.relocateTarget = null;
this.miningStarted = false;
} else if (System.currentTimeMillis() - this.relocateStart > RELOCATE_TIMEOUT_MS) {
// 转移超时(目标点不可达):放弃转移,原地继续挖
this.relocating = false;
this.relocateTarget = null;
this.bridge.cancelAll();
} else if (!this.bridge.isActive()) {
// 转移途中寻路失败/停下:重发
this.bridge.gotoBlock(this.relocateTarget);
}
return;
}
if (this.miningStarted && !this.bridge.isActive()) {
// 附近矿已挖完或寻路失败:连续空闲一段时间后转移到新区域继续找
this.idleTicks++;
if (this.idleTicks >= RELOCATE_TICKS) {
this.idleTicks = 0;
this.miningStarted = false;
this.relocateToNewArea(player);
}
return;
}
if (!this.miningStarted) {
this.idleTicks = 0;
List<String> ores = config.getNetherOreList();
if (!ores.isEmpty()) {
// 无限目标(-1),实际战果由 minedEnough 计数
this.bridge.mineByName(-1, ores.toArray(String[]::new));
this.miningStarted = true;
}
}
}
/** 转移到玩家附近的新位置(随机水平偏移),继续找矿。 */
private void relocateToNewArea(PlayerEntity player) {
int dx = ThreadLocalRandom.current().nextInt(-RELOCATE_DISTANCE, RELOCATE_DISTANCE);
int dz = ThreadLocalRandom.current().nextInt(-RELOCATE_DISTANCE, RELOCATE_DISTANCE);
this.relocateTarget = player.getBlockPos().add(dx, 0, dz);
this.relocateStart = System.currentTimeMillis();
this.relocating = true;
this.bridge.gotoBlock(this.relocateTarget);
}
private double distanceSq(PlayerEntity player, BlockPos target) {
double dx = player.getX() - (target.getX() + 0.5);
double dz = player.getZ() - (target.getZ() + 0.5);
return dx * dx + dz * dz;
}
/** 统计背包中目标矿物产物的总数量(方块 id 映射到掉落物 id)。 */
private int countProducts(DigConfig config, PlayerEntity player) {
List<String> ores = config.getNetherOreList();
@@ -45,6 +45,11 @@ public final class OverworldMiner {
private long lastIssue;
/** 上一次打印诊断日志的时间戳,用于节流 */
private long lastLog;
/** 上一次成功下发的目标点(避免同一目标反复重发) */
private BlockPos lastIssuedTarget;
/** 当前巷道目标点的最近进度时间戳与最佳距离(用于超时/不可达放弃) */
private long lastProgress;
private double bestDist;
/** 背包满连续持续的 tick 数(去抖:给自动丢垃圾留出腾位时间) */
private int fullTicks;
/** 矿物绕道目标;非 null 时优先前往挖掘该矿物 */
@@ -67,6 +72,8 @@ public final class OverworldMiner {
private static final int DETOUR_TIMEOUT_TICKS = 400;
/** 同一矿点放弃后的重试冷却(毫秒) */
private static final long ABANDON_COOLDOWN_MS = 30_000;
/** 巷道目标点不可达超时(毫秒):超过此时间仍未接近,放弃该点跳到下一个 */
private static final long WAYPOINT_STUCK_MS = 20_000;
public OverworldMiner(BaritoneBridge bridge) {
this.bridge = bridge;
@@ -101,6 +108,9 @@ public final class OverworldMiner {
this.detourTicks = 0;
this.abandonedTarget = null;
this.abandonedAt = 0;
this.lastIssuedTarget = null;
this.lastProgress = System.currentTimeMillis();
this.bestDist = Double.MAX_VALUE;
}
private List<BlockPos> buildPlan(BlockPos base, int y, int trunkLen, int branchLen, int branchGap, int axis, int sign) {
@@ -166,9 +176,26 @@ public final class OverworldMiner {
return false; // 绕道期间不推进巷道
}
long now = System.currentTimeMillis();
// 跳过"目标方块本身是岩浆"的巷道点(无法立足,Baritone 必然找不到路)
// 旁边有岩浆的点不跳(正常挖矿很常见),交给下方 20 秒无进展超时兜底
while (this.step < this.plan.size()
&& client.world.getBlockState(this.plan.get(this.step)).isOf(Blocks.LAVA)) {
System.out.println("[AutoDig] overworld 跳过岩浆上的巷道点 step=" + this.step + " target=" + this.plan.get(this.step));
this.step++;
this.lastIssuedTarget = null;
this.lastProgress = now;
this.bestDist = Double.MAX_VALUE;
}
if (this.step >= this.plan.size()) {
this.done = true;
System.out.println("[AutoDig] overworld 结束:后续巷道点均不可立足,计划提前结束");
return true;
}
BlockPos target = this.plan.get(this.step);
double dist = this.distanceSq(player, target);
long now = System.currentTimeMillis();
if (now - this.lastLog > 5000) {
this.lastLog = now;
System.out.println("[AutoDig] overworld: step=" + this.step + "/" + this.plan.size()
@@ -178,14 +205,37 @@ public final class OverworldMiner {
if (dist <= 2.0) {
// 已到达当前目标,推进到下一个
this.step++;
this.lastIssuedTarget = null;
this.lastProgress = now;
this.bestDist = Double.MAX_VALUE;
return this.step >= this.plan.size();
}
// 距离太长或 Baritone 没在干活时重新派发
if (dist > 4.0 || !this.bridge.isActive()) {
if (now - this.lastIssue > 250) {
this.bridge.gotoBlock(target);
this.lastIssue = now;
}
// 进度跟踪:只要在接近目标就刷新"最近有进展"时间
if (dist < this.bestDist) {
this.bestDist = dist;
this.lastProgress = now;
}
// 长时间无进展(目标不可达,如岩浆/熔岩/墙挡路)→ 放弃该点,跳到下一个
if (now - this.lastProgress > WAYPOINT_STUCK_MS) {
System.out.println("[AutoDig] overworld 目标长时间不可达,跳过 step=" + this.step + " target=" + target);
this.bridge.cancelAll(); // 释放旧目标,避免玩家被拉回
this.step++;
this.lastIssuedTarget = null;
this.lastProgress = now;
this.bestDist = Double.MAX_VALUE;
return this.step >= this.plan.size();
}
// 下发寻路:换了新目标才立即下发;同一目标仅在 Baritone 空闲时低频重试
// 避免 250ms 狂发同目标导致 Baritone 永远在"规划→取消→规划"的死循环
if (!target.equals(this.lastIssuedTarget)) {
this.bridge.gotoBlock(target);
this.lastIssuedTarget = target;
this.lastIssue = now;
this.lastProgress = now;
this.bestDist = Double.MAX_VALUE;
} else if (!this.bridge.isActive() && now - this.lastIssue > 2000) {
this.bridge.gotoBlock(target);
this.lastIssue = now;
}
return false;
}
@@ -213,7 +263,8 @@ public final class OverworldMiner {
this.detourTarget = null;
return false;
}
if (now - this.lastIssue > 250) {
// 仅在 Baritone 空闲时低频重发,避免打断正在规划的路线
if (!this.bridge.isActive() && now - this.lastIssue > 1000) {
this.bridge.gotoBlock(this.detourTarget);
this.lastIssue = now;
}
@@ -350,5 +401,8 @@ public final class OverworldMiner {
this.done = false;
this.full = false;
this.fullTicks = 0;
this.lastIssuedTarget = null;
this.lastProgress = System.currentTimeMillis();
this.bestDist = Double.MAX_VALUE;
}
}
@@ -121,6 +121,11 @@ public final class AutoGear {
* @return true 表示手持工具适用(或无需工具),false 表示没有合适工具
*/
public boolean ensureToolForOres(DigConfig config, PlayerEntity player) {
// 正在进食时不切换手持物品:否则会把手里的食物换成镐子,
// 与 AutoEat 抢同一槽位造成"食物/镐子来回切换却始终没吃上"
if (player.isUsingItem()) {
return true;
}
long now = System.currentTimeMillis();
if (now - this.lastToolCheck < TOOL_CHECK_INTERVAL_MS) {
return this.lastToolOk;