Block Events
Block events let your plugin react when blocks are broken, placed, damaged, or interacted with. Unlike the pub-sub event system used for player events, block events are ECS events — they’re dispatched on entities via EntityEventSystem and handled through the component system.
Package Location
Section titled “Package Location”com.hypixel.hytale.server.core.event.events.ecs
All four block events live here:
BreakBlockEventPlaceBlockEventDamageBlockEventUseBlockEvent(withPreandPostsubtypes)
How ECS Events Work
Section titled “How ECS Events Work”Block events extend EcsEvent (or CancellableEcsEvent) instead of implementing IEvent. They’re dispatched directly on entities via entityStore.invoke(ref, event) and handled by registering an EntityEventSystem with the entity store registry.
@Overrideprotected void setup() { getEntityStoreRegistry().registerSystem(new MyBreakBlockSystem());}BreakBlockEvent
Section titled “BreakBlockEvent”Fired when an entity is about to break a block. Extends CancellableEcsEvent, so you can cancel it.
public class MyBreakBlockSystem extends EntityEventSystem<EntityStore, BreakBlockEvent> { public MyBreakBlockSystem() { super(BreakBlockEvent.class); }
@Override public void handle(int index, ArchetypeChunk<EntityStore> chunk, Store<EntityStore> store, CommandBuffer<EntityStore> commandBuffer, BreakBlockEvent event) { BlockType blockType = event.getBlockType(); Vector3i position = event.getTargetBlock();
if (blockType.getId().equals("Protected_Block")) { event.setCancelled(true); } }
@Override public Query<EntityStore> getQuery() { return Archetype.empty(); }}BreakBlockEvent Methods
Section titled “BreakBlockEvent Methods”| Method | Return Type | Description |
|---|---|---|
getBlockType() | BlockType | The block being broken |
getTargetBlock() | Vector3i | World position of the block |
setTargetBlock(Vector3i) | void | Redirect the break to a different position |
getItemInHand() | ItemStack | Item the entity is holding (may be null) |
setCancelled(boolean) | void | Cancel the break |
isCancelled() | boolean | Check if cancelled |
PlaceBlockEvent
Section titled “PlaceBlockEvent”Fired when an entity is about to place a block. Also extends CancellableEcsEvent.
public class MyPlaceBlockSystem extends EntityEventSystem<EntityStore, PlaceBlockEvent> { public MyPlaceBlockSystem() { super(PlaceBlockEvent.class); }
@Override public void handle(int index, ArchetypeChunk<EntityStore> chunk, Store<EntityStore> store, CommandBuffer<EntityStore> commandBuffer, PlaceBlockEvent event) { Vector3i position = event.getTargetBlock();
if (isProtectedArea(position)) { event.setCancelled(true); } }
@Override public Query<EntityStore> getQuery() { return Archetype.empty(); }}PlaceBlockEvent Methods
Section titled “PlaceBlockEvent Methods”| Method | Return Type | Description |
|---|---|---|
getTargetBlock() | Vector3i | Target position for the block |
setTargetBlock(Vector3i) | void | Redirect placement to a different position |
getItemInHand() | ItemStack | Item being placed (may be null) |
getRotation() | RotationTuple | Block rotation |
setRotation(RotationTuple) | void | Change the block rotation |
setCancelled(boolean) | void | Cancel the placement |
isCancelled() | boolean | Check if cancelled |
DamageBlockEvent
Section titled “DamageBlockEvent”Fired each tick while a block is being mined. Extends CancellableEcsEvent. This is where you can modify mining speed or prevent damage entirely.
public class MyDamageBlockSystem extends EntityEventSystem<EntityStore, DamageBlockEvent> { public MyDamageBlockSystem() { super(DamageBlockEvent.class); }
@Override public void handle(int index, ArchetypeChunk<EntityStore> chunk, Store<EntityStore> store, CommandBuffer<EntityStore> commandBuffer, DamageBlockEvent event) { float damage = event.getDamage(); event.setDamage(damage * 1.5f); }
@Override public Query<EntityStore> getQuery() { return Archetype.empty(); }}DamageBlockEvent Methods
Section titled “DamageBlockEvent Methods”| Method | Return Type | Description |
|---|---|---|
getBlockType() | BlockType | Block being mined |
getTargetBlock() | Vector3i | World position |
setTargetBlock(Vector3i) | void | Redirect damage to a different position |
getItemInHand() | ItemStack | Tool being used (may be null) |
getCurrentDamage() | float | Accumulated damage on the block so far |
getDamage() | float | Damage being dealt this tick |
setDamage(float) | void | Modify the damage amount |
setCancelled(boolean) | void | Cancel the damage tick |
isCancelled() | boolean | Check if cancelled |
UseBlockEvent
Section titled “UseBlockEvent”Fired when an entity interacts with a usable block. Unlike the other block events, UseBlockEvent is abstract with two concrete subtypes:
| Subtype | Parent | Cancellable | When |
|---|---|---|---|
UseBlockEvent.Pre | EcsEvent | Yes | Before the interaction executes |
UseBlockEvent.Post | EcsEvent | No | After the interaction completes |
public class MyUseBlockPreSystem extends EntityEventSystem<EntityStore, UseBlockEvent.Pre> { public MyUseBlockPreSystem() { super(UseBlockEvent.Pre.class); }
@Override public void handle(int index, ArchetypeChunk<EntityStore> chunk, Store<EntityStore> store, CommandBuffer<EntityStore> commandBuffer, UseBlockEvent.Pre event) { BlockType blockType = event.getBlockType();
if (blockType.getId().equals("MyPlugin_LockedChest")) { event.setCancelled(true); } }
@Override public Query<EntityStore> getQuery() { return Archetype.empty(); }}UseBlockEvent Methods
Section titled “UseBlockEvent Methods”These methods are available on both Pre and Post:
| Method | Return Type | Description |
|---|---|---|
getBlockType() | BlockType | Block being interacted with |
getTargetBlock() | Vector3i | World position |
getInteractionType() | InteractionType | Type of interaction (Primary, Secondary, Use, etc.) |
getContext() | InteractionContext | Full interaction context |
UseBlockEvent.Pre additionally implements ICancellableEcsEvent:
| Method | Return Type | Description |
|---|---|---|
setCancelled(boolean) | void | Cancel the interaction |
isCancelled() | boolean | Check if cancelled |
Registering Multiple Systems
Section titled “Registering Multiple Systems”You can register multiple block event systems in your plugin’s setup():
@Overrideprotected void setup() { ComponentRegistryProxy<EntityStore> entityStoreRegistry = getEntityStoreRegistry(); entityStoreRegistry.registerSystem(new MyBreakBlockSystem()); entityStoreRegistry.registerSystem(new MyPlaceBlockSystem()); entityStoreRegistry.registerSystem(new MyDamageBlockSystem()); entityStoreRegistry.registerSystem(new MyUseBlockPreSystem());}ECS Events vs Pub-Sub Events
Section titled “ECS Events vs Pub-Sub Events”- Extend
EcsEventorCancellableEcsEvent - Dispatched on a specific entity via
entityStore.invoke(ref, event) - Handled by
EntityEventSystemsubclasses - Registered via
getEntityStoreRegistry().registerSystem() - Your handler receives the entity context (archetype chunk, store, command buffer)
- Implement
IEvent<KeyType>orIAsyncEvent<KeyType> - Dispatched globally via the event bus
- Handled by lambda/method reference callbacks
- Registered via
getEventRegistry().register() - Support priorities (
FIRST,EARLY,NORMAL,LATE,LAST)
Best Practices
Section titled “Best Practices”- Return
Archetype.empty()fromgetQuery()if your system doesn’t need specific components — it will receive all entity events of that type - Keep handlers fast — block damage events fire every tick during mining
- Use
Prefor cancellation —UseBlockEvent.Postfires after the interaction already happened - Check
getItemInHand()for null — the entity may not be holding anything - Use
setTargetBlock()carefully — redirecting a break or place to a different position can have unexpected side effects with chunk boundaries
Related
Section titled “Related”- Event System - Pub-sub event handling
- ECS Overview - Entity Component System basics
- Block Types - BlockType properties