Skip to content

Events

Quick Start: Extend the plugin.

All 15 events extend org.bukkit.event.Event, fire synchronously on the main thread, and live in com.jruk8.jmanhunt.api.event. None are cancellable in v1. Every match event carries getMatchId().

Event When it fires Data
MatchCreateEvent A match is created and announced (pre-start window begins). getLobbyId(), getCellIndex() (world cell, when allocated)
GameBeginEvent The game actually begins (pre-start window over).
MatchEndEvent A winner is announced (end delay still running). getWinner()
MatchCancelEvent A match is cancelled with no winner.
PlayerJoinMatchEvent A player joins a running match. getPlayerId(), getRole()
PlayerLeaveMatchEvent A player leaves a match (quit, kick, or removal). Role switches fire PlayerRoleChangeEvent instead. getPlayerId()
PlayerRoleChangeEvent A completed role switch (join path included). getPlayerId(), getOldRole(), getNewRole()
MatchPlayerDeathEvent A participant dies, after lives decrement. getVictimId(), getKillerId() (empty for natural deaths), isElimination()
MatchPlayerKillEvent A participant kills an enemy participant. getKillerId(), getVictimId(), isElimination()
MatchRespawnEvent A participant respawns, after state settles. getPlayerId(), getLivesLeft()
CombatHitEvent PvP damage between participants of one begun match lands (damage in half-hearts). Skipped entirely while nobody listens, so combat-heavy addons cost nothing when absent. getDamagerId(), getVictimId(), getDamage()
MatchProgressionEvent A participant hits a tracked milestone. getPlayerId(), getKind() (NETHER, END, DRAGON)
ModifierToggleEvent A modifier is enabled or disabled globally. getId(), isEnabled()
PlayerMatchPenaltyEvent A standard-match disconnect spends the last grace strike (forfeit). getPlayerId()
PlayerRankedPenaltyEvent A ranked-match disconnect spends the last grace strike (forfeit). getPlayerId(), getStrikes() (from 1), getCooldownUntilMillis()

Death and kill events fire once state is final: lives are already decremented, so isElimination() tells you whether the victim is out. Cancelling a match with /manhunt end fires MatchCancelEvent, not MatchEndEvent: there is no winner.

import com.jruk8.jmanhunt.api.event.GameBeginEvent;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;

public final class MyListener implements Listener {

    @EventHandler
    public void onGameBegin(GameBeginEvent event) {
        long matchId = event.getMatchId();
        // e.g. apply one-heart here for the active match
    }
}

Prepare hooks

Before a match teleports its players, it sits in MatchPhase.PREPARING:

  • Gamerules and world defaults are not applied yet, modifiers do not run, roles are not announced, and players are neither teleported nor put into their match gamemodes.
  • Their positions stay locked, and a match that loses a whole side (or sits too long) is cancelled instead of starting.
  • Two sockets let an addon extend this phase, one before and one after the engine's own preparation work (currently the cell fetch wait):
AddonHandle handle = api.registerAddon(plugin, new ApiVersion(1, 1, 0));
handle.onPrepareBefore(context -> {
    // main thread; return a stage that completes when done
    return CompletableFuture.runAsync(() -> warmMyCache(context.matchId()));
});
handle.onPrepareAfter(context -> {
    // engine work finished, teleports still pending
    return CompletableFuture.completedFuture(null);
});
  • Hooks are invoked on the main thread and may complete on any thread; the match proceeds once every hook's stage completes.
  • A null stage, a thrown exception, or a failed stage completes that hook at once with a console warning, so a broken addon never stalls a match.
  • A watchdog still bounds the whole phase (prepare-timeout-seconds, default 30, 0 waits forever): a match that overruns it is cancelled with a warning. Hooks release automatically when the owning plugin disables.

Example: applying one-heart at game begin

import com.jruk8.jmanhunt.api.JManhuntApi;
import com.jruk8.jmanhunt.api.event.GameBeginEvent;
import com.jruk8.jmanhunt.api.model.ManhuntPlayer;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;

public class OneHeartChallenge implements Listener {

    private final JManhuntApi api;

    public OneHeartChallenge(JManhuntApi api) {
        this.api = api;
    }

    @EventHandler
    public void onGameBegin(GameBeginEvent event) {
        api.matches().match(event.getMatchId()).ifPresent(match -> {
            for (ManhuntPlayer entry : match.players()) {
                Player player = Bukkit.getPlayer(entry.uuid());
                if (player != null && entry.role().isParticipant()) {
                    player.getAttribute(
                            org.bukkit.attribute.Attribute.MAX_HEALTH)
                            .setBaseValue(2.0);
                    player.setHealth(2.0);
                }
            }
        });
    }
}