Skip to content

3. Event System and Behaviors

zawarka03 edited this page Jul 28, 2026 · 1 revision

Monolith API features a robust, hierarchical event system designed to handle interactions with custom blocks efficiently. It distinguishes between global events and block-specific behaviors.

3.1. Event Hierarchy and Dispatch Order

When an interaction occurs (e.g., a player breaks a block), the API dispatches an event. The dispatch process follows a specific order:

  1. Global Event Bus: The event is first dispatched to all listeners registered on the global MonolithAPI.api().events() bus. This allows plugins to monitor or intercept all block interactions globally.
  2. Block Behavior Node: If the event implements MonolithBlockEvent (meaning it pertains to a specific PlacedBlock), it is subsequently dispatched to the BlockBehavior node associated with that block's BlockDefinition.

This architecture ensures that global systems (like logging or protection plugins) can process events before block-specific logic executes.

3.2. Block Behaviors (BlockBehavior)

The BlockBehavior node is a localized event bus specific to a BlockDefinition. Listeners registered here will only receive events related to instances of that specific block type. This eliminates the need for manual identifier checks within event handlers, leading to cleaner and more performant code.

Example: Generator Behavior (Java)

This example demonstrates handling block placement, interaction, and destruction for the IndustrialGenerator.

package com.example.industrial.blocks;

import org.bukkit.entity.Player;
import io.github.zawarka03.monolithAPI.api.block.PlacedBlock;
import io.github.zawarka03.monolithAPI.api.event.MonolithEventHandler;
import io.github.zawarka03.monolithAPI.api.event.events.MonolithBlockBreakEvent;
import io.github.zawarka03.monolithAPI.api.event.events.MonolithBlockInteractEvent;
import io.github.zawarka03.monolithAPI.api.event.events.MonolithBlockPlaceEvent;
import io.github.zawarka03.monolithAPI.api.event.listener.MonolithListener;

public class GeneratorBehavior implements MonolithListener {

    private final IndustrialGenerator definition;

    public GeneratorBehavior(IndustrialGenerator definition) {
        this.definition = definition;
    }

    @MonolithEventHandler
    public void onPlace(MonolithBlockPlaceEvent event) {
        PlacedBlock block = event.getPlacedBlock();
        Player player = event.getPlayer();

        // Initialize persistent data upon placement
        block.getData().set(definition.STORED_ENERGY, 0);
        block.getData().set(definition.OWNER_UUID, player.getUniqueId().toString());

        player.sendMessage("Industrial Generator placed successfully.");
    }

    @MonolithEventHandler
    public void onInteract(MonolithBlockInteractEvent event) {
        PlacedBlock block = event.getPlacedBlock();
        Player player = event.getPlayer();

        // Retrieve persistent data
        int currentEnergy = block.getData().getOrDefault(definition.STORED_ENERGY, 0);
        String ownerId = block.getData().get(definition.OWNER_UUID);

        if (player.isSneaking()) {
            // Simulate charging the generator
            int newEnergy = currentEnergy + 100;
            block.getData().set(definition.STORED_ENERGY, newEnergy);
            player.sendMessage("Generator charged. Current energy: " + newEnergy);
        } else {
            player.sendMessage("Generator Status - Energy: " + currentEnergy + ", Owner: " + ownerId);
        }
    }

    @MonolithEventHandler
    public void onBreak(MonolithBlockBreakEvent event) {
        PlacedBlock block = event.getPlacedBlock();
        int finalEnergy = block.getData().getOrDefault(definition.STORED_ENERGY, 0);

        if (event.getEntity() instanceof Player player) {
            player.sendMessage("Generator destroyed. Lost energy: " + finalEnergy);
        }
    }
}

Example: Infuser Behavior (Kotlin)

This example demonstrates handling entity movement over the ArcaneInfuser.

package com.example.magic.blocks

import org.bukkit.Particle
import org.bukkit.Sound
import io.github.zawarka03.monolithAPI.api.event.events.MonolithEntityOnBlockMoveEvent
import io.github.zawarka03.monolithAPI.api.event.listener.MonolithListener
import io.github.zawarka03.monolithAPI.api.event.MonolithEventHandler

class InfuserBehavior(private val definition: ArcaneInfuser) : MonolithListener {

    @MonolithEventHandler
    fun onEntityMove(event: MonolithEntityOnBlockMoveEvent) {
        val block = event.placedBlock
        val entity = event.entity

        // Retrieve the current charge count
        val currentCharge = block.data.getOrDefault(definition.chargeCount, 0)

        if (currentCharge > 0) {
            // Apply a magical effect to the entity
            entity.world.spawnParticle(Particle.SPELL_WITCH, entity.location, 20)
            entity.world.playSound(entity.location, Sound.BLOCK_ENCHANTMENT_TABLE_USE, 1.0f, 1.0f)

            // Decrement the charge
            block.data[definition.chargeCount] = currentCharge - 1
        }
    }
}

3.3. Global Events

Global listeners are registered directly on the MonolithAPI.api().events() bus. They are useful for cross-cutting concerns, such as logging, custom protection integrations, or managing API lifecycle events.

Java Global Listener
import io.github.zawarka03.monolithAPI.MonolithAPI;
import io.github.zawarka03.monolithAPI.api.event.MonolithEventHandler;
import io.github.zawarka03.monolithAPI.api.event.events.MonolithBlockCreateEvent;
import io.github.zawarka03.monolithAPI.api.event.listener.MonolithListener;

public class GlobalLoggingListener implements MonolithListener {

    @MonolithEventHandler
    public void onBlockCreate(MonolithBlockCreateEvent event) {
        System.out.println("[Monolith Log] Block created: " + event.getPlacedBlock().getIdentifier() +
                           " at " + event.getPlacedBlock().getPosition());
    }
}

// Registration in onEnable():
// MonolithAPI.api().events().register(new GlobalLoggingListener());
Kotlin Global Listener
import io.github.zawarka03.monolithAPI.MonolithAPI
import io.github.zawarka03.monolithAPI.api.event.events.MonolithBlockCreateEvent

// Registration in onEnable():
MonolithAPI.api.events.register<MonolithBlockCreateEvent> { event ->
    println("[Monolith Log] Block created: ${event.placedBlock.identifier} at ${event.placedBlock.position}")
}

3.4. Event Cancellation and Priority

Monolith API supports event cancellation and prioritization, similar to Bukkit.

  • Cancellation: Events implementing CancellableEvent (e.g., MonolithBlockBreakEvent, MonolithBlockPlaceEvent) can be cancelled by setting isCancelled = true. If a global listener cancels an event, subsequent global listeners and the block's behavior listeners will still receive the event unless they are configured to ignore cancelled events.
  • Priority: Listeners can specify an EventPriority (LOWEST, LOW, NORMAL, HIGH, HIGHEST, MONITOR). Listeners with lower priority execute first, allowing higher priority listeners to override their decisions (e.g., un-cancelling an event).
  • Ignore Cancelled: By default, listeners ignore cancelled events (ignoreCancelled = true). If you need a listener to execute even if the event has been cancelled by a previous listener, you must explicitly set ignoreCancelled = false during registration.

Clone this wiki locally