Skip to content

API Reference


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.

  • 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:tag format 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
PlatformPackage NameMin Version
Fabriconekeyminer-fabric0.15.0+
NeoForgeonekeyminer-neoforge21.0+
Forgeonekeyminer-forge59.0+

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}"
}
repositories {
maven {
name = "OneKeyMiner"
url = "https://maven.example.com/releases"
}
}
dependencies {
implementation "org.xiyu:onekeyminer-neoforge:${onekeyminer_version}"
}
repositories {
maven {
name = "OneKeyMiner"
url = "https://maven.example.com/releases"
}
}
dependencies {
implementation fg.deobf("org.xiyu:onekeyminer-forge:${onekeyminer_version}")
}

Before calling OneKeyMiner API, ensure the mod is loaded:

// Fabric
if (FabricLoader.getInstance().isModLoaded("onekeyminer")) {
MyModIntegration.init();
}
// Forge/NeoForge
if (ModList.get().isLoaded("onekeyminer")) {
MyModIntegration.init();
}

org.xiyu.onekeyminer.api.OneKeyMinerAPI is the main API entry point. All methods are static and thread-safe.

import org.xiyu.onekeyminer.api.OneKeyMinerAPI;
// Register by resource location string
OneKeyMinerAPI.registerBlock("mymod:custom_ore");
// Register by Block instance
OneKeyMinerAPI.registerBlock(MyBlocks.CUSTOM_ORE);
// Register all blocks in a tag
OneKeyMinerAPI.registerBlockTag("#mymod:custom_ores");
OneKeyMinerAPI.registerBlockTag("#c:ores"); // Common tag
// Remove from whitelist
OneKeyMinerAPI.unregisterBlock("mymod:custom_ore");

Blacklist has higher priority than whitelist.

// Add to blacklist
OneKeyMinerAPI.blacklistBlock("minecraft:bedrock");
OneKeyMinerAPI.blacklistBlock(Blocks.SPAWNER);
// Remove from blacklist
OneKeyMinerAPI.unblacklistBlock("minecraft:bedrock");
// Add to mining tool whitelist
OneKeyMinerAPI.whitelistTool("mymod:custom_pickaxe");
OneKeyMinerAPI.whitelistTool("#mymod:pickaxes"); // Using tag
// Add to mining tool blacklist
OneKeyMinerAPI.blacklistTool("minecraft:wooden_pickaxe");
// Add to interaction tool whitelist (shears, hoes, axes for stripping, etc.)
OneKeyMinerAPI.whitelistInteractionTool("mymod:magic_shears");
OneKeyMinerAPI.whitelistInteractionTool("#c:shears");
// Add to interaction tool blacklist
OneKeyMinerAPI.blacklistInteractionTool("mymod:broken_shears");

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"
);
// Add to plantable whitelist
OneKeyMinerAPI.whitelistPlantable("mymod:magic_seeds");
OneKeyMinerAPI.whitelistPlantable("#c:seeds");
// Add to plantable blacklist
OneKeyMinerAPI.blacklistPlantable("mymod:cursed_seeds");

Block groups allow different block types to be chain-mined together (useful for “loose matching” mode).

// Add blocks to the same group
OneKeyMinerAPI.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 group
boolean sameGroup = OneKeyMinerAPI.areBlocksInSameGroup(block1, block2);
// Check if two blocks share any tag
boolean shareTag = OneKeyMinerAPI.blocksShareTag(state1, state2);
// Check if a block is allowed for chain mining
boolean allowed = OneKeyMinerAPI.isBlockAllowed(block);
// Check if a block is blacklisted
boolean blacklisted = OneKeyMinerAPI.isBlockBlacklisted(block);
// Check if a tool is allowed for chain mining
boolean toolAllowed = OneKeyMinerAPI.isToolAllowed(itemStack);
// Check if a tool is allowed for chain interaction
boolean interactionAllowed = OneKeyMinerAPI.isInteractionToolAllowed(itemStack);
// Check if an item is plantable
boolean plantable = OneKeyMinerAPI.isPlantableAllowed(itemStack);
// Access read-only sets
Set<String> whitelist = OneKeyMinerAPI.BLOCK_WHITELIST;
Set<String> blacklist = OneKeyMinerAPI.BLOCK_BLACKLIST;
Set<String> toolWhitelist = OneKeyMinerAPI.TOOL_WHITELIST;
Set<String> toolBlacklist = OneKeyMinerAPI.TOOL_BLACKLIST;

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 type = ChainActionType.MINING;
// Get display name (for UI)
String name = type.getDisplayName(); // "Mining"
// Get ID (for serialization)
String id = type.getId(); // "mining"
// Parse from string
ChainActionType parsed = ChainActionType.fromId("mining");

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 mining
ChainActionContext miningContext = ChainActionContext.forMining(player, level, pos, state)
.build();
// Quick creation for interaction
ChainActionContext interactionContext = ChainActionContext.forInteraction(
player, level, pos, state, heldItem, hand)
.build();
// Quick creation for planting
ChainActionContext plantingContext = ChainActionContext.forPlanting(
player, level, pos, heldItem, hand)
.build();
// Full Builder usage
ChainActionContext 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();
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();

The result object returned after executing a chain operation:

import org.xiyu.onekeyminer.chain.ChainActionResult;
ChainActionResult result = ChainActionLogic.execute(context);
// Check success
boolean success = result.isSuccess();
// Get statistics
int totalCount = result.totalCount(); // Total blocks processed
int durabilityUsed = result.durabilityUsed(); // Durability consumed
int hungerUsed = result.hungerUsed(); // Hunger consumed
Set<BlockPos> positions = result.successPositions(); // All processed positions
// Get stop reason
ChainActionResult.StopReason reason = result.stopReason();
// Get summary string (for logging/display)
String summary = result.getSummary();
// Example: "Mining: 32 blocks, stopped: MAX_BLOCKS"
ValueDescription
COMPLETEDNormal completion (all blocks processed)
MAX_BLOCKSReached maximum block limit
MAX_DISTANCEReached maximum distance limit
LOW_DURABILITYTool durability too low
TOOL_BROKENTool broke during operation
LOW_HUNGERPlayer hunger too low
TIMEOUTOperation timeout (anti-lag protection)
EVENT_CANCELLEDCancelled by event listener
NO_VALID_BLOCKSNo valid blocks found
ERRORAn error occurred

The core logic class for executing chain operations:

import org.xiyu.onekeyminer.chain.ChainActionLogic;
// Execute with context
ChainActionResult result = ChainActionLogic.execute(context);
// Convenience method for block break events
ChainActionResult miningResult = ChainActionLogic.onBlockBreak(
player, level, pos, state);
// Check if chain mining should trigger (without executing)
boolean shouldTrigger = ChainActionLogic.shouldTriggerChainMining(
player, level, pos, state);

OneKeyMiner provides an event system for external mods to listen to and intervene in chain operations.

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);
}

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();
}
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 only
ChainEvents.registerPreActionListener(ChainActionType.MINING, event -> {
// Only handles mining events
});
// Register with condition
ChainEvents.registerPreActionListener(
event -> event.getPlayer().hasPermissions(2), // Condition: OP only
event -> {
// Only runs for operators
}
);
// Register post-action listener
ChainEvents.registerPostActionListener(event -> {
LOGGER.info("Player {} processed {} blocks via {}",
event.getPlayer().getName().getString(),
event.getResult().totalCount(),
event.getActionType().getDisplayName());
});
// Register for specific type
ChainEvents.registerPostActionListener(ChainActionType.PLANTING, event -> {
// Only handles planting events
});
// Unregister listener (keep reference)
var listener = ChainEvents.registerPreActionListener(event -> { /* ... */ });
ChainEvents.unregisterPreActionListener(listener);

OneKeyMiner uses TagResolver for pattern matching on blocks and items.

import org.xiyu.onekeyminer.registry.TagResolver;
// Get singleton instance
TagResolver resolver = TagResolver.getInstance();
// Check if block matches pattern
boolean matches = resolver.matchesBlock(blockState, "#minecraft:logs");
// Check if item matches pattern
boolean matches = resolver.matchesItem(itemStack, "#c:shears");
// Check if two blocks share any tag
boolean shareTag = resolver.blocksShareTag(state1, state2);
// Validate pattern format
boolean valid = resolver.isValidPattern("#minecraft:logs");
// Clear cache (call after config changes)
resolver.clearCache();
FormatDescriptionExample
#namespace:tagTag reference#minecraft:logs, #c:ores
namespace:idDirect resource locationminecraft:diamond_ore
*pattern*Contains match*ore*
pattern*Prefix matchminecraft:*
*patternSuffix match*_ore

Conventional tag namespace differs across platforms. OneKeyMiner adapts automatically at runtime via PlatformServices.getConventionalTagPrefix():

PlatformConventional Tag PrefixExample
Fabricc#c:ores, #c:seeds
NeoForgec#c:ores, #c:seeds
Forgeforge#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().


OneKeyMiner uses a platform abstraction layer for cross-platform compatibility.

import org.xiyu.onekeyminer.platform.PlatformServices;
// Get platform instance
PlatformServices platform = PlatformServices.getInstance();
// Get platform name
String name = platform.getPlatformName(); // "Fabric", "NeoForge", or "Forge"
// Check if mod is loaded
boolean loaded = platform.isModLoaded("mymod");
// Get config directory
Path configDir = platform.getConfigDirectory();
// Check/set chain mode for player
boolean active = platform.isChainModeActive(player);
platform.setChainModeActive(player, true);
// Get conventional tag prefix for current platform
String prefix = platform.getConventionalTagPrefix(); // Fabric/NeoForge="c", Forge="forge"

Each platform has its own implementation:

  • FabricPlatformServices - Fabric implementation
  • NeoForgePlatformServices - NeoForge implementation
  • ForgePlatformServices - Forge implementation

The correct implementation is loaded automatically via SPI (Service Provider Interface).


Access the current configuration:

import org.xiyu.onekeyminer.config.MinerConfig;
import org.xiyu.onekeyminer.config.ConfigManager;
// Get current config
MinerConfig config = ConfigManager.getConfig();
// Access config values
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;
// Reload config from file
ConfigManager.reload();
// Save current config to file
ConfigManager.save();

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 config
String shape = OneKeyMinerAPI.getSelectedShape(); // Current mining shape
boolean 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 changes
OneKeyMinerAPI.addConfigChangeListener(key -> {
LOGGER.info("Config changed: {}", key);
});

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");
}
}
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)
);
}
}
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;
}
}
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 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):

@WailaPlugin
public 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.


MethodDescription
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
MethodDescription
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
MethodDescription
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
MethodDescription
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
MethodDescription
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

Always check if OneKeyMiner is loaded before calling its API:

if (Platform.isModLoaded("onekeyminer")) {
// Safe to call OneKeyMiner API
}

Prefer registering block tags over individual blocks for better compatibility:

// Good - works with mod compat
OneKeyMinerAPI.registerBlockTag("#c:ores");
// Less ideal - only works for vanilla
OneKeyMinerAPI.registerBlock("minecraft:diamond_ore");

When handling PreActionEvent, respect if another mod has already cancelled:

ChainEvents.registerPreActionListener(event -> {
if (event.isCancelled()) return; // Respect other mods
// Your logic here
});

API methods are thread-safe, but be careful with your own data structures:

// Use ConcurrentHashMap for thread-safe storage
private static final Map<UUID, Data> playerData = new ConcurrentHashMap<>();

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));
});

Register integration content in your mod’s main initialization method, and ensure OneKeyMiner is loaded:

// Fabric
@Override
public void onInitialize() {
if (FabricLoader.getInstance().isModLoaded("onekeyminer")) {
MyOKMIntegration.init();
}
}
// Forge/NeoForge
@SubscribeEvent
public 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:

  1. Is the block registered via API or tag?
  2. Is the block in the blacklist?
  3. Is the tool valid for mining?
  4. 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.

A: Yes. The API is kept backward compatible. Minor version updates will not break existing integrations.



Documentation Version: 1.7.0 | Last Updated: January 2026


This project is All Rights Reserved (ARR). You may not copy, modify, or distribute the code without permission from the author.