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,0waits 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);
}
}
});
}
}