比较提交
| 作者 | SHA1 | 提交日期 | |
|---|---|---|---|
|
|
a0963747b6 | ||
|
|
08e15ffc9b | ||
|
|
a9c71861f5 | ||
|
|
5193b2e379 | ||
|
|
2309f01291 | ||
|
|
3cddee2abb | ||
|
|
28fe848fb6 | ||
|
|
340fb2ddf1 | ||
|
|
fc14e51894 |
@@ -8,6 +8,7 @@
|
|||||||
- 四种扩地策略:方形外扩、区域扩地、玩家周围、绕国一圈
|
- 四种扩地策略:方形外扩、区域扩地、玩家周围、绕国一圈
|
||||||
- 区域扩地采用 S 形扫描:从与己方领土接壤的区块开始,横向/纵向 S 形扫过整个矩形,不沿邦国边界,但保证每个新占领区块都与**己方领土**接壤(仅四个正方向,不把其他邦国领土计入接壤)
|
- 区域扩地采用 S 形扫描:从与己方领土接壤的区块开始,横向/纵向 S 形扫过整个矩形,不沿邦国边界,但保证每个新占领区块都与**己方领土**接壤(仅四个正方向,不把其他邦国领土计入接壤)
|
||||||
- 自动寻路(Baritone)前往目标区块;监听 Baritone 路径事件,目标不可达(CALC_FAILED,如被悬崖/高山/水体隔断)时立即跳过;寻路超时则重新寻路(不立即回城),重试耗尽才跳过当前目标
|
- 自动寻路(Baritone)前往目标区块;监听 Baritone 路径事件,目标不可达(CALC_FAILED,如被悬崖/高山/水体隔断)时立即跳过;寻路超时则重新寻路(不立即回城),重试耗尽才跳过当前目标
|
||||||
|
- 连续寻路超时回城:连续寻路超时累计超过 1 分钟(可配置)时自动发送 `/u return`,约 10 秒后传送到可寻路位置,随后重新规划并从最高优先级区块重新开始扩地
|
||||||
- 运行时接壤校验:寻路/等待阶段每帧校验目标是否仍与己方领土接壤,地图刷新导致边界变化时立即跳过
|
- 运行时接壤校验:寻路/等待阶段每帧校验目标是否仍与己方领土接壤,地图刷新导致边界变化时立即跳过
|
||||||
- 目标有效性校验:目标若已被其他邦国占领则立即跳过
|
- 目标有效性校验:目标若已被其他邦国占领则立即跳过
|
||||||
- 现实时间限制:仅 11:00-22:00(本地现实时间)允许开拓,其余时段启动会被拒绝、运行中到点自动停止
|
- 现实时间限制:仅 11:00-22:00(本地现实时间)允许开拓,其余时段启动会被拒绝、运行中到点自动停止
|
||||||
@@ -24,7 +25,7 @@
|
|||||||
## 安装
|
## 安装
|
||||||
|
|
||||||
1. 安装 Fabric Loader 0.16.14+ 和 Fabric API
|
1. 安装 Fabric Loader 0.16.14+ 和 Fabric API
|
||||||
2. 将 `autostore-1.2.1.jar` 放入 `.minecraft/mods/` 目录
|
2. 将 `autostore-1.2.4.jar` 放入 `.minecraft/mods/` 目录
|
||||||
3. 启动游戏,按 `Ctrl+R` 打开 GUI
|
3. 启动游戏,按 `Ctrl+R` 打开 GUI
|
||||||
|
|
||||||
## 构建
|
## 构建
|
||||||
@@ -34,7 +35,7 @@
|
|||||||
./build_mod.sh
|
./build_mod.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
输出文件:`autostore-1.2.1.jar`
|
输出文件:`autostore-1.2.4.jar`
|
||||||
|
|
||||||
## 赞助支持
|
## 赞助支持
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -7,7 +7,7 @@ yarn_mappings=1.21.4+build.8
|
|||||||
loader_version=0.19.3
|
loader_version=0.19.3
|
||||||
|
|
||||||
# Mod Properties
|
# Mod Properties
|
||||||
mod_version=1.2.1
|
mod_version=1.2.4
|
||||||
maven_group=autostore
|
maven_group=autostore
|
||||||
archives_base_name=autostore
|
archives_base_name=autostore
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import baritone.api.event.events.PathEvent;
|
|||||||
import baritone.api.event.listener.AbstractGameEventListener;
|
import baritone.api.event.listener.AbstractGameEventListener;
|
||||||
import autostore.client.config.ExpansionConfig;
|
import autostore.client.config.ExpansionConfig;
|
||||||
import autostore.client.territory.ChunkPos;
|
import autostore.client.territory.ChunkPos;
|
||||||
|
import baritone.api.pathing.goals.GoalXZ;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import net.fabricmc.api.EnvType;
|
import net.fabricmc.api.EnvType;
|
||||||
@@ -55,12 +56,27 @@ public final class BaritoneBridge {
|
|||||||
settings.allowSprint.value = config.allowSprint;
|
settings.allowSprint.value = config.allowSprint;
|
||||||
settings.allowBreak.value = config.allowBreak;
|
settings.allowBreak.value = config.allowBreak;
|
||||||
settings.allowPlace.value = config.allowPlace;
|
settings.allowPlace.value = config.allowPlace;
|
||||||
settings.allowParkour.value = config.allowJump;
|
// 跳跃(跑酷)前进:开启放置时也允许跳跃搭路,避免过河/过缝时必须纯步行
|
||||||
|
settings.allowParkour.value = config.allowJump || config.allowPlace;
|
||||||
|
// 关键修复:开启放置时,配套允许“跑酷搭路放方块”。
|
||||||
|
// 若仅开 allowPlace 而不给 allowParkourPlace,Baritone 在边缘会把
|
||||||
|
// “下蹲放方块”与“跳跃过去”两条腿计划打架:想放又判定不该放,
|
||||||
|
// 于是原地反复下蹲+跳跃却始终放不出方块(用户观察到的“原地抽搐”)。
|
||||||
|
settings.allowParkourPlace.value = config.allowPlace;
|
||||||
|
// 降低放方块的路径代价:Baritone 默认对搭路惩罚较高,会倾向犹豫/绕行;
|
||||||
|
// 归零后它更愿意稳定搭路,而不是在缺口边反复空跳。
|
||||||
|
settings.blockPlacementPenalty.value = 0.0;
|
||||||
settings.avoidance.value = config.avoidMobs;
|
settings.avoidance.value = config.avoidMobs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 让 Baritone 寻路前往指定区块(以“到达区块内”为完成条件)。
|
* 让 Baritone 寻路前往指定区块中心。
|
||||||
|
*
|
||||||
|
* <p>使用 Baritone <b>原生</b>{@link GoalXZ}(目标区块中心单格):原生 GoalXZ 对
|
||||||
|
* 可达地形几乎不会误判“目标不可达”,是最好的默认选择。早期用自定义
|
||||||
|
* {@code ChunkGoal}(曾加入高度硬约束)疑似是“平原上一按 W 就能走、Baritone 却秒
|
||||||
|
* CALC_FAILED”的根因——自定义 Goal 在某些站位/边界下会把可达目标判成不可达。
|
||||||
|
* 到达判定仍由控制器 {@code playerChunk.equals(target)} 保证,与 Goal 自身容差无关。</p>
|
||||||
*
|
*
|
||||||
* @param chunk 目标区块
|
* @param chunk 目标区块
|
||||||
*/
|
*/
|
||||||
@@ -69,11 +85,7 @@ public final class BaritoneBridge {
|
|||||||
if (baritone == null) {
|
if (baritone == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 使用自定义 ChunkGoal:完成条件为“玩家脚下方块落在目标区块内”,
|
baritone.getCustomGoalProcess().setGoalAndPath(new GoalXZ(chunk.centerBlockX(), chunk.centerBlockZ()));
|
||||||
// 与控制器 playerChunk.equals(target) 的判定一致。避免 Baritone 自带
|
|
||||||
// GoalXZ(容差 0,精确到区块中心单格)在中心不可达时停在相邻区块,
|
|
||||||
// 造成目标区块错位与反复寻路。
|
|
||||||
baritone.getCustomGoalProcess().setGoalAndPath(new ChunkGoal(chunk));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 强制取消当前寻路任务。 */
|
/** 强制取消当前寻路任务。 */
|
||||||
|
|||||||
@@ -1,52 +0,0 @@
|
|||||||
/*
|
|
||||||
* 本文件为 AutoStore 模组自定义的 Baritone 寻路目标。
|
|
||||||
*/
|
|
||||||
package autostore.client.bridge;
|
|
||||||
|
|
||||||
import autostore.client.territory.ChunkPos;
|
|
||||||
import baritone.api.pathing.goals.Goal;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 以“到达目标区块内”为完成条件的寻路目标。
|
|
||||||
*
|
|
||||||
* <p>修复“目标区块错位”问题:Baritone 自带的 {@code GoalXZ} 容差为 0,
|
|
||||||
* 会要求玩家精确站在区块中心的单个方块上(如 {@code x*16+8, z*16+8})。
|
|
||||||
* 一旦该方块不可达(被树、石头、水、建筑等占据),玩家会被停在区块边缘甚至
|
|
||||||
* 相邻区块,导致玩家所在区块与目标区块错位、反复寻路。</p>
|
|
||||||
*
|
|
||||||
* <p>本目标把完成条件放宽为“玩家脚下方块落在目标区块内”,与控制器中
|
|
||||||
* {@code playerChunk.equals(target)} 的判定完全一致,从而彻底消除
|
|
||||||
* 中心点不可达带来的错位。</p>
|
|
||||||
*/
|
|
||||||
public final class ChunkGoal implements Goal {
|
|
||||||
|
|
||||||
/** 目标区块 X 坐标 */
|
|
||||||
private final int targetX;
|
|
||||||
/** 目标区块 Z 坐标 */
|
|
||||||
private final int targetZ;
|
|
||||||
/** 目标区块中心的方块 X 坐标(用于启发式距离估算) */
|
|
||||||
private final int centerX;
|
|
||||||
/** 目标区块中心的方块 Z 坐标(用于启发式距离估算) */
|
|
||||||
private final int centerZ;
|
|
||||||
|
|
||||||
public ChunkGoal(ChunkPos chunk) {
|
|
||||||
this.targetX = chunk.x();
|
|
||||||
this.targetZ = chunk.z();
|
|
||||||
this.centerX = chunk.centerBlockX();
|
|
||||||
this.centerZ = chunk.centerBlockZ();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 判断玩家脚下方块是否落在目标区块内 */
|
|
||||||
@Override
|
|
||||||
public boolean isInGoal(int x, int y, int z) {
|
|
||||||
return Math.floorDiv(x, 16) == this.targetX && Math.floorDiv(z, 16) == this.targetZ;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 启发式:到目标区块中心的水平欧几里得距离(可采纳,保证寻路效率) */
|
|
||||||
@Override
|
|
||||||
public double heuristic(int x, int y, int z) {
|
|
||||||
double dx = x - this.centerX;
|
|
||||||
double dz = z - this.centerZ;
|
|
||||||
return Math.sqrt(dx * dx + dz * dz);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -31,6 +31,12 @@ public class ExpansionConfig {
|
|||||||
/** 超时后的最大重试次数 */
|
/** 超时后的最大重试次数 */
|
||||||
public int retries = 2;
|
public int retries = 2;
|
||||||
|
|
||||||
|
/** 连续寻路超时累计达到该秒数后,发送 /u return 回城传送并重新开始(默认 60 秒) */
|
||||||
|
public int returnThresholdSeconds = 60;
|
||||||
|
|
||||||
|
/** 发送 /u return 后等待传送完成的时间(秒);默认 12 秒,覆盖 10 秒传送延迟并留余量 */
|
||||||
|
public int returnWaitSeconds = 12;
|
||||||
|
|
||||||
/** 区域扩地策略的中心 X 坐标(区块) */
|
/** 区域扩地策略的中心 X 坐标(区块) */
|
||||||
public int regionCenterX;
|
public int regionCenterX;
|
||||||
|
|
||||||
@@ -94,6 +100,8 @@ public class ExpansionConfig {
|
|||||||
c.targetCount = this.targetCount;
|
c.targetCount = this.targetCount;
|
||||||
c.waitTimeoutSeconds = this.waitTimeoutSeconds;
|
c.waitTimeoutSeconds = this.waitTimeoutSeconds;
|
||||||
c.retries = this.retries;
|
c.retries = this.retries;
|
||||||
|
c.returnThresholdSeconds = this.returnThresholdSeconds;
|
||||||
|
c.returnWaitSeconds = this.returnWaitSeconds;
|
||||||
c.regionCenterX = this.regionCenterX;
|
c.regionCenterX = this.regionCenterX;
|
||||||
c.regionCenterZ = this.regionCenterZ;
|
c.regionCenterZ = this.regionCenterZ;
|
||||||
c.regionRadius = this.regionRadius;
|
c.regionRadius = this.regionRadius;
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import net.minecraft.entity.player.PlayerEntity;
|
|||||||
import net.minecraft.entity.player.PlayerInventory;
|
import net.minecraft.entity.player.PlayerInventory;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.util.math.Vec3d;
|
||||||
import net.minecraft.client.MinecraftClient;
|
import net.minecraft.client.MinecraftClient;
|
||||||
import net.minecraft.component.DataComponentTypes;
|
import net.minecraft.component.DataComponentTypes;
|
||||||
import net.minecraft.text.Text;
|
import net.minecraft.text.Text;
|
||||||
@@ -93,6 +94,24 @@ implements SubtitleChatMonitor.Listener, BaritoneBridge.PathListener {
|
|||||||
private boolean eating;
|
private boolean eating;
|
||||||
/** Baritone 最近一次路径计算是否失败(目标不可达);由事件监听设置,寻路阶段消费 */
|
/** Baritone 最近一次路径计算是否失败(目标不可达);由事件监听设置,寻路阶段消费 */
|
||||||
private volatile boolean pathCalcFailed;
|
private volatile boolean pathCalcFailed;
|
||||||
|
/** 等待开拓时段自动开始:不在 11:00-22:00 时点击开始会暂存配置,进入时段后自动启动(null 表示无等待任务) */
|
||||||
|
private ExpansionConfig pendingStart;
|
||||||
|
/** 连续寻路超时累计时长(毫秒):多次寻路超时逐次累加,达到阈值触发 /u return 回城传送 */
|
||||||
|
private long consecutiveTimeoutMillis;
|
||||||
|
/** 发送 /u return 回城指令的时间戳(RETURNING 状态使用,用于等待传送完成) */
|
||||||
|
private long returnStartMillis;
|
||||||
|
/** 当前目标是否已使用过"往边上走两步"策略(重试耗尽时先用它,避免重复;再次耗尽才切换目标) */
|
||||||
|
private boolean sideStepAttempted;
|
||||||
|
/** 是否正在"往边上走两步"阶段(手动向目标连线垂直方向走开约 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) {
|
public ExpansionController(TerritoryDataService dataService) {
|
||||||
this.dataService = dataService;
|
this.dataService = dataService;
|
||||||
@@ -140,6 +159,11 @@ implements SubtitleChatMonitor.Listener, BaritoneBridge.PathListener {
|
|||||||
return this.state != State.IDLE && this.state != State.COMPLETE && this.state != State.STOPPED;
|
return this.state != State.IDLE && this.state != State.COMPLETE && this.state != State.STOPPED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @return 是否处于"等待开拓时段自动开始"状态 */
|
||||||
|
public boolean isWaitingForClaimTime() {
|
||||||
|
return this.pendingStart != null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 判断当前现实时间(本地时区)是否在允许开拓的时间段内。
|
* 判断当前现实时间(本地时区)是否在允许开拓的时间段内。
|
||||||
* 仅 11:00(含)到 22:00(不含)允许开拓,其余时段服务器会拒绝开拓。
|
* 仅 11:00(含)到 22:00(不含)允许开拓,其余时段服务器会拒绝开拓。
|
||||||
@@ -171,14 +195,15 @@ implements SubtitleChatMonitor.Listener, BaritoneBridge.PathListener {
|
|||||||
this.status = "Baritone \u5c1a\u672a\u521d\u59cb\u5316\uff0c\u8bf7\u7a0d\u540e\u518d\u8bd5";
|
this.status = "Baritone \u5c1a\u672a\u521d\u59cb\u5316\uff0c\u8bf7\u7a0d\u540e\u518d\u8bd5";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// 现实时间限制:仅 11:00-22:00 允许开拓,其余时段服务器会拒绝开拓(等待超时)
|
// 现实时间限制:仅 11:00-22:00 允许开拓,其余时段服务器会拒绝开拓(等待超时)。
|
||||||
|
// 不在时段时不再直接拒绝,而是登记"等待自动开始":进入时段后由 tick() 自动启动。
|
||||||
if (!ExpansionController.isWithinClaimTime()) {
|
if (!ExpansionController.isWithinClaimTime()) {
|
||||||
this.status = "\u4e0d\u5728\u5f00\u62d3\u65f6\u9650\u5185\uff0811:00-22:00\uff09\uff0c\u65e0\u6cd5\u5f00\u62d3";
|
this.pendingStart = requestedConfig.copy();
|
||||||
this.setState(State.IDLE, this.status);
|
this.setState(State.IDLE, "\u7b49\u5f85\u5f00\u62d3\u65f6\u6bb5\uff0c11:00 \u81ea\u52a8\u5f00\u59cb");
|
||||||
this.log("\u542f\u52a8\u5931\u8d25\uff1a\u4e0d\u5728\u5f00\u62d3\u65f6\u9650\u5185\uff0c\u5f53\u524d\u73b0\u5b9e\u65f6\u95f4 " + LocalTime.now().format(LOG_TIME_FORMAT));
|
this.log("\u4e0d\u5728\u5f00\u62d3\u65f6\u6bb5\u5185\uff0811:00-22:00\uff09\uff0c\u5df2\u767b\u8bb0\u81ea\u52a8\u5f00\u59cb\uff0c\u5f53\u524d\u73b0\u5b9e\u65f6\u95f4 " + LocalTime.now().format(LOG_TIME_FORMAT));
|
||||||
// 给玩家发送聊天栏提示,避免玩家未打开 GUI 时看不到原因
|
// 给玩家发送聊天栏提示,避免玩家未打开 GUI 时看不到原因
|
||||||
client.player.sendMessage(Text.literal("[AutoStore] \u4e0d\u5728\u5f00\u62d3\u65f6\u9650\u5185\uff0811:00-22:00\uff09\uff0c\u65e0\u6cd5\u5f00\u62d3"), false);
|
client.player.sendMessage(Text.literal("[AutoStore] \u4e0d\u5728\u5f00\u62d3\u65f6\u6bb5\u5185\uff0811:00-22:00\uff09\uff0c\u5df2\u767b\u8bb0\uff1a\u8fdb\u5165\u65f6\u6bb5\u540e\u81ea\u52a8\u5f00\u59cb"), false);
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
this.config = requestedConfig.copy();
|
this.config = requestedConfig.copy();
|
||||||
this.model = this.dataService.model();
|
this.model = this.dataService.model();
|
||||||
@@ -203,6 +228,9 @@ implements SubtitleChatMonitor.Listener, BaritoneBridge.PathListener {
|
|||||||
}
|
}
|
||||||
this.completedClaims = 0;
|
this.completedClaims = 0;
|
||||||
this.retryCount = 0;
|
this.retryCount = 0;
|
||||||
|
this.consecutiveTimeoutMillis = 0L;
|
||||||
|
this.sideStepAttempted = false;
|
||||||
|
this.sideStepping = false;
|
||||||
this.queue.clear();
|
this.queue.clear();
|
||||||
this.claimedSet.clear();
|
this.claimedSet.clear();
|
||||||
this.target = null;
|
this.target = null;
|
||||||
@@ -214,12 +242,22 @@ implements SubtitleChatMonitor.Listener, BaritoneBridge.PathListener {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 手动停止任务:取消 Baritone 寻路并进入 STOPPED 状态 */
|
/** 手动停止任务:取消 Baritone 寻路并进入 STOPPED 状态;若处于等待自动开始,则取消等待 */
|
||||||
public void stop() {
|
public void stop() {
|
||||||
|
if (this.pendingStart != null) {
|
||||||
|
this.pendingStart = null;
|
||||||
|
this.setState(State.IDLE, "\u5df2\u53d6\u6d88\u81ea\u52a8\u5f00\u59cb");
|
||||||
|
this.log("\u5df2\u53d6\u6d88\u7b49\u5f85\u81ea\u52a8\u5f00\u59cb");
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (this.state == State.IDLE || this.state == State.COMPLETE || this.state == State.STOPPED) {
|
if (this.state == State.IDLE || this.state == State.COMPLETE || this.state == State.STOPPED) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.baritoneBridge.cancel();
|
this.baritoneBridge.cancel();
|
||||||
|
// 若正处于"往边上走两步"阶段,先释放手动按下的前进键
|
||||||
|
if (this.sideStepping) {
|
||||||
|
this.stopSideStep(false);
|
||||||
|
}
|
||||||
this.eating = false;
|
this.eating = false;
|
||||||
this.setState(State.STOPPED, "\u5df2\u624b\u52a8\u505c\u6b62");
|
this.setState(State.STOPPED, "\u5df2\u624b\u52a8\u505c\u6b62");
|
||||||
this.log("\u5df2\u624b\u52a8\u505c\u6b62");
|
this.log("\u5df2\u624b\u52a8\u505c\u6b62");
|
||||||
@@ -240,6 +278,13 @@ implements SubtitleChatMonitor.Listener, BaritoneBridge.PathListener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.playerChunk = this.playerChunk(client);
|
this.playerChunk = this.playerChunk(client);
|
||||||
|
// 等待开拓时段自动开始:进入 11:00-22:00 后自动启动已登记的任务
|
||||||
|
if (this.pendingStart != null && ExpansionController.isWithinClaimTime()) {
|
||||||
|
ExpansionConfig pending = this.pendingStart;
|
||||||
|
this.pendingStart = null;
|
||||||
|
this.log("\u5df2\u8fdb\u5165\u5f00\u62d3\u65f6\u6bb5\uff0c\u81ea\u52a8\u5f00\u59cb");
|
||||||
|
this.start(pending);
|
||||||
|
}
|
||||||
// 现实时间限制:运行中超出 11:00-22:00 则自动停止,避免服务器拒绝开拓导致超时
|
// 现实时间限制:运行中超出 11:00-22:00 则自动停止,避免服务器拒绝开拓导致超时
|
||||||
if (this.isRunning() && !ExpansionController.isWithinClaimTime()) {
|
if (this.isRunning() && !ExpansionController.isWithinClaimTime()) {
|
||||||
this.stop();
|
this.stop();
|
||||||
@@ -252,6 +297,11 @@ implements SubtitleChatMonitor.Listener, BaritoneBridge.PathListener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.tickAutoSwim(client);
|
this.tickAutoSwim(client);
|
||||||
|
// 处于"往边上走两步"阶段:每帧驱动手动横向行走,走开后自动重试原目标
|
||||||
|
if (this.sideStepping) {
|
||||||
|
this.tickSideStep();
|
||||||
|
return;
|
||||||
|
}
|
||||||
switch (this.state.ordinal()) {
|
switch (this.state.ordinal()) {
|
||||||
case 1: {
|
case 1: {
|
||||||
this.plan();
|
this.plan();
|
||||||
@@ -277,6 +327,10 @@ implements SubtitleChatMonitor.Listener, BaritoneBridge.PathListener {
|
|||||||
this.tickNext();
|
this.tickNext();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 7: {
|
||||||
|
this.tickReturning();
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,6 +370,7 @@ implements SubtitleChatMonitor.Listener, BaritoneBridge.PathListener {
|
|||||||
/** 让 Baritone 寻路前往当前目标区块,并进入 PATHING 状态 */
|
/** 让 Baritone 寻路前往当前目标区块,并进入 PATHING 状态 */
|
||||||
private void startPathing() {
|
private void startPathing() {
|
||||||
this.retryCount = 0;
|
this.retryCount = 0;
|
||||||
|
this.sideStepAttempted = false;
|
||||||
this.stageStartedMillis = System.currentTimeMillis();
|
this.stageStartedMillis = System.currentTimeMillis();
|
||||||
this.lastProgress = 0;
|
this.lastProgress = 0;
|
||||||
this.baritoneBridge.pathTo(this.target);
|
this.baritoneBridge.pathTo(this.target);
|
||||||
@@ -325,7 +380,7 @@ implements SubtitleChatMonitor.Listener, BaritoneBridge.PathListener {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 寻路阶段每帧逻辑:到达目标区块后进入等待开垦状态;
|
* 寻路阶段每帧逻辑:到达目标区块后进入等待开垦状态;
|
||||||
* 若寻路超时则重新寻路(而非回城),超过重试次数则跳过该目标。
|
* 若寻路超时则重新寻路;重试耗尽先"往边上走两步"再重试,仍失败则回城(连续超时达阈值)或跳过该目标。
|
||||||
*/
|
*/
|
||||||
private void tickPathing() {
|
private void tickPathing() {
|
||||||
if (this.target == null) {
|
if (this.target == null) {
|
||||||
@@ -336,11 +391,24 @@ implements SubtitleChatMonitor.Listener, BaritoneBridge.PathListener {
|
|||||||
if (this.skipIfTargetClaimed("\u76ee\u6807\u5df2\u88ab\u5360\u9886")) {
|
if (this.skipIfTargetClaimed("\u76ee\u6807\u5df2\u88ab\u5360\u9886")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Baritone 报告路径计算失败(目标不可达,如被悬崖/高山/水体完全隔断):立即跳过,不再傻等超时
|
// Baritone 报告路径计算失败(目标不可达,如被悬崖/高山/水体完全隔断)。
|
||||||
|
// 不立即跳过:CALC_FAILED 常因瞬时能力限制或起点卡位误报,先重新寻路一次
|
||||||
|
//(相当于“往边上走两步再算一遍”),重试耗尽才跳过当前目标。
|
||||||
if (this.pathCalcFailed) {
|
if (this.pathCalcFailed) {
|
||||||
this.pathCalcFailed = false;
|
this.pathCalcFailed = false;
|
||||||
this.log("\u5bfb\u8def\u5931\u8d25\uff08\u76ee\u6807\u4e0d\u53ef\u8fbe\uff09\uff0c\u8df3\u8fc7 " + this.target.x() + "," + this.target.z());
|
++this.retryCount;
|
||||||
this.setState(State.FAIL, "\u5bfb\u8def\u5931\u8d25");
|
if (this.retryCount >= this.config.retries) {
|
||||||
|
// 重试耗尽:不直接切换目标,先"往边上走两步"再重试一次;仍失败才交给回城/切换处理
|
||||||
|
if (!this.sideStepAttempted) {
|
||||||
|
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();
|
||||||
|
this.retrySameTarget("\u5bfb\u8def\u5931\u8d25\uff0c\u91cd\u65b0\u5bfb\u8def");
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 目标已不再与己方领土接壤(地图刷新后边界变化):立即跳过,避免走到无效区块
|
// 目标已不再与己方领土接壤(地图刷新后边界变化):立即跳过,避免走到无效区块
|
||||||
@@ -349,6 +417,8 @@ implements SubtitleChatMonitor.Listener, BaritoneBridge.PathListener {
|
|||||||
}
|
}
|
||||||
if (this.playerChunk.equals(this.target)) {
|
if (this.playerChunk.equals(this.target)) {
|
||||||
this.baritoneBridge.cancel();
|
this.baritoneBridge.cancel();
|
||||||
|
// 成功到达目标:寻路不再卡死,清零连续超时计数
|
||||||
|
this.consecutiveTimeoutMillis = 0L;
|
||||||
this.stageStartedMillis = System.currentTimeMillis();
|
this.stageStartedMillis = System.currentTimeMillis();
|
||||||
this.setState(State.WAITING, "\u5df2\u5230\u8fbe\uff0c\u7b49\u5f85\u9886\u5730\u5f00\u62d3");
|
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");
|
this.log("\u5df2\u5230\u8fbe\u76ee\u6807 " + this.target.x() + "," + this.target.z() + "\uff0c\u5f00\u59cb\u7b49\u5f85\u5f00\u57a6");
|
||||||
@@ -359,18 +429,147 @@ implements SubtitleChatMonitor.Listener, BaritoneBridge.PathListener {
|
|||||||
++this.retryCount;
|
++this.retryCount;
|
||||||
// 主动刷新地图,尽快同步最新领土状态
|
// 主动刷新地图,尽快同步最新领土状态
|
||||||
this.dataService.refreshAsync();
|
this.dataService.refreshAsync();
|
||||||
if (this.retryCount >= this.config.retries) {
|
// 累计"连续寻路超时"时长:连续多次寻路都超时说明玩家被地形/缝隙卡死走不到目标,
|
||||||
// 重试耗尽:跳过当前目标(不再回城),交给下一个目标
|
// 达到阈值后作为发送 /u return 回城(约 10 秒后传送到可寻路位置)的最后手段,平时不打断重试/走两步。
|
||||||
this.log("\u5bfb\u8def\u8d85\u65f6\uff0c\u8df3\u8fc7 " + this.target.x() + "," + this.target.z());
|
this.consecutiveTimeoutMillis += (long)this.config.waitTimeoutSeconds * 1000L;
|
||||||
this.setState(State.FAIL, "\u5bfb\u8def\u8d85\u65f6");
|
if (this.retryCount < this.config.retries) {
|
||||||
} else {
|
// 未耗尽:重新寻路(而非回城),取消旧路径并重新计算,往往"在边上走走"就能走出困境
|
||||||
// 重新寻路(而非回城):取消旧路径并重新计算,往往“在边上走走”就能走出困境
|
|
||||||
this.log("\u5bfb\u8def\u8d85\u65f6\uff0c\u91cd\u65b0\u5bfb\u8def " + 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");
|
this.retrySameTarget("\u5bfb\u8def\u8d85\u65f6\uff0c\u91cd\u65b0\u5bfb\u8def");
|
||||||
|
} else if (!this.sideStepAttempted) {
|
||||||
|
// 重试耗尽:先不切换目标,用"往边上走两步"再重试一次(走开障碍后重新寻路原目标)
|
||||||
|
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} 回城或切换目标。
|
||||||
|
* 每个目标最多触发一次(sideStepAttempted 标志在 startPathing 时重置)。
|
||||||
|
*/
|
||||||
|
private void startSideStep() {
|
||||||
|
this.baritoneBridge.cancel();
|
||||||
|
MinecraftClient client = MinecraftClient.getInstance();
|
||||||
|
this.sideStepping = true;
|
||||||
|
this.sideStepAttempted = true;
|
||||||
|
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");
|
||||||
|
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() {
|
private void tickWaiting() {
|
||||||
if (this.target == null) {
|
if (this.target == null) {
|
||||||
@@ -592,6 +791,8 @@ implements SubtitleChatMonitor.Listener, BaritoneBridge.PathListener {
|
|||||||
// 缩短"会话占领等待确认"窗口,避免假占领长时间残留
|
// 缩短"会话占领等待确认"窗口,避免假占领长时间残留
|
||||||
this.dataService.refreshAsync();
|
this.dataService.refreshAsync();
|
||||||
++this.completedClaims;
|
++this.completedClaims;
|
||||||
|
// 开垦成功:寻路/等待恢复正常,清零连续超时计数
|
||||||
|
this.consecutiveTimeoutMillis = 0L;
|
||||||
this.target = chunk;
|
this.target = chunk;
|
||||||
this.lastProgress = 100;
|
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");
|
this.log("\u5360\u9886\u6210\u529f " + chunk.x() + "," + chunk.z() + "\uff08\u533a\u5757 " + chunk.x() + "," + chunk.z() + " / \u4e16\u754c " + blockX + "," + blockZ + "\uff09");
|
||||||
@@ -622,6 +823,8 @@ implements SubtitleChatMonitor.Listener, BaritoneBridge.PathListener {
|
|||||||
// 占领成功后立即触发一次地图刷新,尽快确认该区块
|
// 占领成功后立即触发一次地图刷新,尽快确认该区块
|
||||||
this.dataService.refreshAsync();
|
this.dataService.refreshAsync();
|
||||||
++this.completedClaims;
|
++this.completedClaims;
|
||||||
|
// 开垦成功:寻路/等待恢复正常,清零连续超时计数
|
||||||
|
this.consecutiveTimeoutMillis = 0L;
|
||||||
this.lastProgress = 100;
|
this.lastProgress = 100;
|
||||||
this.log("\u5360\u9886\u6210\u529f " + this.target.x() + "," + this.target.z());
|
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());
|
this.setState(State.SUCCESS, "\u7586\u571f\u5df2\u5f00\u62d3 " + this.target.x() + "," + this.target.z());
|
||||||
@@ -718,10 +921,10 @@ implements SubtitleChatMonitor.Listener, BaritoneBridge.PathListener {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 进食完成后恢复任务:若仍在寻路阶段则重新发起寻路 */
|
/** 进食完成后恢复任务:若仍在寻路阶段则重新发起寻路(走两步阶段不抢路) */
|
||||||
private void resumeAfterEating() {
|
private void resumeAfterEating() {
|
||||||
this.eating = false;
|
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.stageStartedMillis = System.currentTimeMillis();
|
||||||
this.baritoneBridge.pathTo(this.target);
|
this.baritoneBridge.pathTo(this.target);
|
||||||
}
|
}
|
||||||
@@ -796,6 +999,7 @@ implements SubtitleChatMonitor.Listener, BaritoneBridge.PathListener {
|
|||||||
SUCCESS,
|
SUCCESS,
|
||||||
FAIL,
|
FAIL,
|
||||||
NEXT,
|
NEXT,
|
||||||
|
RETURNING,
|
||||||
COMPLETE,
|
COMPLETE,
|
||||||
STOPPED;
|
STOPPED;
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{"schemaVersion":1,"id":"autostore","version":"1.2.1","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.4","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"}]}
|
||||||
在新工单中引用
屏蔽一个用户