diff --git a/README.md b/README.md
index de6d539..d0544b5 100644
--- a/README.md
+++ b/README.md
@@ -10,11 +10,12 @@
- **目标矿物 id**:带不带 `minecraft:` 前缀均可识别(如 `diamond_ore` 与 `minecraft:diamond_ore` 等价)。
### 智能辅助(可逐项开关)
+- **无路自动搭方块**:开启后 Baritone 在没有路时(深渊、缺口、岩浆挡路)自动放置方块搭桥/垫脚;模组会自动把背包里的搭路方块(按清单)补进快捷栏供其使用,快捷栏物品永不自动丢弃。
- **工具自动选择**:挖掘前校验手持工具能否采掘目标矿物(如远古残骸必须钻石镐以上,铁镐只会白挖不掉落),不适用时自动从背包换上能采掘且耐久最高的工具;没有合适工具时提示并暂停矿物挖掘,放入工具后自动恢复。
- **自动切换工具**:由 Baritone 在挖掘途中自动选择最合适的工具。
- **自动进食**:饥饿度低时自动吃背包食物,跳过金苹果等特殊/珍贵食物。
- **自动换备用装备**:护甲/工具耐久过低时自动从背包换上同类备用装备。
-- **提前丢垃圾**:背包接近满(已用 ≥30 格)时就提前丢弃圆石、深板岩等垃圾,为矿物腾出空间,避免被误判"背包已满需返回"。
+- **提前丢垃圾**:背包接近满(已用 ≥30 格)时就提前丢弃圆石、深板岩等垃圾,为矿物腾出空间,避免被误判"背包已满需返回";快捷栏物品(搭路方块/工具/食物)永不丢弃。
- **杀戮光环**:自动攻击附近真正敌对生物,不误伤铁傀儡、僵尸猪灵、末影人等中立生物。
- **保命优先**:血线过低时暂停挖矿向上撤离,恢复后自动续任务。
- **装备耐久保护**:护甲/工具耐久低于阈值时暂停或换装,下界模式直接返程。
@@ -41,6 +42,7 @@
## 版本记录
+- **v2.2.2**:新增无路自动搭方块(可开关):Baritone 放置方块搭桥/垫脚,自动补给快捷栏搭路方块并喂给 Baritone 可放置物品清单;AutoTrash 不再丢弃快捷栏物品。
- **v2.2.1**:移除自动火把;新增主世界矿物绕道挖掘(附近 10 格目标矿物自动挖掉,不漏矿);新增工具适用性检查(远古残骸等自动换钻石镐);支巷长度可配置(默认 10);矿物 id 兼容 `minecraft:` 前缀。
- **v2.2.0**:新增实时状态 HUD、`/autodig status` 命令;自动进食跳过特殊食物;背包接近满提前丢垃圾防误判返程;杀戮光环不再误伤中立生物;GUI 补全进食/保命/攻击范围等设置。
- **v2.1.0**:自动换备用装备 + 背包满自动丢垃圾。
diff --git a/gradle.properties b/gradle.properties
index 0397cc1..4fa5103 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.1
+mod_version=2.2.2
maven_group=autodig
archives_base_name=autodig
diff --git a/src/main/java/autodig/client/bridge/BaritoneBridge.java b/src/main/java/autodig/client/bridge/BaritoneBridge.java
index bbc123b..496b209 100644
--- a/src/main/java/autodig/client/bridge/BaritoneBridge.java
+++ b/src/main/java/autodig/client/bridge/BaritoneBridge.java
@@ -5,17 +5,22 @@ import baritone.api.BaritoneAPI;
import baritone.api.IBaritone;
import baritone.api.Settings;
import baritone.api.utils.BlockOptionalMeta;
+import java.util.ArrayList;
import java.util.List;
import net.minecraft.block.Block;
import net.minecraft.block.Blocks;
+import net.minecraft.item.Item;
+import net.minecraft.item.Items;
+import net.minecraft.registry.Registries;
+import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
/**
* Baritone API 封装(桥接层)。
*
*
把模组对 Baritone 的所有调用集中在此类:同步用户配置(工具切换、避岩浆、
- * 避怪、保命优先)、按名字/数量挖矿、前往指定坐标、前往最近传送门、
- * 取消任务等。(杀戮光环由 KillAura 近战实现,不经 Baritone。)
+ * 避怪、保命优先、无路时放置方块搭路)、按名字/数量挖矿、前往指定坐标、
+ * 前往最近传送门、取消任务等。(杀戮光环由 KillAura 近战实现,不经 Baritone。)
*
* 所有调用都做空安全保护,Baritone 未加载时相关操作会安全地静默失败。
*/
@@ -23,6 +28,9 @@ public final class BaritoneBridge {
/** 上一次打印 gotoBlock 诊断日志的时间戳,用于节流 */
private long lastGotoLog;
+ /** 搭路方块缓存:原始配置串未变化时直接复用,避免每 tick 解析 */
+ private String lastScaffoldRaw;
+ private List- scaffoldItemsCache = List.of();
/**
* 把模组配置同步到 Baritone 全局设置。
@@ -34,9 +42,9 @@ public final class BaritoneBridge {
Settings settings = BaritoneAPI.getSettings();
settings.allowSprint.value = true;
settings.allowBreak.value = true; // 挖矿必须允许挖方块
- settings.allowPlace.value = true; // 允许搭路/垫脚
+ settings.allowPlace.value = config.allowPlace; // 无路时放置方块(搭桥/垫脚),可关
+ settings.allowParkourPlace.value = config.allowPlace; // 跑酷垫脚同样受开关控制
settings.allowParkour.value = true;
- settings.allowParkourPlace.value = true;
settings.freeLook.value = true;
// 自动切换最合适工具
settings.autoTool.value = config.autoTool;
@@ -47,11 +55,41 @@ public final class BaritoneBridge {
settings.blocksToAvoid.value = config.avoidLava
? List.of(Blocks.LAVA)
: List.of();
+ // 搭路方块:喂给 Baritone 的可放置/可消耗物品清单(放方块时从中挑选)
+ settings.acceptableThrowawayItems.value = this.scaffoldItems(config);
} catch (Throwable ignored) {
// Baritone 未就绪时静默失败,避免抛出异常中断每帧逻辑
}
}
+ /**
+ * 解析搭路方块清单为物品列表(带缓存,配置串不变时不重复解析)。
+ * 无法识别的 id 跳过;清单为空时返回空列表(Baritone 沿用其默认清单)。
+ */
+ private List
- scaffoldItems(DigConfig config) {
+ String raw = config.scaffoldBlocks;
+ if (raw == null || raw.isBlank()) {
+ return List.of();
+ }
+ if (raw.equals(this.lastScaffoldRaw)) {
+ return this.scaffoldItemsCache;
+ }
+ List
- items = new ArrayList<>();
+ for (String name : config.getScaffoldItemList()) {
+ Identifier id = Identifier.tryParse(name.contains(":") ? name : "minecraft:" + name);
+ if (id == null) {
+ continue;
+ }
+ Item item = Registries.ITEM.get(id);
+ if (item != null && item != Items.AIR) {
+ items.add(item);
+ }
+ }
+ this.lastScaffoldRaw = raw;
+ this.scaffoldItemsCache = items;
+ return items;
+ }
+
/**
* 按方块 id 挖指定数量的矿。
*
diff --git a/src/main/java/autodig/client/config/ConfigStore.java b/src/main/java/autodig/client/config/ConfigStore.java
index ff38fcb..692ece0 100644
--- a/src/main/java/autodig/client/config/ConfigStore.java
+++ b/src/main/java/autodig/client/config/ConfigStore.java
@@ -89,6 +89,9 @@ public final class ConfigStore {
if (c.trashItems == null || c.trashItems.isBlank()) {
c.trashItems = "cobblestone,cobbled_deepslate,deepslate,tuff,dirt,granite,diorite,andesite,netherrack,blackstone,basalt";
}
+ if (c.scaffoldBlocks == null || c.scaffoldBlocks.isBlank()) {
+ c.scaffoldBlocks = "cobblestone,cobbled_deepslate,deepslate,netherrack,dirt,granite,diorite,andesite,tuff,blackstone,basalt";
+ }
return c;
}
diff --git a/src/main/java/autodig/client/config/DigConfig.java b/src/main/java/autodig/client/config/DigConfig.java
index b578782..1af00b6 100644
--- a/src/main/java/autodig/client/config/DigConfig.java
+++ b/src/main/java/autodig/client/config/DigConfig.java
@@ -49,6 +49,13 @@ public class DigConfig {
/** 优先绕开岩浆池(给岩浆方块额外寻路代价) */
public boolean avoidLava = true;
+ // ---- 搭路/放置 ----
+ /** 允许 Baritone 放置方块:没有路(深渊/缺口/岩浆挡路)时自动搭桥、垫脚 */
+ public boolean allowPlace = true;
+ /** 搭路方块清单(逗号分隔的物品 id,带不带 "minecraft:" 前缀均可);
+ * 会自动从背包挪一组到快捷栏供 Baritone 放置,同时喂给 Baritone 作为可放置物品 */
+ public String scaffoldBlocks = "cobblestone,cobbled_deepslate,deepslate,netherrack,dirt,granite,diorite,andesite,tuff,blackstone,basalt";
+
// ---- 杀戮光环 ----
/** 杀戮光环:自动攻击附近敌对生物 */
public boolean killAura = false;
@@ -98,6 +105,11 @@ public class DigConfig {
return parseOreList(this.trashItems);
}
+ /** 获取搭路方块清单 */
+ public List getScaffoldItemList() {
+ return parseOreList(this.scaffoldBlocks);
+ }
+
private static List parseOreList(String str) {
if (str == null || str.isBlank()) {
return List.of();
@@ -125,6 +137,8 @@ public class DigConfig {
c.trashItems = this.trashItems;
c.trashReserve = this.trashReserve;
c.avoidLava = this.avoidLava;
+ c.allowPlace = this.allowPlace;
+ c.scaffoldBlocks = this.scaffoldBlocks;
c.killAura = this.killAura;
c.killAuraSafe = this.killAuraSafe;
c.killRange = this.killRange;
diff --git a/src/main/java/autodig/client/controller/DigController.java b/src/main/java/autodig/client/controller/DigController.java
index 6a959a6..636ce4d 100644
--- a/src/main/java/autodig/client/controller/DigController.java
+++ b/src/main/java/autodig/client/controller/DigController.java
@@ -7,6 +7,7 @@ import autodig.client.miner.NetherMiner;
import autodig.client.miner.OverworldMiner;
import autodig.client.service.AutoEat;
import autodig.client.service.AutoGear;
+import autodig.client.service.AutoScaffold;
import autodig.client.service.AutoTrash;
import autodig.client.service.DeathRecorder;
import autodig.client.service.KillAura;
@@ -35,6 +36,7 @@ public final class DigController {
private final KillAura killAura = new KillAura();
private final AutoGear autoGear = new AutoGear();
private final AutoTrash autoTrash = new AutoTrash();
+ private final AutoScaffold autoScaffold = new AutoScaffold();
private final DeathRecorder deathRecorder = new DeathRecorder();
private final SessionStats stats = new SessionStats();
private final OverworldMiner overworld = new OverworldMiner(this.bridge);
@@ -60,10 +62,11 @@ public final class DigController {
}
PlayerEntity player = client.player;
- // 常驻能力:杀戮光环、自动进食、背包满丢垃圾、换装冷却
+ // 常驻能力:杀戮光环、自动进食、背包满丢垃圾、搭路方块补给、换装冷却
this.killAura.tick(config);
this.autoEat.tick(config);
this.autoTrash.tick(config);
+ this.autoScaffold.tick(config);
this.autoGear.tick();
// 死亡处理:记录死亡次数,暂停任务,重生后按需回到死亡坐标
diff --git a/src/main/java/autodig/client/gui/AutoDigGui.java b/src/main/java/autodig/client/gui/AutoDigGui.java
index e11c6d9..1bc0755 100644
--- a/src/main/java/autodig/client/gui/AutoDigGui.java
+++ b/src/main/java/autodig/client/gui/AutoDigGui.java
@@ -58,6 +58,7 @@ public final class AutoDigGui extends Screen {
private TextFieldWidget overworldOresField;
private TextFieldWidget netherOresField;
private TextFieldWidget trashField;
+ private TextFieldWidget scaffoldField;
public AutoDigGui(AutoDigModClient mod) {
super(Text.literal("AutoDig"));
@@ -75,6 +76,7 @@ public final class AutoDigGui extends Screen {
this.overworldOresField = null;
this.netherOresField = null;
this.trashField = null;
+ this.scaffoldField = null;
// 面板宽度自适应窗口,最小 200 保证 +/- 与输入框可用
this.panelW = MathHelper.clamp(this.width - 16, 200, 400);
@@ -120,6 +122,12 @@ public final class AutoDigGui extends Screen {
this.config.autoTrash = v;
this.init(); // 切换后显示/隐藏垃圾设置区
});
+ y = this.checkPair(y, "允许搭方块(无路时自动放置)", () -> this.config.allowPlace, v -> {
+ this.syncFields(); // 保留输入框中未应用的编辑
+ this.config.allowPlace = v;
+ this.init(); // 切换后显示/隐藏搭路设置区
+ },
+ null, null, null);
y += 10;
y = this.header(y, "进食/保命/攻击阈值");
@@ -149,6 +157,13 @@ public final class AutoDigGui extends Screen {
y += 10;
}
+ if (this.config.allowPlace) {
+ y = this.header(y, "搭路方块设置(无路时自动放置)");
+ y = this.fieldRow(y, "搭路方块(逗号分隔,自动补进快捷栏)",
+ this.config.scaffoldBlocks, tf -> this.scaffoldField = tf);
+ y += 10;
+ }
+
if (this.config.task == MineTask.OVERWORLD) {
y = this.header(y, "主世界设置");
y = this.numberRow(y, "挖掘层 (Y)", () -> this.config.overworldLevel,
@@ -391,6 +406,9 @@ public final class AutoDigGui extends Screen {
if (this.trashField != null) {
this.config.trashItems = this.trashField.getText();
}
+ if (this.scaffoldField != null) {
+ this.config.scaffoldBlocks = this.scaffoldField.getText();
+ }
}
/** 把界面临时配置写回 mod 全局配置并保存(供 controller 实时读取)。 */
@@ -399,6 +417,8 @@ public final class AutoDigGui extends Screen {
target.task = this.config.task;
target.survivalFirst = this.config.survivalFirst;
target.avoidLava = this.config.avoidLava;
+ target.allowPlace = this.config.allowPlace;
+ target.scaffoldBlocks = this.config.scaffoldBlocks;
target.autoEat = this.config.autoEat;
target.eatHungerThreshold = this.config.eatHungerThreshold;
target.autoTool = this.config.autoTool;
diff --git a/src/main/java/autodig/client/service/AutoScaffold.java b/src/main/java/autodig/client/service/AutoScaffold.java
new file mode 100644
index 0000000..5c9bde9
--- /dev/null
+++ b/src/main/java/autodig/client/service/AutoScaffold.java
@@ -0,0 +1,135 @@
+package autodig.client.service;
+
+import autodig.client.config.DigConfig;
+import autodig.client.config.MineTask;
+import java.util.HashSet;
+import java.util.Set;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.item.BlockItem;
+import net.minecraft.item.ItemStack;
+import net.minecraft.registry.Registries;
+import net.minecraft.screen.ScreenHandler;
+import net.minecraft.screen.slot.SlotActionType;
+
+/**
+ * 搭路方块补给服务。
+ *
+ *
Baritone 放置方块(无路时搭桥/垫脚)时从快捷栏挑选方块。
+ * 本服务在挖矿任务进行中周期性检查:快捷栏里搭路方块不足时,自动把主背包中
+ * 数量最多的一组搭路方块(按 {@code scaffoldBlocks} 清单)挪进快捷栏空位;
+ * 快捷栏已满时,只与快捷栏中已有的小堆搭路方块做整体交换,绝不碰工具、
+ * 食物等其他物品。操作走 {@code clickSlot} 正规网络包,单人/多人均可用。
+ *
+ * 与 AutoTrash 协同:AutoTrash 丢弃垃圾时不会丢快捷栏物品,
+ * {@code trashReserve} 保留的方块正是搭路补给的来源。
+ */
+public final class AutoScaffold {
+
+ /** 检查间隔(毫秒) */
+ private static final long CHECK_INTERVAL_MS = 2000;
+ /** 快捷栏搭路方块低于该数量时才补给 */
+ private static final int MIN_HOTBAR_COUNT = 16;
+
+ /** 上一次检查的时间戳(节流) */
+ private long lastCheck;
+
+ /** 每帧调用:快捷栏缺搭路方块时自动补给。 */
+ public void tick(DigConfig config) {
+ if (!config.allowPlace || config.task == MineTask.NONE) {
+ return;
+ }
+ MinecraftClient client = MinecraftClient.getInstance();
+ if (client.player == null || client.interactionManager == null) {
+ return;
+ }
+ if (client.currentScreen != null) {
+ return; // 有界面打开时不操作背包
+ }
+ PlayerEntity player = client.player;
+ ScreenHandler handler = player.currentScreenHandler;
+ if (handler != player.playerScreenHandler) {
+ return;
+ }
+ if (!handler.getCursorStack().isEmpty()) {
+ return;
+ }
+ long now = System.currentTimeMillis();
+ if (now - this.lastCheck < CHECK_INTERVAL_MS) {
+ return;
+ }
+ this.lastCheck = now;
+
+ Set scaffold = new HashSet<>(config.getScaffoldItemList());
+ if (scaffold.isEmpty()) {
+ return;
+ }
+ // 快捷栏(main 0-8)已有的搭路方块总数
+ int hotbarCount = 0;
+ for (int i = 0; i < 9; i++) {
+ ItemStack s = player.getInventory().main.get(i);
+ if (!s.isEmpty() && isScaffold(s, scaffold)) {
+ hotbarCount += s.getCount();
+ }
+ }
+ if (hotbarCount >= MIN_HOTBAR_COUNT) {
+ return;
+ }
+ // 主背包(main 9-35)里找数量最多的一组搭路方块
+ int bestSlot = -1;
+ int bestCount = 0;
+ for (int i = 9; i < 36; i++) {
+ ItemStack s = player.getInventory().main.get(i);
+ if (!s.isEmpty() && isScaffold(s, scaffold) && s.getCount() > bestCount) {
+ bestCount = s.getCount();
+ bestSlot = i;
+ }
+ }
+ if (bestSlot < 0) {
+ return; // 背包里没有搭路方块(trashReserve 为 0 或耗尽)
+ }
+ ItemStack bagStack = player.getInventory().main.get(bestSlot);
+ int bagScreenSlot = screenSlotOf(handler, bagStack);
+ if (bagScreenSlot < 0) {
+ return;
+ }
+ // 优先放进快捷栏空位(playerScreenHandler 槽位 36-44 对应快捷栏 0-8)
+ for (int i = 0; i < 9; i++) {
+ if (player.getInventory().main.get(i).isEmpty()) {
+ client.interactionManager.clickSlot(handler.syncId, bagScreenSlot, 0, SlotActionType.PICKUP, player);
+ client.interactionManager.clickSlot(handler.syncId, 36 + i, 0, SlotActionType.PICKUP, player);
+ return;
+ }
+ }
+ // 无空位:只与快捷栏里已有的更小堆搭路方块整体交换,不碰其他物品
+ for (int i = 0; i < 9; i++) {
+ ItemStack s = player.getInventory().main.get(i);
+ if (!s.isEmpty() && isScaffold(s, scaffold) && s.getCount() < bagStack.getCount()) {
+ int hotScreenSlot = screenSlotOf(handler, s);
+ if (hotScreenSlot < 0) {
+ continue;
+ }
+ client.interactionManager.clickSlot(handler.syncId, hotScreenSlot, 0, SlotActionType.PICKUP, player);
+ client.interactionManager.clickSlot(handler.syncId, bagScreenSlot, 0, SlotActionType.PICKUP, player);
+ client.interactionManager.clickSlot(handler.syncId, hotScreenSlot, 0, SlotActionType.PICKUP, player);
+ return;
+ }
+ }
+ }
+
+ /** 是否为清单内的搭路方块(BlockItem 且按 path 匹配,兼容命名空间前缀)。 */
+ private static boolean isScaffold(ItemStack stack, Set scaffold) {
+ return stack.getItem() instanceof BlockItem
+ && scaffold.contains(Registries.ITEM.getId(stack.getItem()).getPath());
+ }
+
+ /** 按对象引用在 ScreenHandler 里找槽位下标。 */
+ private static int screenSlotOf(ScreenHandler handler, ItemStack stack) {
+ for (int i = 0; i < handler.slots.size(); i++) {
+ if (handler.getSlot(i).getStack() == stack) {
+ return i;
+ }
+ }
+ return -1;
+ }
+}
diff --git a/src/main/java/autodig/client/service/AutoTrash.java b/src/main/java/autodig/client/service/AutoTrash.java
index b27fb78..b17bf0e 100644
--- a/src/main/java/autodig/client/service/AutoTrash.java
+++ b/src/main/java/autodig/client/service/AutoTrash.java
@@ -21,7 +21,8 @@ import net.minecraft.screen.slot.SlotActionType;
*
* 背包接近满(已用格数达到阈值)时就主动把 {@code trashItems} 清单里的
* 垃圾物品丢出背包(圆石、凝灰岩、深板岩等挖掘副产物),为挖到的矿物提前
- * 腾出空间,避免背包彻底满后触发"背包已满需返回"而中断挖矿。方块类垃圾
+ * 腾出空间,避免背包彻底满后触发"背包已满需返回"而中断挖矿。快捷栏
+ * 物品永不自动丢弃(搭路方块/工具/食物常驻快捷栏)。方块类垃圾
* 保留 {@code trashReserve} 个供 Baritone 搭路/垫脚使用。丢弃通过
* {@code clickSlot(THROW)} 走正规网络包,单人/多人均可用。
*/
@@ -62,9 +63,10 @@ public final class AutoTrash {
}
// 收集背包里的垃圾,按物品 id 分组(并记录是否为可放置的方块)
+ // 从 main 9-35 收集:快捷栏(main 0-8)物品永不自动丢弃(搭路方块/工具/食物)
Map> groups = new HashMap<>();
Map blockLike = new HashMap<>();
- for (int i = 0; i < 36; i++) {
+ for (int i = 9; i < 36; i++) {
ItemStack s = player.getInventory().main.get(i);
if (s.isEmpty()) {
continue;
diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json
index d967130..ccddaf5 100644
--- a/src/main/resources/fabric.mod.json
+++ b/src/main/resources/fabric.mod.json
@@ -3,7 +3,7 @@
"id": "autodig",
"version": "${version}",
"name": "AutoDig",
- "description": "自动挖矿模组(内置 Baritone 1.13.1,无需另行安装):主世界模式与下界模式,均可自定义目标矿物(带不带 minecraft: 前缀均可)。主世界鱼骨巷道挖矿 + 附近目标矿物自动绕道挖掘;下界自动进出传送门挖矿并支持返程。自动切换最适用工具(远古残骸等自动换钻石镐)、自动进食(避开特殊食物)、杀戮光环(不误伤中立生物)、保命优先、优先绕开岩浆、装备耐久保护(自动换备用装备)、背包近满提前丢垃圾防误判返程、死亡重生回点、实时状态 HUD。",
+ "description": "自动挖矿模组(内置 Baritone 1.13.1,无需另行安装):主世界模式与下界模式,均可自定义目标矿物(带不带 minecraft: 前缀均可)。主世界鱼骨巷道挖矿 + 附近目标矿物自动绕道挖掘;下界自动进出传送门挖矿并支持返程。无路时自动放置方块搭桥/垫脚(自动补给快捷栏搭路方块)、自动切换最适用工具(远古残骸等自动换钻石镐)、自动进食(避开特殊食物)、杀戮光环(不误伤中立生物)、保命优先、优先绕开岩浆、装备耐久保护(自动换备用装备)、背包近满提前丢垃圾防误判返程、死亡重生回点、实时状态 HUD。",
"authors": [],
"contact": {},
"license": "All-Rights-Reserved",