API Reference
OneKeyMiner API Developer Documentation
Section titled “OneKeyMiner API Developer Documentation”Table of Contents
Section titled “Table of Contents”- Introduction
- Getting Started
- Core API
- Chain Action System
- Event System
- Tag System
- Platform Services
- Configuration Access
- Code Examples
- Class Reference
- Best Practices
- FAQ
- Support
Introduction
Section titled “Introduction”OneKeyMiner is a multi-platform chain operation mod supporting Fabric, NeoForge, and Forge. This documentation is intended for mod developers who wish to integrate with or extend OneKeyMiner’s functionality.
Key Features
Section titled “Key Features”- Three Chain Operation Types: Mining, Interaction, and Planting
- Block/Item Whitelist/Blacklist API: Register custom blocks and tools
- Event System: Listen to pre/post operation events for custom logic
- Tag System: Support for
#namespace:tagformat tag matching - Platform Abstraction: Multi-platform support via SPI mechanism
- BFS Algorithm: Breadth-first search prevents stack overflow
- Thread-Safe API: All API methods are thread-safe
Supported Platforms
Section titled “Supported Platforms”| Platform | Package Name | Min Version |
|---|---|---|
| Fabric | onekeyminer-fabric | 0.15.0+ |
| NeoForge | onekeyminer-neoforge | 21.0+ |
| Forge | onekeyminer-forge | 59.0+ |
Getting Started
Section titled “Getting Started”Adding Dependency
Section titled “Adding Dependency”Fabric (build.gradle)
Section titled “Fabric (build.gradle)”repositories { maven { name = "OneKeyMiner" url = "https://maven.example.com/releases" }}
dependencies { // Full implementation (recommended) modImplementation "org.xiyu:onekeyminer-fabric:${onekeyminer_version}"
// API only (compile-time dependency) 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}")}Checking Mod Presence
Section titled “Checking Mod Presence”Before calling OneKeyMiner API, ensure the mod is loaded:
// Fabricif (FabricLoader.getInstance().isModLoaded("onekeyminer")) { MyModIntegration.init();}
// Forge/NeoForgeif (ModList.get().isLoaded("onekeyminer")) { MyModIntegration.init();}Core API
Section titled “Core API”OneKeyMinerAPI Class
Section titled “OneKeyMinerAPI Class”org.xiyu.onekeyminer.api.OneKeyMinerAPI is the main API entry point. All methods are static and thread-safe.
import org.xiyu.onekeyminer.api.OneKeyMinerAPI;Block Whitelist/Blacklist
Section titled “Block Whitelist/Blacklist”Adding Blocks to Whitelist
Section titled “Adding Blocks to Whitelist”// Register by resource location stringOneKeyMinerAPI.registerBlock("mymod:custom_ore");
// Register by Block instanceOneKeyMinerAPI.registerBlock(MyBlocks.CUSTOM_ORE);
// Register all blocks in a tagOneKeyMinerAPI.registerBlockTag("#mymod:custom_ores");OneKeyMinerAPI.registerBlockTag("#c:ores"); // Common tag
// Remove from whitelistOneKeyMinerAPI.unregisterBlock("mymod:custom_ore");Adding Blocks to Blacklist
Section titled “Adding Blocks to Blacklist”Blacklist has higher priority than whitelist.
// Add to blacklistOneKeyMinerAPI.blacklistBlock("minecraft:bedrock");OneKeyMinerAPI.blacklistBlock(Blocks.SPAWNER);
// Remove from blacklistOneKeyMinerAPI.unblacklistBlock("minecraft:bedrock");Tool Whitelist/Blacklist
Section titled “Tool Whitelist/Blacklist”Mining Tools
Section titled “Mining Tools”// Add to mining tool whitelistOneKeyMinerAPI.whitelistTool("mymod:custom_pickaxe");OneKeyMinerAPI.whitelistTool("#mymod:pickaxes"); // Using tag
// Add to mining tool blacklistOneKeyMinerAPI.blacklistTool("minecraft:wooden_pickaxe");Interaction Tools
Section titled “Interaction Tools”// Add to interaction tool whitelist (shears, hoes, axes for stripping, etc.)OneKeyMinerAPI.whitelistInteractionTool("mymod:magic_shears");OneKeyMinerAPI.whitelistInteractionTool("#c:shears");
// Add to interaction tool blacklistOneKeyMinerAPI.blacklistInteractionTool("mymod:broken_shears");Custom Tool Action Rules
Section titled “Custom Tool Action Rules”Bind a specific tool to chain actions on blocks or entities, and optionally limit targets:
// Entity shearing rule (only sheep)OneKeyMinerAPI.registerEntityShearingRule( "mymod:golden_shears", "minecraft:sheep");
// Block interaction rule (hoe-like behavior on dirt)OneKeyMinerAPI.registerInteractionToolRule( "mymod:terra_hoe", OneKeyMinerAPI.ToolTargetType.BLOCK, OneKeyMinerAPI.InteractionRule.TILLING, "#minecraft:dirt");
// Mining rule (trigger chain mining on right-click for specific blocks)OneKeyMinerAPI.registerMiningToolRule( "mymod:ore_wand", "#c:ores");Plantable Items
Section titled “Plantable Items”// Add to plantable whitelistOneKeyMinerAPI.whitelistPlantable("mymod:magic_seeds");OneKeyMinerAPI.whitelistPlantable("#c:seeds");
// Add to plantable blacklistOneKeyMinerAPI.blacklistPlantable("mymod:cursed_seeds");Block Grouping
Section titled “Block Grouping”Block groups allow different block types to be chain-mined together (useful for “loose matching” mode).
// Add blocks to the same groupOneKeyMinerAPI.addBlockToGroup("mymod:copper_ore", "copper");OneKeyMinerAPI.addBlockToGroup("mymod:deepslate_copper_ore", "copper");OneKeyMinerAPI.addBlockToGroup("minecraft:copper_ore", "copper");
// Check if two blocks are in the same groupboolean sameGroup = OneKeyMinerAPI.areBlocksInSameGroup(block1, block2);
// Check if two blocks share any tagboolean shareTag = OneKeyMinerAPI.blocksShareTag(state1, state2);Query Methods
Section titled “Query Methods”// Check if a block is allowed for chain miningboolean allowed = OneKeyMinerAPI.isBlockAllowed(block);
// Check if a block is blacklistedboolean blacklisted = OneKeyMinerAPI.isBlockBlacklisted(block);
// Check if a tool is allowed for chain miningboolean toolAllowed = OneKeyMinerAPI.isToolAllowed(itemStack);
// Check if a tool is allowed for chain interactionboolean interactionAllowed = OneKeyMinerAPI.isInteractionToolAllowed(itemStack);
// Check if an item is plantableboolean plantable = OneKeyMinerAPI.isPlantableAllowed(itemStack);
// Access read-only setsSet<String> whitelist = OneKeyMinerAPI.BLOCK_WHITELIST;Set<String> blacklist = OneKeyMinerAPI.BLOCK_BLACKLIST;Set<String> toolWhitelist = OneKeyMinerAPI.TOOL_WHITELIST;Set<String> toolBlacklist = OneKeyMinerAPI.TOOL_BLACKLIST;Chain Action System
Section titled “Chain Action System”ChainActionType Enum
Section titled “ChainActionType Enum”Defines the three types of chain operations:
import org.xiyu.onekeyminer.chain.ChainActionType;
ChainActionType.MINING // Chain mining (breaking blocks)ChainActionType.INTERACTION // Chain interaction (shearing, hoeing, stripping, etc.)ChainActionType.PLANTING // Chain planting (planting seeds on farmland)ChainActionType Methods
Section titled “ChainActionType Methods”ChainActionType type = ChainActionType.MINING;
// Get display name (for UI)String name = type.getDisplayName(); // "Mining"
// Get ID (for serialization)String id = type.getId(); // "mining"
// Parse from stringChainActionType parsed = ChainActionType.fromId("mining");ChainActionContext
Section titled “ChainActionContext”The context object contains all information needed to execute a chain operation. Use the Builder pattern to create:
import org.xiyu.onekeyminer.chain.ChainActionContext;
// Quick creation for miningChainActionContext miningContext = ChainActionContext.forMining(player, level, pos, state) .build();
// Quick creation for interactionChainActionContext interactionContext = ChainActionContext.forInteraction( player, level, pos, state, heldItem, hand) .build();
// Quick creation for plantingChainActionContext plantingContext = ChainActionContext.forPlanting( player, level, pos, heldItem, hand) .build();
// Full Builder usageChainActionContext context = ChainActionContext.builder() .player(player) .level(level) .originPos(pos) .originState(state) .actionType(ChainActionType.MINING) .heldItem(player.getMainHandItem()) .hand(InteractionHand.MAIN_HAND) .config(customConfig) // Optional: use custom config .build();ChainActionContext Accessors
Section titled “ChainActionContext Accessors”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”The result object returned after executing a chain operation:
import org.xiyu.onekeyminer.chain.ChainActionResult;
ChainActionResult result = ChainActionLogic.execute(context);
// Check successboolean success = result.isSuccess();
// Get statisticsint totalCount = result.totalCount(); // Total blocks processedint durabilityUsed = result.durabilityUsed(); // Durability consumedint hungerUsed = result.hungerUsed(); // Hunger consumedSet<BlockPos> positions = result.successPositions(); // All processed positions
// Get stop reasonChainActionResult.StopReason reason = result.stopReason();
// Get summary string (for logging/display)String summary = result.getSummary();// Example: "Mining: 32 blocks, stopped: MAX_BLOCKS"StopReason Enum
Section titled “StopReason Enum”| Value | Description |
|---|---|
COMPLETED | Normal completion (all blocks processed) |
MAX_BLOCKS | Reached maximum block limit |
MAX_DISTANCE | Reached maximum distance limit |
LOW_DURABILITY | Tool durability too low |
TOOL_BROKEN | Tool broke during operation |
LOW_HUNGER | Player hunger too low |
TIMEOUT | Operation timeout (anti-lag protection) |
EVENT_CANCELLED | Cancelled by event listener |
NO_VALID_BLOCKS | No valid blocks found |
ERROR | An error occurred |
ChainActionLogic
Section titled “ChainActionLogic”The core logic class for executing chain operations:
import org.xiyu.onekeyminer.chain.ChainActionLogic;
// Execute with contextChainActionResult result = ChainActionLogic.execute(context);
// Convenience method for block break eventsChainActionResult miningResult = ChainActionLogic.onBlockBreak( player, level, pos, state);
// Check if chain mining should trigger (without executing)boolean shouldTrigger = ChainActionLogic.shouldTriggerChainMining( player, level, pos, state);Event System
Section titled “Event System”OneKeyMiner provides an event system for external mods to listen to and intervene in chain operations.
PreActionEvent
Section titled “PreActionEvent”Triggered before a chain operation starts. Cancellable.
import org.xiyu.onekeyminer.api.event.PreActionEvent;
public class PreActionEvent { // Get player performing the action ServerPlayer getPlayer();
// Get world instance Level getLevel();
// Get origin block position BlockPos getOriginPos();
// Get origin block state BlockState getOriginState();
// Get action type ChainActionType getActionType();
// Get held item ItemStack getHeldItem();
// Get target positions (MODIFIABLE - add/remove positions) Set<BlockPos> getTargetPositions();
// Check if cancelled boolean isCancelled();
// Cancel the operation void cancel();
// Set cancelled state void setCancelled(boolean cancelled);}PostActionEvent
Section titled “PostActionEvent”Triggered after a chain operation completes. NOT cancellable - for information only.
import org.xiyu.onekeyminer.api.event.PostActionEvent;
public class PostActionEvent { // Get player who performed the action ServerPlayer getPlayer();
// Get world instance Level getLevel();
// Get origin block position BlockPos getOriginPos();
// Get action type ChainActionType getActionType();
// Get operation result ChainActionResult getResult();}Event Registration
Section titled “Event Registration”import org.xiyu.onekeyminer.api.event.ChainEvents;
// Register pre-action listener (all types)ChainEvents.registerPreActionListener(event -> { if (isProtectedArea(event.getOriginPos())) { event.cancel(); event.getPlayer().sendSystemMessage( Component.literal("Chain operations disabled in this area!")); }});
// Register for specific action type onlyChainEvents.registerPreActionListener(ChainActionType.MINING, event -> { // Only handles mining events});
// Register with conditionChainEvents.registerPreActionListener( event -> event.getPlayer().hasPermissions(2), // Condition: OP only event -> { // Only runs for operators });
// Register post-action listenerChainEvents.registerPostActionListener(event -> { LOGGER.info("Player {} processed {} blocks via {}", event.getPlayer().getName().getString(), event.getResult().totalCount(), event.getActionType().getDisplayName());});
// Register for specific typeChainEvents.registerPostActionListener(ChainActionType.PLANTING, event -> { // Only handles planting events});
// Unregister listener (keep reference)var listener = ChainEvents.registerPreActionListener(event -> { /* ... */ });ChainEvents.unregisterPreActionListener(listener);Tag System
Section titled “Tag System”TagResolver Class
Section titled “TagResolver Class”OneKeyMiner uses TagResolver for pattern matching on blocks and items.
import org.xiyu.onekeyminer.registry.TagResolver;
// Get singleton instanceTagResolver resolver = TagResolver.getInstance();
// Check if block matches patternboolean matches = resolver.matchesBlock(blockState, "#minecraft:logs");
// Check if item matches patternboolean matches = resolver.matchesItem(itemStack, "#c:shears");
// Check if two blocks share any tagboolean shareTag = resolver.blocksShareTag(state1, state2);
// Validate pattern formatboolean valid = resolver.isValidPattern("#minecraft:logs");
// Clear cache (call after config changes)resolver.clearCache();Tag Formats
Section titled “Tag Formats”| Format | Description | Example |
|---|---|---|
#namespace:tag | Tag reference | #minecraft:logs, #c:ores |
namespace:id | Direct resource location | minecraft:diamond_ore |
*pattern* | Contains match | *ore* |
pattern* | Prefix match | minecraft:* |
*pattern | Suffix match | *_ore |
Conventional Tags Across Platforms
Section titled “Conventional Tags Across Platforms”Conventional tag namespace differs across platforms. OneKeyMiner adapts automatically at runtime via PlatformServices.getConventionalTagPrefix():
| Platform | Conventional Tag Prefix | Example |
|---|---|---|
| Fabric | c | #c:ores, #c:seeds |
| NeoForge | c | #c:ores, #c:seeds |
| Forge | forge | #forge:ores, #forge:seeds |
Note: When using tags in config files and API calls, you don’t need to care about platform differences — OneKeyMiner will use the correct prefix for the current platform. If you need to build a conventional tag in code, call
PlatformServices.getInstance().getConventionalTagPrefix().
Platform Services
Section titled “Platform Services”PlatformServices Interface
Section titled “PlatformServices Interface”OneKeyMiner uses a platform abstraction layer for cross-platform compatibility.
import org.xiyu.onekeyminer.platform.PlatformServices;
// Get platform instancePlatformServices platform = PlatformServices.getInstance();
// Get platform nameString name = platform.getPlatformName(); // "Fabric", "NeoForge", or "Forge"
// Check if mod is loadedboolean loaded = platform.isModLoaded("mymod");
// Get config directoryPath configDir = platform.getConfigDirectory();
// Check/set chain mode for playerboolean active = platform.isChainModeActive(player);platform.setChainModeActive(player, true);
// Get conventional tag prefix for current platformString prefix = platform.getConventionalTagPrefix(); // Fabric/NeoForge="c", Forge="forge"Platform-Specific Implementations
Section titled “Platform-Specific Implementations”Each platform has its own implementation:
FabricPlatformServices- Fabric implementationNeoForgePlatformServices- NeoForge implementationForgePlatformServices- Forge implementation
The correct implementation is loaded automatically via SPI (Service Provider Interface).
Configuration Access
Section titled “Configuration Access”MinerConfig Class
Section titled “MinerConfig Class”Access the current configuration:
import org.xiyu.onekeyminer.config.MinerConfig;import org.xiyu.onekeyminer.config.ConfigManager;
// Get current configMinerConfig config = ConfigManager.getConfig();
// Access config valuesboolean 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;Runtime Config Modification
Section titled “Runtime Config Modification”// Reload config from fileConfigManager.reload();
// Save current config to fileConfigManager.save();Config API (for Addons)
Section titled “Config API (for Addons)”Addons can use config read/write methods provided by OneKeyMinerAPI. After modification, changes are automatically synced to the server via network:
import org.xiyu.onekeyminer.api.OneKeyMinerAPI;
// Read configString shape = OneKeyMinerAPI.getSelectedShape(); // Current mining shapeboolean drops = OneKeyMinerAPI.isTeleportDropsEnabled(); // Teleport drops?boolean exp = OneKeyMinerAPI.isTeleportExpEnabled(); // Teleport XP?
// Modify config (auto network sync)OneKeyMinerAPI.setSelectedShape("SPHERE");OneKeyMinerAPI.setTeleportDropsEnabled(true);OneKeyMinerAPI.setTeleportExpEnabled(false);
// Listen for config changesOneKeyMinerAPI.addConfigChangeListener(key -> { LOGGER.info("Config changed: {}", key);});Code Examples
Section titled “Code Examples”Example 1: Basic Integration
Section titled “Example 1: Basic Integration”package com.example.mymod;
import org.xiyu.onekeyminer.api.OneKeyMinerAPI;
public class MyModIntegration {
public static void init() { // Check if OneKeyMiner is loaded if (!isModLoaded("onekeyminer")) { return; }
registerContent(); }
private static void registerContent() { // Register custom ores OneKeyMinerAPI.registerBlock("mymod:mythril_ore"); OneKeyMinerAPI.registerBlock("mymod:deepslate_mythril_ore");
// Or use tag (recommended) OneKeyMinerAPI.registerBlockTag("#mymod:mythril_ores");
// Add to same group for loose matching OneKeyMinerAPI.addBlockToGroup("mymod:mythril_ore", "mythril"); OneKeyMinerAPI.addBlockToGroup("mymod:deepslate_mythril_ore", "mythril");
// Register custom tools OneKeyMinerAPI.whitelistTool("mymod:mythril_pickaxe"); OneKeyMinerAPI.whitelistInteractionTool("mymod:mythril_shears");
// Register custom seeds OneKeyMinerAPI.whitelistPlantable("mymod:magic_seeds"); }}Example 2: Region Protection Integration
Section titled “Example 2: Region Protection Integration”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();
// Check if origin is in protected area if (ProtectionAPI.isProtected(origin, player)) { event.cancel(); player.sendSystemMessage( Component.literal("§cChain operations are disabled in protected areas!")); return; }
// Remove protected blocks from target list event.getTargetPositions().removeIf(pos -> ProtectionAPI.isProtected(pos, player) );
// Limit to claim boundaries event.getTargetPositions().removeIf(pos -> !ProtectionAPI.isInSameClaim(origin, pos, player) ); }}Example 3: Statistics Tracking
Section titled “Example 3: Statistics Tracking”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);
// Achievement check 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; }}Example 4: Custom Tool Logic
Section titled “Example 4: Custom Tool Logic”package com.example.custom;
import org.xiyu.onekeyminer.api.event.ChainEvents;import org.xiyu.onekeyminer.chain.ChainActionType;
public class CustomToolHandler {
public static void init() { // Modify behavior for custom tools ChainEvents.registerPreActionListener(ChainActionType.MINING, event -> { var player = event.getPlayer(); var item = player.getMainHandItem();
// Check for enchantment or NBT if (item.is(MyItems.MINING_LASER)) { // Mining laser has no block limit // (handled by custom logic) }
// Check for special modifier if (item.getEnchantmentLevel(MyEnchantments.EXCAVATION) > 0) { // Double the targets int level = item.getEnchantmentLevel(MyEnchantments.EXCAVATION); // Custom expansion logic... } });
ChainEvents.registerPostActionListener(ChainActionType.MINING, event -> { var item = event.getPlayer().getMainHandItem();
// Trigger special effects if (item.is(MyItems.FORTUNE_AMPLIFIER)) { // Apply bonus drops applyFortune(event.getResult().successPositions(), event.getPlayer()); } }); }}Example 5: Addon Development
Section titled “Example 5: Addon Development”Example structure for a OneKeyMiner addon mod:
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() { // Register addon blocks/tools registerContent();
// Setup event listeners setupEventListeners();
// Optional: Setup client-side rendering if (isClientSide()) { OKMAddonClient.init(); } }
private static void registerContent() { // Register addon's custom ores OneKeyMinerAPI.registerBlockTag("#okm_addon:custom_ores");
// Register addon's custom tools OneKeyMinerAPI.whitelistTool("#okm_addon:enhanced_pickaxes"); }
private static void setupEventListeners() { // Pre-action: Customize target set ChainEvents.registerPreActionListener(event -> { if (event.getActionType() == ChainActionType.MINING) { // Example: Remove disallowed targets event.getTargetPositions().removeIf(pos -> isForbidden(event.getPlayer().level(), pos) ); } });
// Post-action: Custom sound/particle effects ChainEvents.registerPostActionListener(event -> { if (event.getResult().totalCount() > 10) { playChainMiningSound(event.getPlayer(), event.getOriginPos()); spawnChainMiningParticles(event.getResult().successPositions()); } }); }}Example 6: Jade Integration (Show Chain Info)
Section titled “Example 6: Jade Integration (Show Chain Info)”Show the last chain result in Jade (total blocks + current shape).
Register plugin (Fabric entrypoint required):
@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"] }}Collect chain stats on server:
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()); }}Sync and render with 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("Chain: " + s.count())); tooltip.add(Component.literal("Shape: " + s.shape())); }); }
@Override public ResourceLocation getUid() { return new ResourceLocation("onekeyminer", "chain_stats"); }}Note: Use your own codec to serialize
ChainStats.
Class Reference
Section titled “Class Reference”OneKeyMinerAPI
Section titled “OneKeyMinerAPI”| Method | Description |
|---|---|
registerBlock(String) | Register block to whitelist by ID |
registerBlock(Block) | Register block instance to whitelist |
registerBlockTag(String) | Register block tag to whitelist |
unregisterBlock(String) | Remove block from whitelist |
blacklistBlock(String) | Add block to blacklist |
blacklistBlock(Block) | Add block instance to blacklist |
unblacklistBlock(String) | Remove block from blacklist |
whitelistTool(String) | Add tool to mining whitelist |
blacklistTool(String) | Add tool to mining blacklist |
whitelistInteractionTool(String) | Add tool to interaction whitelist |
blacklistInteractionTool(String) | Add tool to interaction blacklist |
registerToolAction(String, ToolTargetType, ChainActionType, InteractionRule, List<String>) | Register custom tool action rule |
registerInteractionToolRule(String, ToolTargetType, InteractionRule, String...) | Register interaction tool rule |
registerMiningToolRule(String, String...) | Register mining tool rule |
registerEntityShearingRule(String, String...) | Register entity shearing rule |
findToolActionForBlock(ItemStack, BlockState) | Find tool rule for block |
findToolActionForEntity(ItemStack, Entity) | Find tool rule for entity |
hasToolActionRule(ItemStack, ChainActionType) | Check if tool has custom rule |
whitelistPlantable(String) | Add item to plantable whitelist |
blacklistPlantable(String) | Add item to plantable blacklist |
addBlockToGroup(String, String) | Add block to group |
areBlocksInSameGroup(Block, Block) | Check if blocks are in same group |
blocksShareTag(BlockState, BlockState) | Check if blocks share any tag |
isBlockAllowed(Block) | Check if block is allowed |
isBlockBlacklisted(Block) | Check if block is blacklisted |
isToolAllowed(ItemStack) | Check if tool is allowed for mining |
isInteractionToolAllowed(ItemStack) | Check if tool is allowed for interaction |
isPlantableAllowed(ItemStack) | Check if item is plantable |
getSelectedShape() | Get currently selected mining shape name |
setSelectedShape(String) | Set mining shape (auto network sync) |
isTeleportDropsEnabled() | Get whether teleport drops is enabled |
setTeleportDropsEnabled(boolean) | Set whether teleport drops is enabled (auto network sync) |
isTeleportExpEnabled() | Get whether teleport XP is enabled |
setTeleportExpEnabled(boolean) | Set whether teleport XP is enabled (auto network sync) |
addConfigChangeListener(Consumer) | Register config change listener |
removeConfigChangeListener(Consumer) | Remove config change listener |
ChainActionContext.Builder
Section titled “ChainActionContext.Builder”| Method | Description |
|---|---|
player(ServerPlayer) | Set the player |
level(Level) | Set the world |
originPos(BlockPos) | Set origin position |
originState(BlockState) | Set origin block state |
actionType(ChainActionType) | Set action type |
heldItem(ItemStack) | Set held item |
hand(InteractionHand) | Set interaction hand |
config(MinerConfig) | Set custom config (optional) |
build() | Build the context |
ChainActionResult
Section titled “ChainActionResult”| Method | Description |
|---|---|
actionType() | Get action type |
successPositions() | Get set of processed positions |
totalCount() | Get total blocks processed |
durabilityUsed() | Get durability consumed |
hungerUsed() | Get hunger consumed |
stopReason() | Get stop reason |
collectedDrops() | Get collected drops (mining/harvesting only) |
experienceCollected() | Get collected experience (mining only) |
isSuccess() | Check if at least one block was processed |
getSummary() | Get summary string |
PreActionEvent
Section titled “PreActionEvent”| Method | Description |
|---|---|
getPlayer() | Get the player performing the action |
getLevel() | Get the world instance |
getOriginPos() | Get origin block position |
getOriginState() | Get origin block state |
getActionType() | Get action type |
getHeldItem() | Get held item |
getTargetPositions() | Get target position set (modifiable) |
isCancelled() | Check if cancelled |
cancel() | Cancel the action |
setCancelled(boolean) | Set cancelled state |
PostActionEvent
Section titled “PostActionEvent”| Method | Description |
|---|---|
getPlayer() | Get the player performing the action |
getLevel() | Get the world instance |
getOriginPos() | Get origin block position |
getActionType() | Get action type |
getResult() | Get action result |
getCollectedDrops() | Convenience: get collected drops |
getExperienceCollected() | Convenience: get collected experience |
Best Practices
Section titled “Best Practices”1. Check Mod Presence
Section titled “1. Check Mod Presence”Always check if OneKeyMiner is loaded before calling its API:
if (Platform.isModLoaded("onekeyminer")) { // Safe to call OneKeyMiner API}2. Use Tags Over Individual Blocks
Section titled “2. Use Tags Over Individual Blocks”Prefer registering block tags over individual blocks for better compatibility:
// Good - works with mod compatOneKeyMinerAPI.registerBlockTag("#c:ores");
// Less ideal - only works for vanillaOneKeyMinerAPI.registerBlock("minecraft:diamond_ore");3. Respect Event Cancellation
Section titled “3. Respect Event Cancellation”When handling PreActionEvent, respect if another mod has already cancelled:
ChainEvents.registerPreActionListener(event -> { if (event.isCancelled()) return; // Respect other mods
// Your logic here});4. Thread Safety
Section titled “4. Thread Safety”API methods are thread-safe, but be careful with your own data structures:
// Use ConcurrentHashMap for thread-safe storageprivate static final Map<UUID, Data> playerData = new ConcurrentHashMap<>();5. Minimize Event Handler Work
Section titled “5. Minimize Event Handler Work”Keep event handlers lightweight - defer heavy work:
ChainEvents.registerPostActionListener(event -> { // Don't do heavy work here // Instead, queue it for later scheduler.schedule(() -> processStats(event));});6. Mod Initialization Timing
Section titled “6. Mod Initialization Timing”Register integration content in your mod’s main initialization method, and ensure OneKeyMiner is loaded:
// 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: How do I make my custom ore work with chain mining?
Section titled “Q: How do I make my custom ore work with chain mining?”A: Either add your block to a supported tag (#c:ores) in your data files, or call OneKeyMinerAPI.registerBlock() during mod initialization.
Q: Can I modify which blocks get chain-mined during an operation?
Section titled “Q: Can I modify which blocks get chain-mined during an operation?”A: Yes, use PreActionEvent.getTargetPositions() to modify the target set.
Q: Why isn’t my block being chain-mined?
Section titled “Q: Why isn’t my block being chain-mined?”A: Check:
- Is the block registered via API or tag?
- Is the block in the blacklist?
- Is the tool valid for mining?
- Is the player holding the activation key?
Q: Can I trigger chain mining programmatically?
Section titled “Q: Can I trigger chain mining programmatically?”A: Yes, create a ChainActionContext and call ChainActionLogic.execute().
Q: What is the execution order of event listeners?
Section titled “Q: What is the execution order of event listeners?”A: They run in registration order. If multiple mods register listeners, they are executed one by one in the order they were registered.
Q: Is the API backward compatible?
Section titled “Q: Is the API backward compatible?”A: Yes. The API is kept backward compatible. Minor version updates will not break existing integrations.
Support
Section titled “Support”- GitHub Issues: https://github.com/Mai-xiyu/OneKeyMiner/issues
- Source Code: https://github.com/Mai-xiyu/OneKeyMiner
Documentation Version: 1.7.0 | Last Updated: January 2026
Copyright Notice
Section titled “Copyright Notice”This project is All Rights Reserved (ARR). You may not copy, modify, or distribute the code without permission from the author.