agentsclimarketplace

Fabric events

Skill PSB1234/Fabric-Minecraft-Skill-1.20.1/fabric-events

Deep reference for the Fabric API event system for Minecraft 1.20.1 modding. Use this skill whenever a Fabric mod task involves listening to game events, registering callbacks, creating custom events, choosing between Fabric events vs Mixins, or troubleshooting event-related code. Triggers on: "how do I listen for...", "what event fires when...", "register a callback", "ServerTickEvents", "PlayerBlockBreakEvents", "AttackEntityCallback", "UseItemCallback", "ServerLifecycleEvents", "custom event", "Event.create", or any question about hooking into game logic without a Mixin. Part of the fabric-mc-modding skill family.From its SKILL.md

Install
npx -y skills add PSB1234/Fabric-Minecraft-Skill-1.20.1 --skill fabric-events

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

13.2 KB, ~2.9k tokens by cl100k_base, as published. Nobody here has run it

Fabric Events — 1.20.1 Reference

Rule of thumb: If Fabric API has an event for what you need, use it instead of a Mixin. Events are safer, more mod-compatible, and easier to read. Use Mixins only when no suitable event exists.


How Events Work

Event<CallbackInterface>   — stores all registered listeners
  └─ .register(callback)  — add your listener lambda
  └─ .invoker()           — call all listeners (used internally by the game)

All event registrations go inside onInitialize() (or onInitializeClient() for client-only events). Never register events lazily or from static initializers.

// Pattern A — single static EVENT field on the callback interface
AttackEntityCallback.EVENT.register((player, world, hand, entity, hitResult) -> {
    return ActionResult.PASS;
});

// Pattern B — grouped events class (multiple related events)
ServerTickEvents.END_SERVER_TICK.register(server -> { /* ... */ });

ActionResult / InteractionResult Return Values

Many interaction callbacks return ActionResult to control whether the game continues:

ValueEffect
ActionResult.PASSContinue normal game logic
ActionResult.SUCCESSStop processing, play swing animation
ActionResult.CONSUMEStop processing, no swing animation
ActionResult.FAILPrevent default action, no swing

For item-use callbacks that return TypedActionResult<ItemStack>:

TypedActionResult.pass(stack)    // continue
TypedActionResult.success(stack) // stop, swing
TypedActionResult.fail(stack)    // cancel

Interaction Events (Player Actions)

These live in net.fabricmc.fabric.api.event.player.

// Left-click a block (mining start)
AttackBlockCallback.EVENT.register((player, world, hand, pos, direction) -> {
    // world is World on client, ServerWorld on server — check side if needed
    return ActionResult.PASS;
});

// Left-click an entity
AttackEntityCallback.EVENT.register((player, world, hand, entity, hitResult) -> {
    return ActionResult.PASS;
});

// Right-click a block
UseBlockCallback.EVENT.register((player, world, hand, hitResult) -> {
    return ActionResult.PASS;
});

// Right-click an entity
UseEntityCallback.EVENT.register((player, world, hand, entity, hitResult) -> {
    return ActionResult.PASS;
});

// Right-click with an item (no block/entity target)
UseItemCallback.EVENT.register((player, world, hand) -> {
    ItemStack stack = player.getStackInHand(hand);
    return TypedActionResult.pass(stack);
});

⚠️ These fire before the spectator check. Always guard:

if (player.isSpectator()) return ActionResult.PASS;

Block Events

// net.fabricmc.fabric.api.event.player
// Before a player breaks a block — return false to cancel
PlayerBlockBreakEvents.BEFORE.register((world, player, pos, state, entity) -> {
    return true; // false = cancel break
});

// After a player breaks a block (block already removed)
PlayerBlockBreakEvents.AFTER.register((world, player, pos, state, entity) -> { });

// When break is cancelled (by BEFORE returning false, or by another mod)
PlayerBlockBreakEvents.CANCELED.register((world, player, pos, state, entity) -> { });

Server Lifecycle Events

net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents

// Server is starting — player manager and worlds not yet loaded
ServerLifecycleEvents.SERVER_STARTING.register(server -> { });

// Server is fully started — all worlds live, safe to query data
ServerLifecycleEvents.SERVER_STARTED.register(server -> { });

// Server beginning shutdown — worlds still accessible
ServerLifecycleEvents.SERVER_STOPPING.register(server -> { });

// Server fully stopped — worlds closed, entities unloaded
ServerLifecycleEvents.SERVER_STOPPED.register(server -> { });

// Before /reload — good place to save cached data
ServerLifecycleEvents.START_DATA_PACK_RELOAD.register((server, resourceManager) -> { });

// After /reload — success boolean indicates if reload succeeded
ServerLifecycleEvents.END_DATA_PACK_RELOAD.register((server, resourceManager, success) -> { });

// Server is about to send recipes/tags to a player (e.g. on login or reload)
ServerLifecycleEvents.SYNC_DATA_PACK_CONTENTS.register((player, joined) -> { });

Server Tick Events

net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents

// Start/end of the entire server tick
ServerTickEvents.START_SERVER_TICK.register(server -> { });
ServerTickEvents.END_SERVER_TICK.register(server -> { });

// Start/end of a specific world's tick (fires once per world per tick)
ServerTickEvents.START_WORLD_TICK.register(world -> { });
ServerTickEvents.END_WORLD_TICK.register(world -> { });

💡 Use END_WORLD_TICK for starting async computations for the next tick. Dedicated servers may "pause" when empty — none of these fire while paused.


Server World Events

net.fabricmc.fabric.api.event.lifecycle.v1.ServerWorldEvents

// A dimension/world was loaded
ServerWorldEvents.LOAD.register((server, world) -> { });

// A dimension/world is about to unload
ServerWorldEvents.UNLOAD.register((server, world) -> { });

Server Entity & Block Entity Events

net.fabricmc.fabric.api.event.lifecycle.v1.ServerEntityEvents / net.fabricmc.fabric.api.event.lifecycle.v1.ServerBlockEntityEvents

// Entity loaded into a server world
ServerEntityEvents.ENTITY_LOAD.register((entity, world) -> { });

// Entity about to be unloaded
ServerEntityEvents.ENTITY_UNLOAD.register((entity, world) -> { });

// Block entity loaded
ServerBlockEntityEvents.BLOCK_ENTITY_LOAD.register((blockEntity, world) -> { });

// Block entity about to be unloaded
ServerBlockEntityEvents.BLOCK_ENTITY_UNLOAD.register((blockEntity, world) -> { });

Player Connection Events

net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents

// Player finished joining (world loaded, safe to interact with player)
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> {
    ServerPlayerEntity player = handler.player;
});

// Player disconnected
ServerPlayConnectionEvents.DISCONNECT.register((handler, server) -> { });

Loot Table Events

net.fabricmc.fabric.api.loot.v2.LootTableEvents

// Add items to an existing loot table without replacing it
LootTableEvents.MODIFY.register((key, tableBuilder, source) -> {
    if (source.isBuiltin() && LootTables.COAL_ORE_GAMEPLAY.equals(key)) {
        LootPool.Builder pool = LootPool.builder()
            .rolls(ConstantLootNumberProvider.create(1))
            .with(ItemEntry.builder(Items.EGG));
        tableBuilder.pool(pool);
    }
});

Client Lifecycle Events

Register these inside onInitializeClient() only.

net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents

// Minecraft client started (splash screen still showing)
ClientLifecycleEvents.CLIENT_STARTED.register(client -> { });

// Minecraft client is stopping
ClientLifecycleEvents.CLIENT_STOPPING.register(client -> { });

Client Tick Events

net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents

ClientTickEvents.START_CLIENT_TICK.register(client -> { });
ClientTickEvents.END_CLIENT_TICK.register(client -> { });
ClientTickEvents.START_WORLD_TICK.register(world -> { });
ClientTickEvents.END_WORLD_TICK.register(world -> { });

HUD Render Event

net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback

HudRenderCallback.EVENT.register((drawContext, tickDelta) -> {
    drawContext.drawText(
        MinecraftClient.getInstance().textRenderer,
        Text.literal("Hello HUD"),
        5, 5, 0xFFFFFF, true
    );
});

Screen Events

net.fabricmc.fabric.api.client.screen.v1.ScreenEvents

// After a screen is initialized (safe to add widgets)
ScreenEvents.AFTER_INIT.register((client, screen, scaledWidth, scaledHeight) -> {
    if (screen instanceof TitleScreen) {
        // add custom widgets to the screen here
    }
});

// Before a screen is initialized
ScreenEvents.BEFORE_INIT.register((client, screen, scaledWidth, scaledHeight) -> { });

Command Registration

net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback

CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
    dispatcher.register(CommandManager.literal("mycommand")
        .executes(ctx -> {
            ctx.getSource().sendFeedback(
                () -> Text.literal("Hello!"), false);
            return 1;
        })
    );
});

Item Group Events (Creative Tab)

net.fabricmc.fabric.api.itemgroup.v1.ItemGroupEvents

// Add items to an existing creative tab
ItemGroupEvents.modifyEntriesEvent(ItemGroups.COMBAT).register(content -> {
    content.add(ModItems.MY_SWORD);
});

Creating a Custom Event

Use this when no existing Fabric event covers your use case.

Step 1 — Define the callback interface

@FunctionalInterface
public interface MyCustomCallback {
    // Static EVENT field is the convention
    Event<MyCustomCallback> EVENT = EventFactory.createArrayBacked(
        MyCustomCallback.class,
        (listeners) -> (player, world) -> {
            for (MyCustomCallback listener : listeners) {
                listener.onMyThing(player, world);
            }
        }
    );

    void onMyThing(PlayerEntity player, World world);
}

Step 2 — Fire it from a Mixin

@Mixin(SheepEntity.class)
public class SheepEntityMixin {
    @Inject(
        at = @At(value = "INVOKE",
            target = "Lnet/minecraft/entity/passive/SheepEntity;sheared(Lnet/minecraft/sound/SoundCategory;)V"),
        method = "interactMob",
        cancellable = true
    )
    private void onShear(PlayerEntity player, Hand hand, CallbackInfoReturnable<ActionResult> cir) {
        ActionResult result = MyCustomCallback.EVENT.invoker().onMyThing(player, this.getWorld());
        if (result == ActionResult.FAIL) {
            cir.setReturnValue(ActionResult.FAIL);
        }
    }
}

Step 3 — Listen to it

MyCustomCallback.EVENT.register((player, world) -> {
    // your logic here
    return ActionResult.PASS;
});

Event Quick-Picker: "What event do I need?"

GoalEvent
Run code every tickServerTickEvents.END_SERVER_TICK
Detect player joiningServerPlayConnectionEvents.JOIN
Detect player leavingServerPlayConnectionEvents.DISCONNECT
Cancel a block breakPlayerBlockBreakEvents.BEFORE (return false)
React after block breakPlayerBlockBreakEvents.AFTER
Intercept item right-clickUseItemCallback.EVENT
Intercept block right-clickUseBlockCallback.EVENT
Intercept entity right-clickUseEntityCallback.EVENT
Intercept left-click entityAttackEntityCallback.EVENT
Add drops to loot tableLootTableEvents.MODIFY
Run on server startServerLifecycleEvents.SERVER_STARTED
Add a commandCommandRegistrationCallback.EVENT
Add to creative tabItemGroupEvents.modifyEntriesEvent(...)
Draw on player HUDHudRenderCallback.EVENT
Hook into screenScreenEvents.AFTER_INIT
Entity spawn/despawnServerEntityEvents.ENTITY_LOAD/UNLOAD
No event exists for this→ Write a Mixin; consider creating a custom event

Common Mistakes

  • Registering events in a static initializer — must be in onInitialize() / onInitializeClient().
  • Using a server event on the clientServerTickEvents won't fire in single-player unless you're on the logical server side. Check world instanceof ServerWorld when needed.
  • Forgetting the spectator guard on interaction callbacks — they fire before Minecraft's own spectator check.
  • Mutating state inside LootTableEvents.MODIFY outside the condition — always check source.isBuiltin() and the specific loot table key before modifying.
  • Client events in the main entrypointClientTickEvents, HudRenderCallback, ScreenEvents, etc. must be registered in ClientModInitializer, not ModInitializer.

Further Reading

References

FileWhen to load
references/event-index.mdComplete event listing — every event class, package, callback signature, and notes. Load when looking up a specific event or exploring what's available.

What ships with it: 1 file

10.4 KB alongside SKILL.md

references/

Keep looking

Skills are one crate of 326,861. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.