Skip to content

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.

com.hypixel.hytale.server.core.event.events.ecs

All four block events live here:

  • BreakBlockEvent
  • PlaceBlockEvent
  • DamageBlockEvent
  • UseBlockEvent (with Pre and Post subtypes)

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.

Registering a block event system
@Override
protected void setup() {
getEntityStoreRegistry().registerSystem(new MyBreakBlockSystem());
}

Fired when an entity is about to break a block. Extends CancellableEcsEvent, so you can cancel it.

Handling block breaks
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();
}
}
MethodReturn TypeDescription
getBlockType()BlockTypeThe block being broken
getTargetBlock()Vector3iWorld position of the block
setTargetBlock(Vector3i)voidRedirect the break to a different position
getItemInHand()ItemStackItem the entity is holding (may be null)
setCancelled(boolean)voidCancel the break
isCancelled()booleanCheck if cancelled

Fired when an entity is about to place a block. Also extends CancellableEcsEvent.

Handling block placement
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();
}
}
MethodReturn TypeDescription
getTargetBlock()Vector3iTarget position for the block
setTargetBlock(Vector3i)voidRedirect placement to a different position
getItemInHand()ItemStackItem being placed (may be null)
getRotation()RotationTupleBlock rotation
setRotation(RotationTuple)voidChange the block rotation
setCancelled(boolean)voidCancel the placement
isCancelled()booleanCheck if cancelled

Fired each tick while a block is being mined. Extends CancellableEcsEvent. This is where you can modify mining speed or prevent damage entirely.

Modifying mining damage
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();
}
}
MethodReturn TypeDescription
getBlockType()BlockTypeBlock being mined
getTargetBlock()Vector3iWorld position
setTargetBlock(Vector3i)voidRedirect damage to a different position
getItemInHand()ItemStackTool being used (may be null)
getCurrentDamage()floatAccumulated damage on the block so far
getDamage()floatDamage being dealt this tick
setDamage(float)voidModify the damage amount
setCancelled(boolean)voidCancel the damage tick
isCancelled()booleanCheck if cancelled

Fired when an entity interacts with a usable block. Unlike the other block events, UseBlockEvent is abstract with two concrete subtypes:

SubtypeParentCancellableWhen
UseBlockEvent.PreEcsEventYesBefore the interaction executes
UseBlockEvent.PostEcsEventNoAfter the interaction completes
Handling block interactions
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();
}
}

These methods are available on both Pre and Post:

MethodReturn TypeDescription
getBlockType()BlockTypeBlock being interacted with
getTargetBlock()Vector3iWorld position
getInteractionType()InteractionTypeType of interaction (Primary, Secondary, Use, etc.)
getContext()InteractionContextFull interaction context

UseBlockEvent.Pre additionally implements ICancellableEcsEvent:

MethodReturn TypeDescription
setCancelled(boolean)voidCancel the interaction
isCancelled()booleanCheck if cancelled

You can register multiple block event systems in your plugin’s setup():

Registering block event systems
@Override
protected void setup() {
ComponentRegistryProxy<EntityStore> entityStoreRegistry = getEntityStoreRegistry();
entityStoreRegistry.registerSystem(new MyBreakBlockSystem());
entityStoreRegistry.registerSystem(new MyPlaceBlockSystem());
entityStoreRegistry.registerSystem(new MyDamageBlockSystem());
entityStoreRegistry.registerSystem(new MyUseBlockPreSystem());
}
  • Extend EcsEvent or CancellableEcsEvent
  • Dispatched on a specific entity via entityStore.invoke(ref, event)
  • Handled by EntityEventSystem subclasses
  • Registered via getEntityStoreRegistry().registerSystem()
  • Your handler receives the entity context (archetype chunk, store, command buffer)
  1. Return Archetype.empty() from getQuery() if your system doesn’t need specific components — it will receive all entity events of that type
  2. Keep handlers fast — block damage events fire every tick during mining
  3. Use Pre for cancellationUseBlockEvent.Post fires after the interaction already happened
  4. Check getItemInHand() for null — the entity may not be holding anything
  5. Use setTargetBlock() carefully — redirecting a break or place to a different position can have unexpected side effects with chunk boundaries