Skip to content

World and Blocks

goshi-hider edited this page Jul 24, 2026 · 1 revision

World and Blocks

Package: dev.goshi.omnimixin.api.event.world

Block interaction, placement, breaking, mining speed, and redstone power calculation.

PlayBlock

Three independent events, not phases of one action.

INTERACT

Fires when a player right-clicks a block (the "use" interaction — opening a chest, pressing a button, etc.). Cancellable.

// Lock a specific chest behind a key item.
PlayBlock.INTERACT.register(ctx -> {
    if (ctx.state().isOf(MyBlocks.VAULT_CHEST) && !hasKey(ctx.player())) {
        ctx.cancel().cancel();
    }
});

BlockInteractContext(World world, BlockPos pos, BlockState state, PlayerEntity player, BlockHitResult hit, Cancellable cancel)

Note: this covers the "use" interaction only, not item-based interactions like using bonemeal or flint-and-steel on a block — see [[Known Limitations]].

PLACE

Fires when a block is about to be placed.

// Prevent placing blocks within a protected radius of spawn.
PlayBlock.PLACE.register(ctx -> {
    if (isWithinSpawnProtection(ctx.pos())) {
        ctx.cancel().cancel();
    }
});

PlaceContext(World world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack itemStack, Cancellable cancel)

NEIGHBOR_UPDATE

Fires when a block is notified that a neighboring block changed (the same signal that drives redstone updates, water flow triggers, etc.).

PlayBlock.NEIGHBOR_UPDATE.register(ctx -> {
    if (ctx.state().isOf(MyBlocks.UNSTABLE_CRYSTAL) && ctx.sourceBlock() == Blocks.TNT) {
        triggerCollapse(ctx.world(), ctx.pos());
    }
});

NeighborUpdateContext(World world, BlockPos pos, BlockState state, Block sourceBlock, BlockPos sourcePos, Cancellable cancel)

PlayBreak

Three phases across the lifetime of breaking a single block.

// Mining fatigue near a specific boss arena.
PlayBreak.CALCULATE_SPEED.register(ctx -> {
    if (isInBossArena(ctx.player())) {
        ctx.speed().multiply(0.25f);
    }
});

// Unbreakable "core" blocks.
PlayBreak.PRE_BREAK.register(ctx -> {
    if (ctx.state().isOf(MyBlocks.DUNGEON_CORE)) {
        ctx.cancel().cancel();
    }
});

// Custom particle burst on successfully breaking a block.
PlayBreak.POST_BREAK.register(ctx -> {
    spawnBreakParticles(ctx.world(), ctx.pos(), ctx.state());
});

MiningSpeedContext(PlayerEntity player, BlockState state, MutableFloat speed)CALCULATE_SPEED

BreakContext(World world, BlockPos pos, BlockState state, PlayerEntity player, Cancellable cancel)PRE_BREAK

AfterBreakContext(World world, PlayerEntity player, BlockPos pos, BlockState state, BlockEntity blockEntity, ItemStack tool, Cancellable cancel)POST_BREAK

PRE_BREAK's Cancellable stops the block from breaking at all. POST_BREAK fires after the block is already gone — its Cancellable has no effect on the break itself, but is still passed for consistency and in case a future version needs to veto a specific side effect of breaking (drops, XP) without touching the break itself.

PlayRedstone.MODIFY_POWER

Fires whenever a block's redstone power output (weak or strong) is being calculated.

// A custom lever variant that outputs full power in both directions.
PlayRedstone.MODIFY_POWER.register(ctx -> {
    if (ctx.state().isOf(MyBlocks.OMNI_LEVER)) {
        ctx.power().set(15);
    }
});

RedstonePowerContext(BlockState state, BlockView world, BlockPos pos, Direction direction, boolean strong, MutableInt power)

strong tells you whether this is a strong-power query (the kind that can power a block two steps away) or a weak-power query — check it if your logic needs to differ between the two.

Note: there's no separate "signal changed" event — the closest verified vanilla call site for that would just duplicate PlayBlock.NEIGHBOR_UPDATE above, so it isn't exposed twice. See Known Limitations.

Clone this wiki locally