4 次代码提交
作者 SHA1 备注 提交日期
lyizai bc86c26d1e v1.2.5: 高度偏好寻路(优先往高处走) + 寻路/重试前校验目标接壤 + 超时先走两步再传送
- 新增 GoalXZAltitude 寻路目标:启发式成本 = 水平距离 - 高度权重×y,y 越大优先级越高、y 越小施惩罚,引导寻路优先走高
- ExpansionConfig 新增 altitudePreference/altitudeWeight 配置(默认开/0.1),BaritoneBridge 按配置选择原生 GoalXZ 或高度偏好 Goal
- 寻路/重试前先检查目标区块是否仍与己方领土接壤(startPathing/retrySameTarget),不接壤立即跳过
- 寻路超时不再直接传送:重试耗尽后先往边上走两步自救(每轮重试原目标,最多 sideStepMaxRounds=3 轮),多轮无效才按连续超时阈值回城或切换目标
2026-08-21 17:02:43 +08:00
lyizai a0963747b6 v1.2.4: 寻路重试耗尽先往边上走两步再重试原目标,仍失败才回城或切换目标 2026-08-21 16:25:00 +08:00
lyizai 08e15ffc9b v1.2.4: 连续寻路超时超过1分钟自动发送/u return回城,传送后从最高优先级区块重新扩地 2026-08-21 16:10:02 +08:00
lyizai a9c71861f5 v1.2.3: 更新README安装/构建的jar版本引用至1.2.3 2026-08-21 16:02:10 +08:00
共修改 7 个文件,包含 286 行新增和 19 行删除
+5 -3
查看文件
@@ -7,7 +7,9 @@
- 从远程 `markers.json` 拉取各国领土边界
- 四种扩地策略:方形外扩、区域扩地、玩家周围、绕国一圈
- 区域扩地采用 S 形扫描:从与己方领土接壤的区块开始,横向/纵向 S 形扫过整个矩形,不沿邦国边界,但保证每个新占领区块都与**己方领土**接壤(仅四个正方向,不把其他邦国领土计入接壤)
- 自动寻路(Baritone)前往目标区块;监听 Baritone 路径事件,目标不可达(CALC_FAILED,如被悬崖/高山/水体隔断)时立即跳过;寻路超时则重新寻路(不立即回城),重试耗尽才跳过当前目标
- 自动寻路(Baritone)前往目标区块;监听 Baritone 路径事件,目标不可达(CALC_FAILED,如被悬崖/高山/水体隔断)时重试、耗尽后先"往边上走两步"再试,仍失败才切换目标;寻路超时则重新寻路(不立即回城)
- 高度偏好寻路:寻路优先往高处走(y 越大优先级越高,y 越小有惩罚),适合山区/高原目标;可通过配置关闭或调整权重
- 连续寻路超时回城:连续寻路超时累计超过 1 分钟(可配置)时,先反复尝试"往边上走两步"脱困(默认最多 3 轮),仍无法脱困才自动发送 `/u return`,约 10 秒后传送到可寻路位置,随后重新规划并从最高优先级区块重新开始扩地
- 运行时接壤校验:寻路/等待阶段每帧校验目标是否仍与己方领土接壤,地图刷新导致边界变化时立即跳过
- 目标有效性校验:目标若已被其他邦国占领则立即跳过
- 现实时间限制:仅 11:00-22:00(本地现实时间)允许开拓,其余时段启动会被拒绝、运行中到点自动停止
@@ -24,7 +26,7 @@
## 安装
1. 安装 Fabric Loader 0.16.14+ 和 Fabric API
2. 将 `autostore-1.2.1.jar` 放入 `.minecraft/mods/` 目录
2. 将 `autostore-1.2.5.jar` 放入 `.minecraft/mods/` 目录
3. 启动游戏,按 `Ctrl+R` 打开 GUI
## 构建
@@ -34,7 +36,7 @@
./build_mod.sh
```
输出文件:`autostore-1.2.1.jar`
输出文件:`autostore-1.2.5.jar`
## 赞助支持
+1 -1
查看文件
@@ -7,7 +7,7 @@ yarn_mappings=1.21.4+build.8
loader_version=0.19.3
# Mod Properties
mod_version=1.2.3
mod_version=1.2.5
maven_group=autostore
archives_base_name=autostore
@@ -11,6 +11,7 @@ import baritone.api.event.events.PathEvent;
import baritone.api.event.listener.AbstractGameEventListener;
import autostore.client.config.ExpansionConfig;
import autostore.client.territory.ChunkPos;
import baritone.api.pathing.goals.Goal;
import baritone.api.pathing.goals.GoalXZ;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -39,6 +40,8 @@ public final class BaritoneBridge {
private final Set<PathListener> pathListeners = ConcurrentHashMap.newKeySet();
/** 是否已向 Baritone 注册事件监听器(只注册一次) */
private volatile boolean eventListenerRegistered;
/** 最近一次应用的行为配置(applyConfig 时保存,供 pathTo 选择寻路 Goal) */
private ExpansionConfig config = new ExpansionConfig();
/** 注册一个路径事件监听器;同时确保已向 Baritone 事件总线挂载转发器 */
public void addPathListener(PathListener listener) {
@@ -52,6 +55,7 @@ public final class BaritoneBridge {
* @param config 扩地配置
*/
public void applyConfig(ExpansionConfig config) {
this.config = config;
Settings settings = BaritoneAPI.getSettings();
settings.allowSprint.value = config.allowSprint;
settings.allowBreak.value = config.allowBreak;
@@ -72,12 +76,15 @@ public final class BaritoneBridge {
/**
* 让 Baritone 寻路前往指定区块中心。
*
* <p>使用 Baritone <b>原生</b>{@link GoalXZ}(目标区块中心单格):原生 GoalXZ 对
* <p>默认使用 Baritone <b>原生</b>{@link GoalXZ}(目标区块中心单格):原生 GoalXZ 对
* 可达地形几乎不会误判“目标不可达”,是最好的默认选择。早期用自定义
* {@code ChunkGoal}(曾加入高度硬约束)疑似是“平原上一按 W 就能走、Baritone 却秒
* CALC_FAILED”的根因——自定义 Goal 在某些站位/边界下会把可达目标判成不可达。
* 到达判定仍由控制器 {@code playerChunk.equals(target)} 保证,与 Goal 自身容差无关。</p>
*
* <p>当开启高度偏好({@code altitudePreference})时改用 {@link GoalXZAltitude}:
* 到达判定与 GoalXZ 完全一致(水平到达),仅启发式成本偏向高处,引导寻路优先走高。</p>
*
* @param chunk 目标区块
*/
public void pathTo(ChunkPos chunk) {
@@ -85,7 +92,13 @@ public final class BaritoneBridge {
if (baritone == null) {
return;
}
baritone.getCustomGoalProcess().setGoalAndPath(new GoalXZ(chunk.centerBlockX(), chunk.centerBlockZ()));
Goal goal;
if (this.config.altitudePreference) {
goal = new GoalXZAltitude(chunk.centerBlockX(), chunk.centerBlockZ(), this.config.altitudeWeight);
} else {
goal = new GoalXZ(chunk.centerBlockX(), chunk.centerBlockZ());
}
baritone.getCustomGoalProcess().setGoalAndPath(goal);
}
/** 强制取消当前寻路任务。 */
@@ -0,0 +1,51 @@
/*
* 本文件由 autostore-1.0-SNAPSHOT.jar 反编译并整理而来(CFR 0.152),
* 混淆名已替换为 Minecraft 1.21.4 Yarn 可读映射,并补充中文注释。
*/
package autostore.client.bridge;
import baritone.api.pathing.goals.Goal;
import baritone.api.pathing.goals.GoalXZ;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
/**
* 带高度偏好的区块中心寻路目标。
*
* <p>到达判定与 Baritone 原生 {@link GoalXZ} 完全一致(水平到达目标区块中心即成功,
* 不约束高度,避免旧版 ChunkGoal 因高度硬约束误判“不可达”);
* 但启发式成本中加入高度项:y 越大启发成本越低(A* 更倾向走高处),
* y 越低成本越高(对走低处施惩罚),从而实现“寻路优先往高处走”。</p>
*/
@Environment(EnvType.CLIENT)
public final class GoalXZAltitude implements Goal {
/** 目标区块中心 X */
private final int x;
/** 目标区块中心 Z */
private final int z;
/** 高度偏好权重:每上升 1 格减少的启发成本;越大越明显偏向高处 */
private final double altitudeWeight;
public GoalXZAltitude(int x, int z, double altitudeWeight) {
this.x = x;
this.z = z;
this.altitudeWeight = altitudeWeight;
}
/** 到达判定与 GoalXZ 相同:水平坐标命中目标区块中心即算到达,不限制高度 */
@Override
public boolean isInGoal(int x, int y, int z) {
return this.x == x && this.z == z;
}
/**
* 启发式成本 = 水平基础成本(复用 GoalXZ 的对角线切比雪夫估计) - 高度奖励。
* y 越高启发值越小(优先级越高),y 越启发值越大(惩罚);clamp 非负避免破坏 A* 排序。
*/
@Override
public double heuristic(int x, int y, int z) {
double base = GoalXZ.calculate(x - this.x, z - this.z);
double altitudeCost = this.altitudeWeight * y;
return Math.max(0.0, base - altitudeCost);
}
}
@@ -31,6 +31,15 @@ public class ExpansionConfig {
/** 超时后的最大重试次数 */
public int retries = 2;
/** 连续寻路超时累计达到该秒数后,发送 /u return 回城传送并重新开始(默认 60 秒) */
public int returnThresholdSeconds = 60;
/** 发送 /u return 后等待传送完成的时间(秒);默认 12 秒,覆盖 10 秒传送延迟并留余量 */
public int returnWaitSeconds = 12;
/** 单个目标最多触发"往边上走两步"自救的轮数;每轮走两步后仍超时再重试,多轮无效后才回城/切换目标(默认 3) */
public int sideStepMaxRounds = 3;
/** 区域扩地策略的中心 X 坐标(区块) */
public int regionCenterX;
@@ -67,6 +76,12 @@ public class ExpansionConfig {
/** 是否允许 Baritone 放置方块 */
public boolean allowPlace;
/** 寻路高度偏好:true 时寻路优先往高处走(y 越大启发成本越低/优先级越高,y 越小施惩罚) */
public boolean altitudePreference = true;
/** 高度偏好权重:每上升 1 格折算的启发成本减少量(越大越明显偏向高处,默认 0.1) */
public double altitudeWeight = 0.1;
/** 是否启用避开怪物 */
public boolean avoidMobs = true;
@@ -94,6 +109,9 @@ public class ExpansionConfig {
c.targetCount = this.targetCount;
c.waitTimeoutSeconds = this.waitTimeoutSeconds;
c.retries = this.retries;
c.returnThresholdSeconds = this.returnThresholdSeconds;
c.returnWaitSeconds = this.returnWaitSeconds;
c.sideStepMaxRounds = this.sideStepMaxRounds;
c.regionCenterX = this.regionCenterX;
c.regionCenterZ = this.regionCenterZ;
c.regionRadius = this.regionRadius;
@@ -106,6 +124,8 @@ public class ExpansionConfig {
c.allowJump = this.allowJump;
c.allowBreak = this.allowBreak;
c.allowPlace = this.allowPlace;
c.altitudePreference = this.altitudePreference;
c.altitudeWeight = this.altitudeWeight;
c.avoidMobs = this.avoidMobs;
c.autoEat = this.autoEat;
c.eatThreshold = this.eatThreshold;
@@ -33,6 +33,7 @@ import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.client.MinecraftClient;
import net.minecraft.component.DataComponentTypes;
import net.minecraft.text.Text;
@@ -95,6 +96,22 @@ implements SubtitleChatMonitor.Listener, BaritoneBridge.PathListener {
private volatile boolean pathCalcFailed;
/** 等待开拓时段自动开始:不在 11:00-22:00 时点击开始会暂存配置,进入时段后自动启动(null 表示无等待任务) */
private ExpansionConfig pendingStart;
/** 连续寻路超时累计时长(毫秒):多次寻路超时逐次累加,达到阈值触发 /u return 回城传送 */
private long consecutiveTimeoutMillis;
/** 发送 /u return 回城指令的时间戳(RETURNING 状态使用,用于等待传送完成) */
private long returnStartMillis;
/** 当前目标已触发"往边上走两步"的次数(每次重试耗尽先走两步再试,达上限才切换目标/回城) */
private int sideStepCount;
/** 是否正在"往边上走两步"阶段(手动向目标连线垂直方向走开约 2.5 格再重试) */
private boolean sideStepping;
/** 边走两步的方向符号(+1 左侧 / -1 右侧),被挡时换另一侧 */
private int sideStepSign = 1;
/** 边走两步的整体超时截止时间戳(毫秒) */
private long sideStepDeadlineMillis;
/** 最近一次换向/开始的时间戳(毫秒,用于判断该方向是否卡住) */
private long sideStepSinceMillis;
/** 边走两步开始时的玩家方块坐标(用于判断是否已走开约 2.5 格) */
private BlockPos sideStepStartPos;
public ExpansionController(TerritoryDataService dataService) {
this.dataService = dataService;
@@ -211,6 +228,9 @@ implements SubtitleChatMonitor.Listener, BaritoneBridge.PathListener {
}
this.completedClaims = 0;
this.retryCount = 0;
this.consecutiveTimeoutMillis = 0L;
this.sideStepCount = 0;
this.sideStepping = false;
this.queue.clear();
this.claimedSet.clear();
this.target = null;
@@ -234,6 +254,10 @@ implements SubtitleChatMonitor.Listener, BaritoneBridge.PathListener {
return;
}
this.baritoneBridge.cancel();
// 若正处于"往边上走两步"阶段,先释放手动按下的前进键
if (this.sideStepping) {
this.stopSideStep(false);
}
this.eating = false;
this.setState(State.STOPPED, "\u5df2\u624b\u52a8\u505c\u6b62");
this.log("\u5df2\u624b\u52a8\u505c\u6b62");
@@ -273,6 +297,11 @@ implements SubtitleChatMonitor.Listener, BaritoneBridge.PathListener {
return;
}
this.tickAutoSwim(client);
// 处于"往边上走两步"阶段:每帧驱动手动横向行走,走开后自动重试原目标
if (this.sideStepping) {
this.tickSideStep();
return;
}
switch (this.state.ordinal()) {
case 1: {
this.plan();
@@ -298,6 +327,10 @@ implements SubtitleChatMonitor.Listener, BaritoneBridge.PathListener {
this.tickNext();
break;
}
case 7: {
this.tickReturning();
break;
}
}
}
@@ -336,7 +369,12 @@ implements SubtitleChatMonitor.Listener, BaritoneBridge.PathListener {
/** 让 Baritone 寻路前往当前目标区块,并进入 PATHING 状态 */
private void startPathing() {
// 寻路前先校验目标区块是否仍与己方领土接壤(地图异步刷新后可能已不接壤),不接壤则直接跳过
if (this.skipIfTargetNotAdjacent()) {
return;
}
this.retryCount = 0;
this.sideStepCount = 0;
this.stageStartedMillis = System.currentTimeMillis();
this.lastProgress = 0;
this.baritoneBridge.pathTo(this.target);
@@ -346,7 +384,7 @@ implements SubtitleChatMonitor.Listener, BaritoneBridge.PathListener {
/**
* 寻路阶段每帧逻辑:到达目标区块后进入等待开垦状态;
* 若寻路超时则重新寻路(而非回城),超过重试次数则跳过该目标。
* 若寻路超时则重新寻路;重试耗尽先"往边上走两步"再重试,仍失败则回城(连续超时达阈值)或跳过该目标。
*/
private void tickPathing() {
if (this.target == null) {
@@ -364,8 +402,12 @@ implements SubtitleChatMonitor.Listener, BaritoneBridge.PathListener {
this.pathCalcFailed = false;
++this.retryCount;
if (this.retryCount >= this.config.retries) {
this.log("\u5bfb\u8def\u5931\u8d25\uff08\u76ee\u6807\u4e0d\u53ef\u8fbe\uff09\uff0c\u91cd\u8bd5\u8017\u5c3d\uff0c\u8df3\u8fc7 " + this.target.x() + "," + this.target.z());
this.setState(State.FAIL, "\u5bfb\u8def\u5931\u8d25");
// 重试耗尽:不直接切换目标/回城,先"往边上走两步"再重试原目标;可反复多轮,达上限才交给回城/切换
if (this.sideStepCount < this.config.sideStepMaxRounds) {
this.startSideStep();
} else {
this.giveUpTarget("\u5bfb\u8def\u5931\u8d25\uff08\u76ee\u6807\u4e0d\u53ef\u8fbe\uff09");
}
} else {
this.log("\u5bfb\u8def\u5931\u8d25\uff08\u76ee\u6807\u4e0d\u53ef\u8fbe\uff09\uff0c\u91cd\u65b0\u5bfb\u8def " + this.retryCount + "/" + this.config.retries);
this.dataService.refreshAsync();
@@ -379,6 +421,8 @@ implements SubtitleChatMonitor.Listener, BaritoneBridge.PathListener {
}
if (this.playerChunk.equals(this.target)) {
this.baritoneBridge.cancel();
// 成功到达目标:寻路不再卡死,清零连续超时计数
this.consecutiveTimeoutMillis = 0L;
this.stageStartedMillis = System.currentTimeMillis();
this.setState(State.WAITING, "\u5df2\u5230\u8fbe\uff0c\u7b49\u5f85\u9886\u5730\u5f00\u62d3");
this.log("\u5df2\u5230\u8fbe\u76ee\u6807 " + this.target.x() + "," + this.target.z() + "\uff0c\u5f00\u59cb\u7b49\u5f85\u5f00\u57a6");
@@ -389,18 +433,147 @@ implements SubtitleChatMonitor.Listener, BaritoneBridge.PathListener {
++this.retryCount;
// 主动刷新地图,尽快同步最新领土状态
this.dataService.refreshAsync();
if (this.retryCount >= this.config.retries) {
// 重试耗尽:跳过当前目标(不再回城),交给下一个目标
this.log("\u5bfb\u8def\u8d85\u65f6\uff0c\u8df3\u8fc7 " + this.target.x() + "," + this.target.z());
this.setState(State.FAIL, "\u5bfb\u8def\u8d85\u65f6");
} else {
// 重新寻路(而非回城):取消旧路径并重新计算,往往“在边上走走”就能走出困境
// 累计"连续寻路超时"时长:连续多次寻路都超时说明玩家被地形/缝隙卡死走不到目标,
// 达到阈值后作为发送 /u return 回城(约 10 秒后传送到可寻路位置)的最后手段,平时不打断重试/走两步。
this.consecutiveTimeoutMillis += (long)this.config.waitTimeoutSeconds * 1000L;
if (this.retryCount < this.config.retries) {
// 未耗尽:重新寻路(而非回城),取消旧路径并重新计算,往往"在边上走走"就能走出困境
this.log("\u5bfb\u8def\u8d85\u65f6\uff0c\u91cd\u65b0\u5bfb\u8def " + this.retryCount + "/" + this.config.retries);
this.retrySameTarget("\u5bfb\u8def\u8d85\u65f6\uff0c\u91cd\u65b0\u5bfb\u8def");
} else if (this.sideStepCount < this.config.sideStepMaxRounds) {
// 重试耗尽:先不切换目标/回城,用"往边上走两步"再重试一次(走开障碍后重新寻路原目标);可反复多轮
this.startSideStep();
} else {
// 多轮走两步也未能脱困:连续超时达到阈值则回城传送重新开始,否则切换目标
this.giveUpTarget("\u5bfb\u8def\u8d85\u65f6");
}
}
}
/**
* 连续寻路超时达到阈值时触发回城:
* 取消当前寻路,向服务器发送 /u return 指令(约 10 秒后自动传送回可寻路的安全位置),
* 随后进入 RETURNING 状态等待传送完成,传送后由 {@link #tickReturning()} 重新规划并从头开始。
*/
private void sendReturnCommand() {
this.baritoneBridge.cancel();
MinecraftClient client = MinecraftClient.getInstance();
if (client != null && client.player != null) {
this.log("\u8fde\u7eed\u5bfb\u8def\u8d85\u65f6\u8d85\u8fc7 " + this.config.returnThresholdSeconds + " \u79d2\uff0c\u53d1\u9001 /u return \u56de\u57ce\uff0c10 \u79d2\u540e\u81ea\u52a8\u4f20\u9001");
// 通过玩家网络处理器发送聊天指令 /u return(Yarn 映射:sendChatCommand 自动带上前缀斜杠)
client.player.networkHandler.sendChatCommand("u return");
client.player.sendMessage(Text.literal("[AutoStore] \u8fde\u7eed\u5bfb\u8def\u8d85\u65f6\uff0c\u5df2\u53d1\u9001 /u return\uff0c\u7b49\u5f85\u4f20\u9001\u540e\u91cd\u65b0\u6269\u5730"), false);
} else {
this.log("\u8fde\u7eed\u5bfb\u8def\u8d85\u65f6\uff0c\u4f46\u73a9\u5bb6\u4e0d\u5728\u4e16\u754c\uff0c\u8df3\u8fc7");
}
this.returnStartMillis = System.currentTimeMillis();
this.setState(State.RETURNING, "\u7b49\u5f85\u56de\u57ce\u4f20\u9001\uff0810 \u79d2\u540e\u81ea\u52a8\u4f20\u9001\uff09");
}
/**
* 回城传送等待阶段每帧逻辑:
* 发送 /u return 后服务器约 10 秒把玩家传送回安全位置。等待 {@code returnWaitSeconds}
* 秒(覆盖传送延迟并留余量)后,清零连续超时计数并重新规划扩地计划,
* 从最高优先级区块(队首)重新开始寻路。
*/
private void tickReturning() {
long elapsedMillis = System.currentTimeMillis() - this.returnStartMillis;
if (elapsedMillis < (long)this.config.returnWaitSeconds * 1000L) {
return;
}
this.consecutiveTimeoutMillis = 0L;
this.log("\u56de\u57ce\u4f20\u9001\u5b8c\u6210\uff0c\u91cd\u65b0\u89c4\u5212\u5e76\u4ece\u6700\u9ad8\u4f18\u5148\u7ea7\u533a\u5757\u5f00\u59cb");
// 重新规划:清空队列重新生成计划,从队首(最高优先级)区块开始寻路
this.plan();
}
/**
* 目标寻路重试耗尽时的脱困策略入口:
* 取消当前寻路,改为手动朝"目标连线垂直方向"走开约 2.5 格(左右各试一侧),
* 走开后自动重新寻路原目标;走不开/超时则再走两步或交给 {@link #giveUpTarget} 回城/切换目标。
* 每个目标最多触发 {@code sideStepMaxRounds} 轮(sideStepCount 在 startPathing 时重置)。
*/
private void startSideStep() {
this.baritoneBridge.cancel();
MinecraftClient client = MinecraftClient.getInstance();
this.sideStepping = true;
++this.sideStepCount;
this.sideStepSign = 1; // 默认先朝目标连线左侧走
this.sideStepStartPos = client != null && client.player != null ? client.player.getBlockPos() : null;
this.sideStepDeadlineMillis = System.currentTimeMillis() + 10000L; // 整体最多走 10 秒
this.sideStepSinceMillis = System.currentTimeMillis();
this.log("\u5bfb\u8def\u91cd\u8bd5\u8017\u5c3d\uff0c\u5f00\u59cb\u5f80\u8fb9\u4e0a\u8d70\u4e24\u6b65\u518d\u91cd\u8bd5\u539f\u76ee\u6807\uff08\u7b2c " + this.sideStepCount + "/" + this.config.sideStepMaxRounds + " \u8f6e\uff09");
this.setState(State.PATHING, "\u5f80\u8fb9\u4e0a\u8d70\u4e24\u6b65...");
}
/** 走两步阶段每帧逻辑:朝目标连线垂直方向按住前进键行走,走开约 2.5 格后重试原目标 */
private void tickSideStep() {
MinecraftClient client = MinecraftClient.getInstance();
if (client == null || client.player == null || this.sideStepStartPos == null || this.target == null) {
this.stopSideStep(false);
return;
}
// 整体超时:往边上走两步也未能脱困
if (System.currentTimeMillis() >= this.sideStepDeadlineMillis) {
this.stopSideStep(false);
this.giveUpTarget("\u5f80\u8fb9\u4e0a\u8d70\u4e24\u6b65\u8d85\u65f6");
return;
}
// 目标连线方向(水平),取垂直向量作为横向移动方向(左为 +,右为 -)
Vec3d playerPos = client.player.getPos();
Vec3d dirToTarget = new Vec3d(this.target.centerBlockX() + 0.5, playerPos.y, this.target.centerBlockZ() + 0.5)
.subtract(playerPos).normalize();
Vec3d sideDir = new Vec3d(-dirToTarget.z, 0.0, dirToTarget.x).multiply(this.sideStepSign);
// 面朝横向移动方向并按住前进键
float yaw = (float)Math.toDegrees(Math.atan2(sideDir.z, sideDir.x)) - 90.0f;
client.player.setYaw(yaw);
client.options.forwardKey.setPressed(true);
// 已走开足够距离(约 2.5 格):停止并重试原目标
if (client.player.getBlockPos().getSquaredDistance(this.sideStepStartPos) >= 6.25) {
this.stopSideStep(true);
this.retrySameTarget("\u5f80\u8fb9\u4e0a\u8d70\u4e24\u6b65\u5b8c\u6210\uff0c\u91cd\u65b0\u5bfb\u8def");
return;
}
// 卡住检查:同一方向 5 秒没移动就换另一侧;两侧都试过仍不动则放弃
if (System.currentTimeMillis() - this.sideStepSinceMillis > 5000L) {
if (this.sideStepSign != -1) {
this.sideStepSign = -1;
this.sideStepSinceMillis = System.currentTimeMillis();
this.log("\u5f80\u8fb9\u4e0a\u8d70\u88ab\u6321\uff0c\u53d6\u53cd\u65b9\u5411\u7ee7\u7eed");
} else {
this.stopSideStep(false);
this.giveUpTarget("\u5f80\u8fb9\u4e0a\u8d70\u4e24\u4fa7\u5747\u88ab\u6321");
}
}
}
/** 结束走两步阶段:释放手动按下的前进键;success 表示已走开并准备重试原目标 */
private void stopSideStep(boolean success) {
this.sideStepping = false;
MinecraftClient client = MinecraftClient.getInstance();
if (client != null && client.options != null) {
client.options.forwardKey.setPressed(false);
}
}
/**
* 放弃当前目标(重试与走两步均已尝试)的统一出口:
* 若连续寻路超时累计已达到阈值,则发送 /u return 回城传送并重新规划;
* 否则记录日志并进入 FAIL 状态切换下一个目标。
*/
private void giveUpTarget(String reason) {
if (this.consecutiveTimeoutMillis >= (long)this.config.returnThresholdSeconds * 1000L) {
this.sendReturnCommand();
return;
}
if (this.target != null) {
this.log(reason + "\uff0c\u8df3\u8fc7 " + this.target.x() + "," + this.target.z());
} else {
this.log(reason);
}
this.setState(State.FAIL, reason);
}
/** 等待开垦阶段每帧逻辑:玩家离开目标区块则重新寻路;等待超时则重试或跳过当前目标 */
private void tickWaiting() {
if (this.target == null) {
@@ -437,10 +610,13 @@ implements SubtitleChatMonitor.Listener, BaritoneBridge.PathListener {
/** 重试当前目标:取消旧寻路,重新让 Baritone 前往同一目标区块 */
private void retrySameTarget(String statusText) {
// 目标已被他人占领(地图刷新后发现):不再重试,直接跳过,避免玩家被困在已占领区块
// 重试前先校验目标有效性:已被他人占领或已不接壤时不再重试,直接跳过,避免玩家被困在无效区块
if (this.skipIfTargetClaimed("\u91cd\u8bd5\u65f6\u53d1\u73b0\u76ee\u6807\u5df2\u88ab\u5360\u9886")) {
return;
}
if (this.skipIfTargetNotAdjacent()) {
return;
}
this.baritoneBridge.cancel();
this.stageStartedMillis = System.currentTimeMillis();
this.baritoneBridge.pathTo(this.target);
@@ -622,6 +798,8 @@ implements SubtitleChatMonitor.Listener, BaritoneBridge.PathListener {
// 缩短"会话占领等待确认"窗口,避免假占领长时间残留
this.dataService.refreshAsync();
++this.completedClaims;
// 开垦成功:寻路/等待恢复正常,清零连续超时计数
this.consecutiveTimeoutMillis = 0L;
this.target = chunk;
this.lastProgress = 100;
this.log("\u5360\u9886\u6210\u529f " + chunk.x() + "," + chunk.z() + "\uff08\u533a\u5757 " + chunk.x() + "," + chunk.z() + " / \u4e16\u754c " + blockX + "," + blockZ + "\uff09");
@@ -652,6 +830,8 @@ implements SubtitleChatMonitor.Listener, BaritoneBridge.PathListener {
// 占领成功后立即触发一次地图刷新,尽快确认该区块
this.dataService.refreshAsync();
++this.completedClaims;
// 开垦成功:寻路/等待恢复正常,清零连续超时计数
this.consecutiveTimeoutMillis = 0L;
this.lastProgress = 100;
this.log("\u5360\u9886\u6210\u529f " + this.target.x() + "," + this.target.z());
this.setState(State.SUCCESS, "\u7586\u571f\u5df2\u5f00\u62d3 " + this.target.x() + "," + this.target.z());
@@ -748,10 +928,10 @@ implements SubtitleChatMonitor.Listener, BaritoneBridge.PathListener {
return -1;
}
/** 进食完成后恢复任务:若仍在寻路阶段则重新发起寻路 */
/** 进食完成后恢复任务:若仍在寻路阶段则重新发起寻路(走两步阶段不抢路) */
private void resumeAfterEating() {
this.eating = false;
if (this.state == State.PATHING && this.target != null) {
if (this.state == State.PATHING && this.target != null && !this.sideStepping) {
this.stageStartedMillis = System.currentTimeMillis();
this.baritoneBridge.pathTo(this.target);
}
@@ -826,6 +1006,7 @@ implements SubtitleChatMonitor.Listener, BaritoneBridge.PathListener {
SUCCESS,
FAIL,
NEXT,
RETURNING,
COMPLETE,
STOPPED;
+1 -1
查看文件
@@ -1 +1 @@
{"schemaVersion":1,"id":"autostore","version":"1.2.3","name":"AutoStore","description":"","authors":[],"contact":{},"license":"All-Rights-Reserved","environment":"client","entrypoints":{"fabric-datagen":["autostore.client.AutoStoreModDataGenerator"],"client":["autostore.client.AutoStoreModClient"],"main":["autostore.AutoStoreMod"]},"mixins":["autostore.mixins.json",{"config":"autostore.client.mixins.json","environment":"client"}],"depends":{"fabricloader":">=0.16.14","fabric-api":"*","minecraft":"1.21.4"},"jars":[{"file":"META-INF/jars/baritone-api-fabric-1.13.1.jar"}]}
{"schemaVersion":1,"id":"autostore","version":"1.2.5","name":"AutoStore","description":"","authors":[],"contact":{},"license":"All-Rights-Reserved","environment":"client","entrypoints":{"fabric-datagen":["autostore.client.AutoStoreModDataGenerator"],"client":["autostore.client.AutoStoreModClient"],"main":["autostore.AutoStoreMod"]},"mixins":["autostore.mixins.json",{"config":"autostore.client.mixins.json","environment":"client"}],"depends":{"fabricloader":">=0.16.14","fabric-api":"*","minecraft":"1.21.4"},"jars":[{"file":"META-INF/jars/baritone-api-fabric-1.13.1.jar"}]}