API 参考
OneKeyMiner API 开发者文档
Section titled “OneKeyMiner API 开发者文档”OneKeyMiner 是一个支持 Fabric、NeoForge 和 Forge 的多平台连锁操作模组。本文档面向希望与 OneKeyMiner 集成或扩展其功能的模组开发者。
- 三种链式操作类型:挖矿(Mining)、交互(Interaction)、种植(Planting)
- 方块/物品白名单/黑名单 API:注册自定义方块和工具
- 事件系统:监听操作前后事件,实现自定义逻辑
- 标签系统:支持
#namespace:tag格式的标签匹配 - 平台抽象:通过 SPI 机制支持多平台
- BFS 算法:使用广度优先搜索防止栈溢出
- 线程安全 API:所有 API 方法都是线程安全的
| 平台 | 包名 | 最低版本 |
|---|---|---|
| Fabric | onekeyminer-fabric | 0.15.0+ |
| NeoForge | onekeyminer-neoforge | 21.0+ |
| Forge | onekeyminer-forge | 59.0+ |
Fabric (build.gradle)
Section titled “Fabric (build.gradle)”repositories { maven { name = "OneKeyMiner" url = "https://maven.example.com/releases" }}
dependencies { // 完整实现(推荐) modImplementation "org.xiyu:onekeyminer-fabric:${onekeyminer_version}"
// 仅 API(编译时依赖) modCompileOnly "org.xiyu:onekeyminer-api:${onekeyminer_version}"}NeoForge (build.gradle)
Section titled “NeoForge (build.gradle)”repositories { maven { name = "OneKeyMiner" url = "https://maven.example.com/releases" }}
dependencies { implementation "org.xiyu:onekeyminer-neoforge:${onekeyminer_version}"}Forge (build.gradle)
Section titled “Forge (build.gradle)”repositories { maven { name = "OneKeyMiner" url = "https://maven.example.com/releases" }}
dependencies { implementation fg.deobf("org.xiyu:onekeyminer-forge:${onekeyminer_version}")}检查模组是否加载
Section titled “检查模组是否加载”在调用 OneKeyMiner API 之前,请确保模组已加载:
// Fabricif (FabricLoader.getInstance().isModLoaded("onekeyminer")) { MyModIntegration.init();}
// Forge/NeoForgeif (ModList.get().isLoaded("onekeyminer")) { MyModIntegration.init();}核心 API
Section titled “核心 API”OneKeyMinerAPI 类
Section titled “OneKeyMinerAPI 类”org.xiyu.onekeyminer.api.OneKeyMinerAPI 是主要的 API 入口点。所有方法都是静态的且线程安全。
import org.xiyu.onekeyminer.api.OneKeyMinerAPI;方块白名单/黑名单
Section titled “方块白名单/黑名单”添加方块到白名单
Section titled “添加方块到白名单”// 通过资源位置字符串注册OneKeyMinerAPI.registerBlock("mymod:custom_ore");
// 通过 Block 实例注册OneKeyMinerAPI.registerBlock(MyBlocks.CUSTOM_ORE);
// 注册标签内的所有方块OneKeyMinerAPI.registerBlockTag("#mymod:custom_ores");OneKeyMinerAPI.registerBlockTag("#c:ores"); // 通用标签
// 从白名单移除OneKeyMinerAPI.unregisterBlock("mymod:custom_ore");添加方块到黑名单
Section titled “添加方块到黑名单”黑名单优先级高于白名单。
// 添加到黑名单OneKeyMinerAPI.blacklistBlock("minecraft:bedrock");OneKeyMinerAPI.blacklistBlock(Blocks.SPAWNER);
// 从黑名单移除OneKeyMinerAPI.unblacklistBlock("minecraft:bedrock");工具白名单/黑名单
Section titled “工具白名单/黑名单”// 添加到挖掘工具白名单OneKeyMinerAPI.whitelistTool("mymod:custom_pickaxe");OneKeyMinerAPI.whitelistTool("#mymod:pickaxes"); // 使用标签
// 添加到挖掘工具黑名单OneKeyMinerAPI.blacklistTool("minecraft:wooden_pickaxe");// 添加到交互工具白名单(剪刀、锄头、去皮用的斧头等)OneKeyMinerAPI.whitelistInteractionTool("mymod:magic_shears");OneKeyMinerAPI.whitelistInteractionTool("#c:shears");
// 添加到交互工具黑名单OneKeyMinerAPI.blacklistInteractionTool("mymod:broken_shears");自定义工具动作规则
Section titled “自定义工具动作规则”为特定工具绑定方块/实体目标,并指定连锁动作:
// 实体剪羊毛规则(仅羊)OneKeyMinerAPI.registerEntityShearingRule( "mymod:golden_shears", "minecraft:sheep");
// 方块交互规则(类似锄头的耕地行为)OneKeyMinerAPI.registerInteractionToolRule( "mymod:terra_hoe", OneKeyMinerAPI.ToolTargetType.BLOCK, OneKeyMinerAPI.InteractionRule.TILLING, "#minecraft:dirt");
// 挖掘规则(右键触发连锁挖掘)OneKeyMinerAPI.registerMiningToolRule( "mymod:ore_wand", "#c:ores");// 添加到可种植白名单OneKeyMinerAPI.whitelistPlantable("mymod:magic_seeds");OneKeyMinerAPI.whitelistPlantable("#c:seeds");
// 添加到可种植黑名单OneKeyMinerAPI.blacklistPlantable("mymod:cursed_seeds");方块分组允许不同类型的方块一起被连锁挖掘(用于”宽松匹配”模式)。
// 将方块添加到同一组OneKeyMinerAPI.addBlockToGroup("mymod:copper_ore", "copper");OneKeyMinerAPI.addBlockToGroup("mymod:deepslate_copper_ore", "copper");OneKeyMinerAPI.addBlockToGroup("minecraft:copper_ore", "copper");
// 检查两个方块是否在同一组boolean sameGroup = OneKeyMinerAPI.areBlocksInSameGroup(block1, block2);
// 检查两个方块是否共享任何标签boolean shareTag = OneKeyMinerAPI.blocksShareTag(state1, state2);// 检查方块是否允许连锁挖矿boolean allowed = OneKeyMinerAPI.isBlockAllowed(block);
// 检查方块是否在黑名单中boolean blacklisted = OneKeyMinerAPI.isBlockBlacklisted(block);
// 检查工具是否允许连锁挖矿boolean toolAllowed = OneKeyMinerAPI.isToolAllowed(itemStack);
// 检查工具是否允许连锁交互boolean interactionAllowed = OneKeyMinerAPI.isInteractionToolAllowed(itemStack);
// 检查物品是否可种植boolean plantable = OneKeyMinerAPI.isPlantableAllowed(itemStack);
// 访问只读集合Set<String> whitelist = OneKeyMinerAPI.BLOCK_WHITELIST;Set<String> blacklist = OneKeyMinerAPI.BLOCK_BLACKLIST;Set<String> toolWhitelist = OneKeyMinerAPI.TOOL_WHITELIST;Set<String> toolBlacklist = OneKeyMinerAPI.TOOL_BLACKLIST;链式操作系统
Section titled “链式操作系统”ChainActionType 枚举
Section titled “ChainActionType 枚举”定义三种链式操作类型:
import org.xiyu.onekeyminer.chain.ChainActionType;
ChainActionType.MINING // 连锁挖矿(破坏方块)ChainActionType.INTERACTION // 连锁交互(剪羊毛、锄地、去皮等)ChainActionType.PLANTING // 连锁种植(在农田播种)ChainActionType 方法
Section titled “ChainActionType 方法”ChainActionType type = ChainActionType.MINING;
// 获取显示名称(用于 UI)String name = type.getDisplayName(); // "Mining"
// 获取 ID(用于序列化)String id = type.getId(); // "mining"
// 从字符串解析ChainActionType parsed = ChainActionType.fromId("mining");ChainActionContext 上下文
Section titled “ChainActionContext 上下文”上下文对象包含执行链式操作所需的所有信息。使用 Builder 模式创建:
import org.xiyu.onekeyminer.chain.ChainActionContext;
// 快速创建挖矿上下文ChainActionContext miningContext = ChainActionContext.forMining(player, level, pos, state) .build();
// 快速创建交互上下文ChainActionContext interactionContext = ChainActionContext.forInteraction( player, level, pos, state, heldItem, hand) .build();
// 快速创建种植上下文ChainActionContext plantingContext = ChainActionContext.forPlanting( player, level, pos, heldItem, hand) .build();
// 完整 Builder 用法ChainActionContext context = ChainActionContext.builder() .player(player) .level(level) .originPos(pos) .originState(state) .actionType(ChainActionType.MINING) .heldItem(player.getMainHandItem()) .hand(InteractionHand.MAIN_HAND) .config(customConfig) // 可选:使用自定义配置 .build();ChainActionContext 访问器
Section titled “ChainActionContext 访问器”ServerPlayer player = context.getPlayer();Level level = context.getLevel();BlockPos originPos = context.getOriginPos();BlockState originState = context.getOriginState();ChainActionType actionType = context.getActionType();ItemStack heldItem = context.getHeldItem();InteractionHand hand = context.getHand();MinerConfig config = context.getConfig();ChainActionResult 结果
Section titled “ChainActionResult 结果”执行链式操作后返回的结果对象:
import org.xiyu.onekeyminer.chain.ChainActionResult;
ChainActionResult result = ChainActionLogic.execute(context);
// 检查是否成功boolean success = result.isSuccess();
// 获取统计信息int totalCount = result.totalCount(); // 处理的总方块数int durabilityUsed = result.durabilityUsed(); // 消耗的耐久度int hungerUsed = result.hungerUsed(); // 消耗的饥饿值Set<BlockPos> positions = result.successPositions(); // 所有处理的位置
// 获取收集的掉落物和经验值(仅挖矿和收割操作有数据)List<ItemStack> drops = result.collectedDrops(); // 收集的掉落物int exp = result.experienceCollected(); // 收集的经验值
// 获取停止原因ChainActionResult.StopReason reason = result.stopReason();
// 获取摘要字符串(用于日志/显示)String summary = result.getSummary();// 示例: "Mining: 32 blocks, 15 drops, 48 exp, stopped: MAX_BLOCKS"StopReason 枚举
Section titled “StopReason 枚举”| 枚举值 | 描述 |
|---|---|
COMPLETED | 正常完成(所有方块处理完毕) |
MAX_BLOCKS | 达到最大方块数限制 |
MAX_DISTANCE | 达到最大距离限制 |
LOW_DURABILITY | 工具耐久度不足 |
TOOL_BROKEN | 工具在操作中损坏 |
LOW_HUNGER | 玩家饥饿值不足 |
TIMEOUT | 操作超时(防卡顿保护) |
EVENT_CANCELLED | 被事件监听器取消 |
NO_VALID_BLOCKS | 没有找到有效方块 |
ERROR | 发生错误 |
ChainActionLogic 核心逻辑
Section titled “ChainActionLogic 核心逻辑”执行链式操作的核心逻辑类:
import org.xiyu.onekeyminer.chain.ChainActionLogic;
// 使用上下文执行ChainActionResult result = ChainActionLogic.execute(context);
// 方块破坏事件的便捷方法ChainActionResult miningResult = ChainActionLogic.onBlockBreak( player, level, pos, state);
// 检查是否应该触发连锁挖矿(不执行)boolean shouldTrigger = ChainActionLogic.shouldTriggerChainMining( player, level, pos, state);OneKeyMiner 提供事件系统供外部模组监听和干预链式操作。
PreActionEvent 操作前事件
Section titled “PreActionEvent 操作前事件”在链式操作开始前触发。可取消。
import org.xiyu.onekeyminer.api.event.PreActionEvent;
public class PreActionEvent { // 获取执行操作的玩家 ServerPlayer getPlayer();
// 获取世界实例 Level getLevel();
// 获取起始方块位置 BlockPos getOriginPos();
// 获取起始方块状态 BlockState getOriginState();
// 获取操作类型 ChainActionType getActionType();
// 获取手持物品 ItemStack getHeldItem();
// 获取目标位置(可修改 - 添加/移除位置) Set<BlockPos> getTargetPositions();
// 检查是否已取消 boolean isCancelled();
// 取消操作 void cancel();
// 设置取消状态 void setCancelled(boolean cancelled);}PostActionEvent 操作后事件
Section titled “PostActionEvent 操作后事件”在链式操作完成后触发。不可取消 - 仅用于信息收集。
import org.xiyu.onekeyminer.api.event.PostActionEvent;
public class PostActionEvent { // 获取执行操作的玩家 ServerPlayer getPlayer();
// 获取世界实例 Level getLevel();
// 获取起始方块位置 BlockPos getOriginPos();
// 获取操作类型 ChainActionType getActionType();
// 获取操作结果 ChainActionResult getResult();
// 便捷方法:获取收集的掉落物(委托给 result) List<ItemStack> getCollectedDrops();
// 便捷方法:获取收集的经验值(委托给 result) int getExperienceCollected();}事件注册方法
Section titled “事件注册方法”import org.xiyu.onekeyminer.api.event.ChainEvents;
// 注册操作前监听器(所有类型)ChainEvents.registerPreActionListener(event -> { if (isProtectedArea(event.getOriginPos())) { event.cancel(); event.getPlayer().sendSystemMessage( Component.literal("此区域禁止链式操作!")); }});
// 仅注册特定操作类型ChainEvents.registerPreActionListener(ChainActionType.MINING, event -> { // 只处理挖矿事件});
// 带条件的注册ChainEvents.registerPreActionListener( event -> event.getPlayer().hasPermissions(2), // 条件:仅管理员 event -> { // 只对管理员执行 });
// 注册操作后监听器ChainEvents.registerPostActionListener(event -> { LOGGER.info("玩家 {} 通过 {} 处理了 {} 个方块", event.getPlayer().getName().getString(), event.getActionType().getDisplayName(), event.getResult().totalCount());});
// 注册特定类型ChainEvents.registerPostActionListener(ChainActionType.PLANTING, event -> { // 只处理种植事件});
// 注销监听器(保留引用)var listener = ChainEvents.registerPreActionListener(event -> { /* ... */ });ChainEvents.unregisterPreActionListener(listener);TagResolver 类
Section titled “TagResolver 类”OneKeyMiner 使用 TagResolver 进行方块和物品的模式匹配。
import org.xiyu.onekeyminer.registry.TagResolver;
// 获取单例实例TagResolver resolver = TagResolver.getInstance();
// 检查方块是否匹配模式boolean matches = resolver.matchesBlock(blockState, "#minecraft:logs");
// 检查物品是否匹配模式boolean matches = resolver.matchesItem(itemStack, "#c:shears");
// 检查两个方块是否共享任何标签boolean shareTag = resolver.blocksShareTag(state1, state2);
// 验证模式格式boolean valid = resolver.isValidPattern("#minecraft:logs");
// 清除缓存(配置更改后调用)resolver.clearCache();| 格式 | 说明 | 示例 |
|---|---|---|
#namespace:tag | 标签引用 | #minecraft:logs、#c:ores |
namespace:id | 直接资源位置 | minecraft:diamond_ore |
*pattern* | 包含匹配 | *ore* |
pattern* | 前缀匹配 | minecraft:* |
*pattern | 后缀匹配 | *_ore |
平台共用标签
Section titled “平台共用标签”不同平台的共用标签命名空间不同,OneKeyMiner 通过 PlatformServices.getConventionalTagPrefix() 在运行时自动适配:
| 平台 | 共用标签前缀 | 示例 |
|---|---|---|
| Fabric | c | #c:ores、#c:seeds |
| NeoForge | c | #c:ores、#c:seeds |
| Forge | forge | #forge:ores、#forge:seeds |
注意:在配置文件和 API 中使用标签时,无需关心平台差异——模组内部会自动使用当前平台的正确前缀。如果您需要在代码中手动构建共用标签,可以调用
PlatformServices.getInstance().getConventionalTagPrefix()来获取当前平台的前缀。
PlatformServices 接口
Section titled “PlatformServices 接口”OneKeyMiner 使用平台抽象层实现跨平台兼容。
import org.xiyu.onekeyminer.platform.PlatformServices;
// 获取平台实例PlatformServices platform = PlatformServices.getInstance();
// 获取平台名称String name = platform.getPlatformName(); // "Fabric"、"NeoForge" 或 "Forge"
// 检查模组是否加载boolean loaded = platform.isModLoaded("mymod");
// 获取配置目录Path configDir = platform.getConfigDirectory();
// 检查/设置玩家的连锁模式boolean active = platform.isChainModeActive(player);platform.setChainModeActive(player, true);
// 获取当前平台的共用标签前缀String prefix = platform.getConventionalTagPrefix(); // Fabric/NeoForge="c", Forge="forge"平台特定实现
Section titled “平台特定实现”每个平台都有自己的实现:
FabricPlatformServices- Fabric 实现NeoForgePlatformServices- NeoForge 实现ForgePlatformServices- Forge 实现
正确的实现通过 SPI(服务提供者接口)自动加载。
MinerConfig 类
Section titled “MinerConfig 类”访问当前配置:
import org.xiyu.onekeyminer.config.MinerConfig;import org.xiyu.onekeyminer.config.ConfigManager;
// 获取当前配置MinerConfig config = ConfigManager.getConfig();
// 访问配置值boolean enabled = config.enabled; // 是否启用int maxBlocks = config.maxBlocks; // 最大方块数int maxDistance = config.maxDistance; // 最大距离boolean allowDiagonal = config.allowDiagonal; // 允许对角线连接boolean consumeDurability = config.consumeDurability; // 消耗耐久int preserveDurability = config.preserveDurability; // 保留耐久boolean consumeHunger = config.consumeHunger; // 消耗饥饿值int minHungerLevel = config.minHungerLevel; // 最低饥饿值boolean allowBareHand = config.allowBareHand; // 允许空手boolean teleportDrops = config.teleportDrops; // 传送掉落物boolean teleportExp = config.teleportExp; // 传送经验boolean playSound = config.playSound; // 播放音效运行时配置修改
Section titled “运行时配置修改”// 从文件重新加载配置ConfigManager.reload();
// 保存当前配置到文件ConfigManager.save();Config API(面向附属模组)
Section titled “Config API(面向附属模组)”附属模组可用 OneKeyMinerAPI 提供的配置读写方法,修改后会自动通过网络同步到服务端:
import org.xiyu.onekeyminer.api.OneKeyMinerAPI;
// 读取配置String shape = OneKeyMinerAPI.getSelectedShape(); // 获取当前挖矿形状boolean drops = OneKeyMinerAPI.isTeleportDropsEnabled(); // 是否传送掉落物boolean exp = OneKeyMinerAPI.isTeleportExpEnabled(); // 是否传送经验
// 修改配置(自动触发网络同步)OneKeyMinerAPI.setSelectedShape("SPHERE");OneKeyMinerAPI.setTeleportDropsEnabled(true);OneKeyMinerAPI.setTeleportExpEnabled(false);
// 监听配置变更OneKeyMinerAPI.addConfigChangeListener(key -> { LOGGER.info("配置已变更: {}", key);});示例 1:基础集成
Section titled “示例 1:基础集成”package com.example.mymod;
import org.xiyu.onekeyminer.api.OneKeyMinerAPI;
public class MyModIntegration {
public static void init() { // 检查 OneKeyMiner 是否已加载 if (!isModLoaded("onekeyminer")) { return; }
registerContent(); }
private static void registerContent() { // 注册自定义矿石 OneKeyMinerAPI.registerBlock("mymod:mythril_ore"); OneKeyMinerAPI.registerBlock("mymod:deepslate_mythril_ore");
// 或者使用标签(推荐) OneKeyMinerAPI.registerBlockTag("#mymod:mythril_ores");
// 添加到同一组以支持宽松匹配 OneKeyMinerAPI.addBlockToGroup("mymod:mythril_ore", "mythril"); OneKeyMinerAPI.addBlockToGroup("mymod:deepslate_mythril_ore", "mythril");
// 注册自定义工具 OneKeyMinerAPI.whitelistTool("mymod:mythril_pickaxe"); OneKeyMinerAPI.whitelistInteractionTool("mymod:mythril_shears");
// 注册自定义种子 OneKeyMinerAPI.whitelistPlantable("mymod:magic_seeds"); }}示例 2:区域保护集成
Section titled “示例 2:区域保护集成”package com.example.protection;
import org.xiyu.onekeyminer.api.event.ChainEvents;import org.xiyu.onekeyminer.api.event.PreActionEvent;import net.minecraft.core.BlockPos;import net.minecraft.network.chat.Component;
public class ProtectionIntegration {
public static void init() { ChainEvents.registerPreActionListener(ProtectionIntegration::onPreAction); }
private static void onPreAction(PreActionEvent event) { BlockPos origin = event.getOriginPos(); var player = event.getPlayer();
// 检查起始位置是否在保护区域 if (ProtectionAPI.isProtected(origin, player)) { event.cancel(); player.sendSystemMessage( Component.literal("§c此保护区域内禁止链式操作!")); return; }
// 从目标列表中移除受保护的方块 event.getTargetPositions().removeIf(pos -> ProtectionAPI.isProtected(pos, player) );
// 限制在领地边界内 event.getTargetPositions().removeIf(pos -> !ProtectionAPI.isInSameClaim(origin, pos, player) ); }}示例 3:操作统计追踪
Section titled “示例 3:操作统计追踪”package com.example.stats;
import org.xiyu.onekeyminer.api.event.ChainEvents;import org.xiyu.onekeyminer.api.event.PostActionEvent;import org.xiyu.onekeyminer.chain.ChainActionType;import java.util.Map;import java.util.UUID;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.atomic.AtomicLong;
public class ActionStatsTracker {
private static final Map<UUID, Map<ChainActionType, AtomicLong>> PLAYER_STATS = new ConcurrentHashMap<>();
public static void init() { ChainEvents.registerPostActionListener(event -> { UUID playerId = event.getPlayer().getUUID(); ChainActionType actionType = event.getActionType(); int count = event.getResult().totalCount();
PLAYER_STATS .computeIfAbsent(playerId, k -> new ConcurrentHashMap<>()) .computeIfAbsent(actionType, k -> new AtomicLong(0)) .addAndGet(count);
// 利用新的掉落物/经验数据 List<ItemStack> drops = event.getCollectedDrops(); int exp = event.getExperienceCollected(); if (!drops.isEmpty()) { LOGGER.info("玩家 {} 通过 {} 收集了 {} 个掉落物, {} 经验", event.getPlayer().getName().getString(), actionType.getDisplayName(), drops.size(), exp); }
// 成就检查 long total = getTotalActions(playerId, actionType); if (total >= 1000) { grantAchievement(event.getPlayer(), "chain_master_" + actionType.getId()); } }); }
public static long getTotalActions(UUID playerId, ChainActionType type) { return PLAYER_STATS .getOrDefault(playerId, Map.of()) .getOrDefault(type, new AtomicLong(0)) .get(); }
public static Map<ChainActionType, Long> getPlayerStats(UUID playerId) { Map<ChainActionType, AtomicLong> stats = PLAYER_STATS.get(playerId); if (stats == null) return Map.of();
Map<ChainActionType, Long> result = new HashMap<>(); stats.forEach((type, count) -> result.put(type, count.get())); return result; }}示例 4:自定义工具逻辑
Section titled “示例 4:自定义工具逻辑”package com.example.custom;
import org.xiyu.onekeyminer.api.event.ChainEvents;import org.xiyu.onekeyminer.chain.ChainActionType;
public class CustomToolHandler {
public static void init() { // 修改自定义工具的行为 ChainEvents.registerPreActionListener(ChainActionType.MINING, event -> { var player = event.getPlayer(); var item = player.getMainHandItem();
// 检查附魔或 NBT if (item.is(MyItems.MINING_LASER)) { // 采矿激光没有方块限制 // (由自定义逻辑处理) }
// 检查特殊修饰器 if (item.getEnchantmentLevel(MyEnchantments.EXCAVATION) > 0) { // 双倍目标 int level = item.getEnchantmentLevel(MyEnchantments.EXCAVATION); // 自定义扩展逻辑... } });
ChainEvents.registerPostActionListener(ChainActionType.MINING, event -> { var item = event.getPlayer().getMainHandItem();
// 触发特殊效果 if (item.is(MyItems.FORTUNE_AMPLIFIER)) { // 应用额外掉落 applyFortune(event.getResult().successPositions(), event.getPlayer()); } }); }}示例 5:附属模组开发
Section titled “示例 5:附属模组开发”OneKeyMiner 附属模组的示例结构:
package com.example.okm_addon;
import org.xiyu.onekeyminer.api.OneKeyMinerAPI;import org.xiyu.onekeyminer.api.event.ChainEvents;import org.xiyu.onekeyminer.api.event.PreActionEvent;import org.xiyu.onekeyminer.chain.ChainActionType;
public class OKMAddonMain {
public static void init() { // 注册附属模组的方块/工具 registerContent();
// 设置事件监听器 setupEventListeners();
// 可选:设置客户端渲染 if (isClientSide()) { OKMAddonClient.init(); } }
private static void registerContent() { // 注册附属模组的自定义矿石 OneKeyMinerAPI.registerBlockTag("#okm_addon:custom_ores");
// 注册附属模组的自定义工具 OneKeyMinerAPI.whitelistTool("#okm_addon:enhanced_pickaxes"); }
private static void setupEventListeners() { // 操作前:自定义目标集合 ChainEvents.registerPreActionListener(event -> { if (event.getActionType() == ChainActionType.MINING) { // 示例:移除不允许的目标 event.getTargetPositions().removeIf(pos -> isForbidden(event.getPlayer().level(), pos) ); } });
// 操作后:自定义音效/粒子效果 ChainEvents.registerPostActionListener(event -> { if (event.getResult().totalCount() > 10) { playChainMiningSound(event.getPlayer(), event.getOriginPos()); spawnChainMiningParticles(event.getResult().successPositions()); } }); }}示例 6:Jade 联动(显示连锁信息)
Section titled “示例 6:Jade 联动(显示连锁信息)”在 Jade 中显示最近一次连锁结果(方块数量 + 当前搜索形状)。
注册插件(Fabric 需添加 entrypoint):
@WailaPluginpublic class OKMJadePlugin implements IWailaPlugin { @Override public void register(IWailaCommonRegistration registration) { registration.registerBlockDataProvider(OKMChainDataProvider.INSTANCE, BlockEntity.class); }
@Override public void registerClient(IWailaClientRegistration registration) { registration.registerBlockComponent(OKMChainComponent.INSTANCE, Block.class); }}Fabric entrypoint:
{ "entrypoints": { "jade": ["your.package.OKMJadePlugin"] }}服务端记录连锁数据:
public record ChainStats(int count, String shape) {}
public final class ChainStatsCache { private static final Map<UUID, ChainStats> LAST = new ConcurrentHashMap<>();
public static void init() { ChainEvents.registerPostActionListener(event -> { var cfg = ConfigManager.getConfig(); LAST.put(event.getPlayer().getUUID(), new ChainStats(event.getResult().totalCount(), cfg.shapeMode.name())); }); }
public static ChainStats get(ServerPlayer player) { return LAST.get(player.getUUID()); }}Jade 同步与渲染:
public class OKMChainDataProvider implements StreamServerDataProvider<BlockAccessor, ChainStats> { public static final OKMChainDataProvider INSTANCE = new OKMChainDataProvider();
@Override public @Nullable ChainStats streamData(BlockAccessor accessor) { return ChainStatsCache.get(accessor.getPlayer()); }
@Override public StreamCodec<RegistryFriendlyByteBuf, ChainStats> streamCodec() { return ChainStatsCodec.CODEC; }
@Override public ResourceLocation getUid() { return new ResourceLocation("onekeyminer", "chain_stats"); }}
public class OKMChainComponent implements IBlockComponentProvider { public static final OKMChainComponent INSTANCE = new OKMChainComponent();
@Override public void appendTooltip(ITooltip tooltip, BlockAccessor accessor, IPluginConfig config) { Optional<ChainStats> stats = OKMChainDataProvider.INSTANCE.decodeFromData(accessor); stats.ifPresent(s -> { tooltip.add(Component.literal("连锁数量: " + s.count())); tooltip.add(Component.literal("搜索形状: " + s.shape())); }); }
@Override public ResourceLocation getUid() { return new ResourceLocation("onekeyminer", "chain_stats"); }}注意:
ChainStats的序列化需自行实现 Codec。
OneKeyMinerAPI
Section titled “OneKeyMinerAPI”| 方法 | 描述 |
|---|---|
registerBlock(String) | 通过 ID 注册方块到白名单 |
registerBlock(Block) | 通过实例注册方块到白名单 |
registerBlockTag(String) | 注册方块标签到白名单 |
unregisterBlock(String) | 从白名单移除方块 |
blacklistBlock(String) | 将方块添加到黑名单 |
blacklistBlock(Block) | 将方块实例添加到黑名单 |
unblacklistBlock(String) | 从黑名单移除方块 |
whitelistTool(String) | 将工具添加到挖掘白名单(支持 #tag 格式) |
blacklistTool(String) | 将工具添加到挖掘黑名单(支持 #tag 格式) |
whitelistInteractionTool(String) | 将工具添加到交互白名单(支持 #tag 格式) |
blacklistInteractionTool(String) | 将工具添加到交互黑名单(支持 #tag 格式) |
registerToolAction(String, ToolTargetType, ChainActionType, InteractionRule, List<String>) | 注册自定义工具动作规则 |
registerInteractionToolRule(String, ToolTargetType, InteractionRule, String...) | 注册交互工具规则 |
registerMiningToolRule(String, String...) | 注册挖掘工具规则 |
registerEntityShearingRule(String, String...) | 注册实体剪羊毛规则 |
findToolActionForBlock(ItemStack, BlockState) | 查询方块上的工具规则 |
findToolActionForEntity(ItemStack, Entity) | 查询实体上的工具规则 |
hasToolActionRule(ItemStack, ChainActionType) | 判断工具是否有规则 |
whitelistPlantable(String) | 将物品添加到可种植白名单(支持 #tag 格式) |
blacklistPlantable(String) | 将物品添加到可种植黑名单(支持 #tag 格式) |
addBlockToGroup(String, String) | 将方块添加到分组 |
areBlocksInSameGroup(Block, Block) | 检查方块是否在同一组 |
blocksShareTag(BlockState, BlockState) | 检查方块是否共享任何标签 |
isBlockAllowed(Block) | 检查方块是否允许连锁 |
isBlockBlacklisted(Block) | 检查方块是否在黑名单中 |
isToolAllowed(ItemStack) | 检查工具是否允许挖掘 |
isInteractionToolAllowed(ItemStack) | 检查工具是否允许交互 |
isPlantableAllowed(ItemStack) | 检查物品是否可种植 |
getSelectedShape() | 获取当前挖矿形状名称 |
setSelectedShape(String) | 设置挖矿形状(自动触发网络同步) |
isTeleportDropsEnabled() | 获取是否启用传送掉落物 |
setTeleportDropsEnabled(boolean) | 设置是否传送掉落物(自动触发网络同步) |
isTeleportExpEnabled() | 获取是否启用传送经验 |
setTeleportExpEnabled(boolean) | 设置是否传送经验(自动触发网络同步) |
addConfigChangeListener(Consumer) | 注册配置变更监听器 |
removeConfigChangeListener(Consumer) | 移除配置变更监听器 |
ChainActionContext.Builder
Section titled “ChainActionContext.Builder”| 方法 | 描述 |
|---|---|
player(ServerPlayer) | 设置玩家 |
level(Level) | 设置世界 |
originPos(BlockPos) | 设置起始位置 |
originState(BlockState) | 设置起始方块状态 |
actionType(ChainActionType) | 设置操作类型 |
heldItem(ItemStack) | 设置手持物品 |
hand(InteractionHand) | 设置交互手 |
config(MinerConfig) | 设置自定义配置(可选) |
build() | 构建上下文 |
ChainActionResult
Section titled “ChainActionResult”| 方法 | 描述 |
|---|---|
actionType() | 获取操作类型 |
successPositions() | 获取已处理位置的集合 |
totalCount() | 获取处理的总方块数 |
durabilityUsed() | 获取消耗的耐久度 |
hungerUsed() | 获取消耗的饥饿值 |
stopReason() | 获取停止原因 |
collectedDrops() | 获取收集的掉落物列表(挖矿/收割时有数据) |
experienceCollected() | 获取收集的经验值(挖矿时有数据) |
isSuccess() | 检查是否至少处理了一个方块 |
getSummary() | 获取摘要字符串 |
PreActionEvent
Section titled “PreActionEvent”| 方法 | 描述 |
|---|---|
getPlayer() | 获取执行操作的玩家 |
getLevel() | 获取世界实例 |
getOriginPos() | 获取起始方块位置 |
getOriginState() | 获取起始方块状态 |
getActionType() | 获取操作类型 |
getHeldItem() | 获取手持物品 |
getTargetPositions() | 获取目标位置集合(可修改) |
isCancelled() | 检查是否已取消 |
cancel() | 取消操作 |
setCancelled(boolean) | 设置取消状态 |
PostActionEvent
Section titled “PostActionEvent”| 方法 | 描述 |
|---|---|
getPlayer() | 获取执行操作的玩家 |
getLevel() | 获取世界实例 |
getOriginPos() | 获取起始方块位置 |
getActionType() | 获取操作类型 |
getResult() | 获取操作结果 |
getCollectedDrops() | 便捷方法:获取收集的掉落物 |
getExperienceCollected() | 便捷方法:获取收集的经验值 |
1. 检查模组是否存在
Section titled “1. 检查模组是否存在”在调用 OneKeyMiner API 之前,始终检查模组是否已加载:
if (Platform.isModLoaded("onekeyminer")) { // 安全调用 OneKeyMiner API}2. 优先使用标签而非单个方块
Section titled “2. 优先使用标签而非单个方块”优先注册方块标签而非单个方块,以获得更好的兼容性:
// 推荐 - 与模组兼容性好OneKeyMinerAPI.registerBlockTag("#c:ores");
// 不太理想 - 只适用于原版OneKeyMinerAPI.registerBlock("minecraft:diamond_ore");3. 尊重事件取消
Section titled “3. 尊重事件取消”处理 PreActionEvent 时,尊重其他模组是否已取消:
ChainEvents.registerPreActionListener(event -> { if (event.isCancelled()) return; // 尊重其他模组
// 你的逻辑});4. 线程安全
Section titled “4. 线程安全”API 方法是线程安全的,但要注意你自己的数据结构:
// 使用 ConcurrentHashMap 进行线程安全存储private static final Map<UUID, Data> playerData = new ConcurrentHashMap<>();5. 最小化事件处理器工作
Section titled “5. 最小化事件处理器工作”保持事件处理器轻量 - 延迟重型工作:
ChainEvents.registerPostActionListener(event -> { // 不要在这里做重型工作 // 而是将其排队稍后处理 scheduler.schedule(() -> processStats(event));});6. 模组初始化时机
Section titled “6. 模组初始化时机”在模组的主初始化方法中注册内容,确保 OneKeyMiner 已加载:
// Fabric@Overridepublic void onInitialize() { if (FabricLoader.getInstance().isModLoaded("onekeyminer")) { MyOKMIntegration.init(); }}
// Forge/NeoForge@SubscribeEventpublic void onCommonSetup(FMLCommonSetupEvent event) { if (ModList.get().isLoaded("onekeyminer")) { event.enqueueWork(MyOKMIntegration::init); }}Q: 如何让我的自定义矿石支持连锁挖矿?
Section titled “Q: 如何让我的自定义矿石支持连锁挖矿?”A: 可以在你的 data 文件中将方块添加到支持的标签(如 #c:ores),或者在模组初始化时调用 OneKeyMinerAPI.registerBlock()。
Q: 我可以在操作过程中修改哪些方块会被连锁挖掘吗?
Section titled “Q: 我可以在操作过程中修改哪些方块会被连锁挖掘吗?”A: 可以,使用 PreActionEvent.getTargetPositions() 来修改目标集合。
Q: 为什么我的方块没有被连锁挖掘?
Section titled “Q: 为什么我的方块没有被连锁挖掘?”A: 检查以下几点:
- 方块是否通过 API 或标签注册?
- 方块是否在黑名单中?
- 工具是否有效?
- 玩家是否按住了激活键?
Q: 我可以通过代码触发连锁挖矿吗?
Section titled “Q: 我可以通过代码触发连锁挖矿吗?”A: 可以,创建一个 ChainActionContext 并调用 ChainActionLogic.execute()。
Q: 事件监听器的执行顺序是什么?
Section titled “Q: 事件监听器的执行顺序是什么?”A: 按注册顺序执行。如果多个模组注册了监听器,它们按注册顺序依次执行。
Q: API 是向后兼容的吗?
Section titled “Q: API 是向后兼容的吗?”A: 是的,API 保持向后兼容。次版本更新不会破坏现有集成。
- GitHub Issues: https://github.com/Mai-xiyu/OneKeyMiner/issues
- 源代码: https://github.com/Mai-xiyu/OneKeyMiner
文档版本: 1.7.0 | 最后更新: 2026年1月
本项目采用 All Rights Reserved (ARR) 协议。未经作者许可,不得复制、修改或分发本项目代码。