From e84ac7cf41174e5c49a0ee3db518be8d5248c886 Mon Sep 17 00:00:00 2001 From: lyizai <3945107889@qq.com> Date: Tue, 1 Sep 2026 13:41:42 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=B8=BB=E4=B8=96=E7=95=8C=E5=B2=A9?= =?UTF-8?q?=E6=B5=86=E5=8D=A1=E6=AD=BB=20+=20=E4=B8=8B=E7=95=8C=E6=8C=96?= =?UTF-8?q?=E7=9F=BF=E5=A4=9A=E9=A1=B9=E4=BF=AE=E5=A4=8D=20(v2.2.3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 主世界:目标点岩浆卡死——同目标不再重复下发、无进展20秒放弃跳过、绕道仅空闲时低频重发,避免玩家被拉回岩浆边 下界:垃圾清单补灵魂沙/灵魂土/岩浆块/沙砾/下界砖;进食不再被切工具打断;挖矿中不再每秒重发mineByName(解决灵魂沙抽搐/来回折返);寻路失败不再误判完成、自动转移新矿区续挖 --- README.md | 1 + gradle.properties | 2 +- .../autodig/client/config/ConfigStore.java | 2 +- .../java/autodig/client/config/DigConfig.java | 2 +- .../autodig/client/miner/NetherMiner.java | 106 ++++++++++++++---- .../autodig/client/miner/OverworldMiner.java | 70 ++++++++++-- .../java/autodig/client/service/AutoGear.java | 5 + 7 files changed, 158 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index d0544b5..0094eb4 100644 --- a/README.md +++ b/README.md @@ -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 补全进食/保命/攻击范围等设置。 diff --git a/gradle.properties b/gradle.properties index 4fa5103..c53fbf6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -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 diff --git a/src/main/java/autodig/client/config/ConfigStore.java b/src/main/java/autodig/client/config/ConfigStore.java index 692ece0..085ef64 100644 --- a/src/main/java/autodig/client/config/ConfigStore.java +++ b/src/main/java/autodig/client/config/ConfigStore.java @@ -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"; diff --git a/src/main/java/autodig/client/config/DigConfig.java b/src/main/java/autodig/client/config/DigConfig.java index 1af00b6..a92fde9 100644 --- a/src/main/java/autodig/client/config/DigConfig.java +++ b/src/main/java/autodig/client/config/DigConfig.java @@ -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; diff --git a/src/main/java/autodig/client/miner/NetherMiner.java b/src/main/java/autodig/client/miner/NetherMiner.java index 2697fb8..31370e9 100644 --- a/src/main/java/autodig/client/miner/NetherMiner.java +++ b/src/main/java/autodig/client/miner/NetherMiner.java @@ -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 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 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; } + /** + * 未挖够目标时的挖矿驱动:空闲转移到新矿区、持续派发挖矿指令。 + * + *

关键:Baritone 空闲(附近矿挖完或寻路失败)时,不再把它当成"任务完成" + * 回主世界,而是转移到附近新位置继续找矿;无限目标(-1)下只要没背包满/ + * 血量低/装备耐久低,就一直在下界挖。

+ */ + 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 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 ores = config.getNetherOreList(); diff --git a/src/main/java/autodig/client/miner/OverworldMiner.java b/src/main/java/autodig/client/miner/OverworldMiner.java index c2e7875..a8f1dbe 100644 --- a/src/main/java/autodig/client/miner/OverworldMiner.java +++ b/src/main/java/autodig/client/miner/OverworldMiner.java @@ -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 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; } } \ No newline at end of file diff --git a/src/main/java/autodig/client/service/AutoGear.java b/src/main/java/autodig/client/service/AutoGear.java index bcd07e4..4492507 100644 --- a/src/main/java/autodig/client/service/AutoGear.java +++ b/src/main/java/autodig/client/service/AutoGear.java @@ -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;