From fe28bf4f1ac79028385d3b118948f61d1f965e05 Mon Sep 17 00:00:00 2001 From: ECJKropas <3462695908@qq.com> Date: Thu, 30 Jul 2026 12:49:28 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20=E7=A6=81=E7=94=A8=E9=9E=98=E7=BF=85?= =?UTF-8?q?=E9=A3=9E=E8=A1=8C=E6=97=B6=E7=9A=84=E8=A1=A5=E7=BB=99=E7=9B=92?= =?UTF-8?q?=E6=8B=86=E9=99=A4=E5=92=8C=E5=8D=B8=E8=B4=A7=E7=82=B9=E5=AF=BC?= =?UTF-8?q?=E8=88=AA=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client/navigation/NavigationService.java | 7 +++++++ .../client/state/AutomationController.java | 12 +++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/client/java/hackerrouter/perimeterdigger/client/navigation/NavigationService.java b/src/client/java/hackerrouter/perimeterdigger/client/navigation/NavigationService.java index 0f3d0d9..13a0151 100644 --- a/src/client/java/hackerrouter/perimeterdigger/client/navigation/NavigationService.java +++ b/src/client/java/hackerrouter/perimeterdigger/client/navigation/NavigationService.java @@ -87,6 +87,13 @@ public void disablePlacement() { BaritoneAPI.getSettings().allowPlace.value = false; } + public void disableBreakingAndPlacement() { + if (savedAllowBreak == null) savedAllowBreak = BaritoneAPI.getSettings().allowBreak.value; + if (savedAllowPlace == null) savedAllowPlace = BaritoneAPI.getSettings().allowPlace.value; + BaritoneAPI.getSettings().allowBreak.value = false; + BaritoneAPI.getSettings().allowPlace.value = false; + } + public void enableBreakingWithoutPlacement() { if (savedAllowBreak == null) savedAllowBreak = BaritoneAPI.getSettings().allowBreak.value; if (savedAllowPlace == null) savedAllowPlace = BaritoneAPI.getSettings().allowPlace.value; diff --git a/src/client/java/hackerrouter/perimeterdigger/client/state/AutomationController.java b/src/client/java/hackerrouter/perimeterdigger/client/state/AutomationController.java index 97e4d33..0b16546 100644 --- a/src/client/java/hackerrouter/perimeterdigger/client/state/AutomationController.java +++ b/src/client/java/hackerrouter/perimeterdigger/client/state/AutomationController.java @@ -1116,7 +1116,7 @@ private AutomationState supplyInteractionState() { private void startWalkingToSupply() { supply.flying = false; - disablePlacementForNavigation(); + disableBreakingAndPlacementForNavigation(); BlockPos chest = position(supply.point); if (isAtInteractionStand(chest)) { restoreAllowPlace(); @@ -1180,6 +1180,10 @@ private void disablePlacementForNavigation() { navigation.disablePlacement(); } + private void disableBreakingAndPlacementForNavigation() { + navigation.disableBreakingAndPlacement(); + } + private void enableBreakingForNavigation() { navigation.enableBreakingWithoutPlacement(); } @@ -1903,7 +1907,8 @@ private void requestUnload() { unload.point = unloadingPoints.stream() .min(Comparator.comparingLong(point -> horizontalDistanceSquared(player, point.point()))) .orElseThrow(); - UnloadFlow.Candidate preferred = findUnloadCandidates(unload.point.point(), player.y).stream().findFirst().orElse(null); + int preferredY = Math.max(player.y, unload.point.point().minY); + UnloadFlow.Candidate preferred = findUnloadCandidates(unload.point.point(), preferredY).stream().findFirst().orElse(null); BlockPos flightTarget = preferred == null ? new BlockPos(unload.point.point().x, player.y, unload.point.point().z) : preferred.position(); @@ -1927,7 +1932,8 @@ private void beginUnloadApproach() { navigation.stopFlying(); disablePlacementForNavigation(); int currentY = baritone.getPlayerContext().playerFeet().y; - unload.candidates = findUnloadCandidates(unload.point.point(), currentY); + int preferredY = Math.max(currentY, unload.point.point().minY); + unload.candidates = findUnloadCandidates(unload.point.point(), preferredY); unload.candidateIndex = 0; if (unload.candidates.isEmpty()) { restoreAllowPlace(); From 597b98d015af5dbf26e1c93fb5c22c45a6d729be Mon Sep 17 00:00:00 2001 From: ECJKropas <3462695908@qq.com> Date: Thu, 30 Jul 2026 16:14:56 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20=E8=A1=A5=E7=BB=99=E7=AE=B1=E5=AF=BC?= =?UTF-8?q?=E8=88=AA=E4=BF=AE=E5=A4=8D=E3=80=81repair=E9=99=8D=E7=BA=A7?= =?UTF-8?q?=E8=A1=A5=E7=BB=99=E3=80=81=E6=8C=96=E7=9F=BF=E5=81=9C=E6=BB=9E?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E8=84=B1=E5=9B=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Bug 1: 禁用鞘翅飞行时walk拆补给盒 → allowBreak=true + 导航到箱子旁边 - Bug 2: 禁用鞘翅飞行时导航到卸货点下方 → preferredY修正为max(player.y, unloadPoint.minY) - Bug 3: repair_portal未配熔炉时不降级补给 → 加fallback到耐久补给点 - 新增: MINING停滞检测,8秒不移动自动随机方向脱困,15秒冷却 --- gradlew | 0 .../client/config/AdvancedConfig.java | 4 + .../client/state/AutomationController.java | 120 ++++++++++++++++-- .../assets/perimeter-digger/lang/en_us.json | 2 + .../assets/perimeter-digger/lang/zh_cn.json | 2 + 5 files changed, 120 insertions(+), 8 deletions(-) mode change 100644 => 100755 gradlew diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 diff --git a/src/client/java/hackerrouter/perimeterdigger/client/config/AdvancedConfig.java b/src/client/java/hackerrouter/perimeterdigger/client/config/AdvancedConfig.java index 6c621a2..868f592 100644 --- a/src/client/java/hackerrouter/perimeterdigger/client/config/AdvancedConfig.java +++ b/src/client/java/hackerrouter/perimeterdigger/client/config/AdvancedConfig.java @@ -49,4 +49,8 @@ public final class AdvancedConfig { public double supplyInteractionTimeoutSeconds = 2.0; public double furnaceInteractionTimeoutSeconds = 2.0; public int flightRetryCount = 2; + public boolean miningStallDetectionEnabled = true; + public double miningStallTimeoutSeconds = 8.0; + public int miningStallMovementDistance = 3; + public double miningStallCooldownSeconds = 15.0; } diff --git a/src/client/java/hackerrouter/perimeterdigger/client/state/AutomationController.java b/src/client/java/hackerrouter/perimeterdigger/client/state/AutomationController.java index 0b16546..60a081c 100644 --- a/src/client/java/hackerrouter/perimeterdigger/client/state/AutomationController.java +++ b/src/client/java/hackerrouter/perimeterdigger/client/state/AutomationController.java @@ -127,6 +127,11 @@ public final class AutomationController { private Vec3 watchdogPosition; private int watchdogStationaryScans; private int watchdogRetries; + private Vec3 miningStallPosition; + private int miningStallStationaryScans; + private int miningStallLastTriggerTick; + private boolean miningStallRecovery; + private BlockPos miningStallRecoveryTarget; private int stableInventoryScans; private int lastInventoryItemCount; private boolean miningCompletePending; @@ -310,6 +315,7 @@ public List debugSleep(PerimeterConfig config) { public void tick() { if (baritone == null || baritone.getPlayerContext().player() == null) return; executeInventoryClick(); + if (miningStallRecovery && tickMiningStallRecovery()) return; if (state == AutomationState.EATING) { tickEating(); return; @@ -375,6 +381,7 @@ && shouldEat() if (state == AutomationState.IDLE || state == AutomationState.ERROR || state == AutomationState.COMPLETE || state == AutomationState.PAUSED ) return; if (!inventoryClicks.isEmpty()) return; + if (state == AutomationState.MINING && advanced.miningStallDetectionEnabled) tickMiningStallDetection(); ResourceCheck resources = inspectResources(); if (sleepEnabled && bedPoint != null && isBedTime()) { beginSleepFlow(false); @@ -399,9 +406,14 @@ && shouldEat() if (durabilityRecoveryMode.equals("supply_point") && durabilitySupplyPoint != null) { beginSupply(SupplyFlow.Kind.DURABILITY, false); return; - } else if (durabilityRecoveryMode.equals("repair_portal") && hasUsableRepairConfiguration()) { - beginRepairFlow(false); - return; + } else if (durabilityRecoveryMode.equals("repair_portal")) { + if (hasUsableRepairConfiguration()) { + beginRepairFlow(false); + return; + } else if (durabilitySupplyPoint != null) { + beginSupply(SupplyFlow.Kind.DURABILITY, false); + return; + } } } if (resupplyEnabled && consumableSupplyPoint != null @@ -430,6 +442,7 @@ public void stop() { returnPortalWaypoint = null; inventoryClicks.clear(); resetNavigationWatchdog(); + resetMiningStall(); unload.reset(); clearSleepContext(); clearSupplyContext(); @@ -558,6 +571,7 @@ else synchronize(AutomationState.PAUSED, Translations.DETAIL.message("mining_pau } private void startMiningCycle() { + resetMiningStall(); int emptySlots = emptyInventorySlots(); if (emptySlots == 0 && unloadEnabled) { requestUnload(); @@ -572,6 +586,7 @@ private void startMiningCycle() { } private void resumeMiningCycle() { + resetMiningStall(); int emptySlots = emptyInventorySlots(); if (emptySlots == 0 && unloadEnabled) { requestUnload(); @@ -1023,6 +1038,24 @@ private Optional closestBedStand(BlockPos bed) { return interactionPositionFinder.closest(Minecraft.getInstance().level, bed, baritone.getPlayerContext().playerFeet()); } + private BlockPos findSafeStandNear(BlockPos chest) { + BetterBlockPos player = baritone.getPlayerContext().playerFeet(); + List candidates = new ArrayList<>(); + for (int dy = -1; dy <= 1; dy++) { + BlockPos base = chest.atY(chest.getY() + dy); + for (int dx = -advanced.unloadLandingSearchRadius; dx <= advanced.unloadLandingSearchRadius; dx++) { + for (int dz = -advanced.unloadLandingSearchRadius; dz <= advanced.unloadLandingSearchRadius; dz++) { + if (dx == 0 && dz == 0 && dy == 0) continue; + BlockPos position = base.offset(dx, 0, dz); + if (isSafeStandingPosition(position)) { + candidates.add(position); + } + } + } + } + return candidates.stream().min(Comparator.comparingDouble(player::distSqr)).orElse(null); + } + private boolean canReachFurnace(BlockPos furnace) { return canReachInteractionTarget(furnace); } @@ -1116,7 +1149,7 @@ private AutomationState supplyInteractionState() { private void startWalkingToSupply() { supply.flying = false; - disableBreakingAndPlacementForNavigation(); + enableBreakingForNavigation(); BlockPos chest = position(supply.point); if (isAtInteractionStand(chest)) { restoreAllowPlace(); @@ -1127,15 +1160,22 @@ private void startWalkingToSupply() { if (!stands.isEmpty()) { supply.stand = stands.stream().min(Comparator.comparingDouble(baritone.getPlayerContext().playerFeet()::distSqr)).orElseThrow(); navigation.walk(new GoalComposite(stands.stream().map(GoalBlock::new).toArray(Goal[]::new))); - } else { - supply.stand = chest.above(); + transition(supplyNavigationState(), Translations.DETAIL.message("walking_supply", supply.kind.displayName, supply.stand.toShortString())); + return; + } + BlockPos widerStand = findSafeStandNear(chest); + if (widerStand != null) { + supply.stand = widerStand; navigation.walk(new GoalBlock(supply.stand)); + transition(supplyNavigationState(), Translations.DETAIL.message("walking_supply", supply.kind.displayName, supply.stand.toShortString())); + return; } - transition(supplyNavigationState(), Translations.DETAIL.message("walking_supply", supply.kind.displayName, supply.stand.toShortString())); + restoreAllowPlace(); + transition(AutomationState.ERROR, Translations.DETAIL.message("supply_no_safe_stand", supply.kind.displayName, chest.toShortString())); } private void tickSupplyNavigation() { - if (isAtInteractionStand(position(supply.point))) { + if (isAtInteractionStand(position(supply.point)) || baritone.getPlayerContext().playerFeet().equals(supply.stand)) { navigation.stopFlying(); navigation.stopWalking(); restoreElytraMinimumDurability(); @@ -2325,6 +2365,70 @@ private void resetNavigationWatchdog() { watchdogRetries = 0; } + private void tickMiningStallDetection() { + Vec3 position = baritone.getPlayerContext().player().position(); + if (miningStallPosition == null || position.distanceToSqr(miningStallPosition) > 0.25) { + miningStallPosition = position; + miningStallStationaryScans = 0; + return; + } + if (++miningStallStationaryScans < monitorScans(advanced.miningStallTimeoutSeconds)) return; + int cooldownTicks = ticks(advanced.miningStallCooldownSeconds); + if (tickCounter - miningStallLastTriggerTick < cooldownTicks) return; + BlockPos target = randomNearbySafeTarget(advanced.miningStallMovementDistance); + if (target == null) return; + miningStallStationaryScans = 0; + miningStallLastTriggerTick = tickCounter; + miningStallRecovery = true; + miningStallRecoveryTarget = target; + miningProcess.pause(); + navigation.walk(new GoalBlock(target)); + transition(AutomationState.MINING, Translations.DETAIL.message("mining_stall_recovery", target.toShortString())); + } + + private boolean tickMiningStallRecovery() { + BlockPos feet = baritone.getPlayerContext().playerFeet(); + if (feet.equals(miningStallRecoveryTarget) || !navigation.isWalking()) { + navigation.stopWalking(); + miningStallRecovery = false; + miningStallRecoveryTarget = null; + miningStallPosition = null; + miningStallStationaryScans = 0; + resumeMiningCycle(); + return true; + } + return false; + } + + private BlockPos randomNearbySafeTarget(int distance) { + BlockPos feet = baritone.getPlayerContext().playerFeet(); + double[] angles = new double[8]; + for (int i = 0; i < 8; i++) angles[i] = Math.random() * 2.0 * Math.PI; + for (double angle : angles) { + int dx = (int) Math.round(Math.sin(angle) * distance); + int dz = (int) Math.round(Math.cos(angle) * distance); + if (dx == 0 && dz == 0) continue; + BlockPos candidate = feet.offset(dx, 0, dz); + if (isSafeStandingPosition(candidate)) return candidate; + } + for (int dx = -distance; dx <= distance; dx++) { + for (int dz = -distance; dz <= distance; dz++) { + if (dx == 0 && dz == 0) continue; + if (dx * dx + dz * dz > distance * distance) continue; + BlockPos candidate = feet.offset(dx, 0, dz); + if (isSafeStandingPosition(candidate)) return candidate; + } + } + return null; + } + + private void resetMiningStall() { + miningStallPosition = null; + miningStallStationaryScans = 0; + miningStallRecovery = false; + miningStallRecoveryTarget = null; + } + private int firstDisposableSlot() { Inventory inventory = baritone.getPlayerContext().player().getInventory(); for (int slot = 0; slot < 36; slot++) { diff --git a/src/main/resources/assets/perimeter-digger/lang/en_us.json b/src/main/resources/assets/perimeter-digger/lang/en_us.json index d112045..e52c039 100644 --- a/src/main/resources/assets/perimeter-digger/lang/en_us.json +++ b/src/main/resources/assets/perimeter-digger/lang/en_us.json @@ -131,6 +131,7 @@ "perimeterdigger.detail.supply_point_missing": "No %1$s supply point is configured.", "perimeterdigger.detail.walking_supply": "Walking to the %1$s supply chest %2$s.", "perimeterdigger.detail.supply_unreachable": "The %1$s supply chest is unreachable: %2$s.", + "perimeterdigger.detail.supply_no_safe_stand": "No safe standing position was found near the %1$s supply chest at %2$s.", "perimeterdigger.detail.flying_supply": "Flying to the %1$s supply point %2$s.", "perimeterdigger.detail.preparing_supply": "Preparing the %1$s supply chest.", "perimeterdigger.detail.supply_open_timeout": "Timed out while opening the %1$s supply chest.", @@ -202,6 +203,7 @@ "perimeterdigger.detail.repair_debug_complete": "Repair debug flow complete.", "perimeterdigger.detail.sleep_debug_complete": "Sleep debug flow complete.", "perimeterdigger.detail.navigation_stalled": "Navigation made no positional progress after %1$s retries toward %2$s.", + "perimeterdigger.detail.mining_stall_recovery": "Mining stalled; attempting stall recovery move to %1$s.", "perimeterdigger.detail.replacing_item": "Replacing low-durability %1$s.", "perimeterdigger.detail.replaced_item": "Replaced low-durability %1$s.", "perimeterdigger.detail.mining_paused": "Mining paused: %1$s. %2$s", diff --git a/src/main/resources/assets/perimeter-digger/lang/zh_cn.json b/src/main/resources/assets/perimeter-digger/lang/zh_cn.json index ea7c0af..9ae8828 100644 --- a/src/main/resources/assets/perimeter-digger/lang/zh_cn.json +++ b/src/main/resources/assets/perimeter-digger/lang/zh_cn.json @@ -131,6 +131,7 @@ "perimeterdigger.detail.supply_point_missing": "未配置 %1$s 补给点。", "perimeterdigger.detail.walking_supply": "正在步行前往 %1$s 补给箱 %2$s。", "perimeterdigger.detail.supply_unreachable": "无法到达 %1$s 补给箱:%2$s。", + "perimeterdigger.detail.supply_no_safe_stand": "在 %1$s 补给箱 %2$s 附近未找到安全站立位置。", "perimeterdigger.detail.flying_supply": "正在飞向 %1$s 补给点 %2$s。", "perimeterdigger.detail.preparing_supply": "正在准备操作 %1$s 补给箱。", "perimeterdigger.detail.supply_open_timeout": "打开 %1$s 补给箱超时。", @@ -202,6 +203,7 @@ "perimeterdigger.detail.repair_debug_complete": "修复调试流程已完成。", "perimeterdigger.detail.sleep_debug_complete": "睡眠调试流程已完成。", "perimeterdigger.detail.navigation_stalled": "向 %2$s 寻路时连续 %1$s 次重试均无位置进展。", + "perimeterdigger.detail.mining_stall_recovery": "挖掘停滞,尝试向 %1$s 脱困移动。", "perimeterdigger.detail.replacing_item": "正在更换低耐久物品 %1$s。", "perimeterdigger.detail.replaced_item": "已更换低耐久物品 %1$s。", "perimeterdigger.detail.mining_paused": "挖掘已暂停:%1$s。%2$s",