Events & Effects¶
Two related but distinct extension points in RTP:
- Part 1 — Effect listeners are for server operators who want visual / audio feedback (sounds, particles, fireworks, potions, notes) when players move through the teleport pipeline — configured entirely through permissions, no code.
- Part 2 — Event listeners are for plugin developers writing an addon that wants to react to RTP's teleport lifecycle (logging, analytics, economy hooks, custom safety checks, etc.).
Supported server versions: Minecraft 1.20.1 and above. Anything in this document assumes that baseline — legacy-version caveats have been omitted.
See also:
COMMANDS.mdfor permission reference,CONFIGURATION.mdforperformance.ymltoggles.
Part 1 — Effect listeners (for operators)¶
RTP bundles the effects-api module (shaded as io.github.dailystruggle.rtp.effectsapi). BukkitEffectsHandler.setupEffects(...) hooks each teleport pipeline stage and — when enabled — dispatches effects driven entirely by permission nodes granted to the player (directly, via group, or via a permissions plugin).
Operators write no code. Just:
- Enable effect parsing.
- Grant
rtp.effect.<stage>.<TYPE>[.<arg>...]nodes to the players / groups who should see them.
Enable / disable globally¶
In performance.yml:
# Scan player permissions for rtp.effect.* on every teleport pipeline stage.
# MEDIUM impact. Leave false unless you actually use rtp.effect.* nodes.
effectParsing: false
Set to true to activate effect dispatch. A /rtp reload picks the change up without a restart.
Unrelated but sometimes confused:
onEventParsinggates the auto-teleport on lifecycle events listener (rtp.onevent.join,rtp.onevent.respawn, etc.). That is not an effect.
Pipeline stage → permission prefix¶
Each stage scans a specific permission prefix (source of truth: rtp-plugin/.../bukkit/effects/BukkitEffectsHandler.java):
| Pipeline stage | Permission prefix |
|---|---|
| Pre-setup (pipeline start) | rtp.effect.presetup.* |
| Post-setup | rtp.effect.postsetup.* |
| Pre-load (before async chunk load) | rtp.effect.presetup.* (shared with pre-setup — see note) |
| Post-load (chunks ready) | rtp.effect.postload.* |
| Pre-teleport | rtp.effect.preteleport.* |
| Post-teleport | rtp.effect.postteleport.* |
| Teleport cancelled | rtp.effect.cancel.* |
| Queue push (player enters region queue) | rtp.effect.queuepush.* |
| Queue pop (player leaves region queue) | rtp.effect.queuepop.* |
Note — pre-load reuse. The
PreLoadChunksEventstage currently callsbuildEffects("rtp.effect.presetup", ...)rather than a dedicatedpreloadprefix. This means effects granted atrtp.effect.presetup.*will fire twice (once at pipeline start, once before chunk load). If operators need a separate pre-load trigger, open an issue.
Permission node grammar¶
Each .-separated permission granted under a stage prefix is parsed as:
rtp.effect.<stage>.<TYPE>[.<arg1>[.<arg2>[...]]]
<TYPE>is one of the registered effect tokens (see table below).<arg1>,<arg2>, … are positional values, filled into the effect's parameter enum in declaration order. Missing arguments fall back to the effect's defaults.
Built-in effect types¶
Registered in effects-api/.../EffectFactory.java. All five are available on every supported server version (MC ≥ 1.20.1):
<TYPE> |
Class | Safety / extras |
|---|---|---|
FIREWORK |
FireworkEffect |
FireworkSafetyListener auto-suppresses damage from effect-spawned fireworks. |
NOTE |
NoteEffect |
Plays a note block sound at the player. |
PARTICLE |
ParticleEffect |
Spawns particles at the player. |
POTION |
PotionEffect |
Applied to the teleporting player. |
SOUND |
SoundEffect |
Played at the player's location. |
Positional arguments per type¶
Order and names come directly from the matching *TypeNames enum in effects-api/.../LocalEffects/enums/.
FIREWORK — FireworkTypeNames
Since 3.0.0-beta.2, the firework parser walks the canonical key order with a non-rewinding cursor and assigns each token to the first remaining key whose default type accepts it — so trailing booleans land on FLICKER / TRAIL / SAFE even when given before the offsets. For predictability, supply tokens in the order below.
| Pos | Variable | Typical values |
|---|---|---|
| 1 | TYPE |
BALL, BALL_LARGE, BURST, CREEPER, STAR |
| 2 | NUMBER |
integer, fireworks to spawn |
| 3 | POWER |
0–3 (flight duration) |
| 4 | COLOR |
Bukkit Color name or #RRGGBB |
| 5 | FADE |
fade-out Color |
| 6 | FLICKER |
true / false |
| 7 | TRAIL |
true / false |
| 8 | SAFE |
true disables damage (recommended) |
| 9 | DX |
x-offset from player |
| 10 | DY |
y-offset |
| 11 | DZ |
z-offset |
NOTE — NoteTypeNames
| Pos | Variable | Typical values |
|---|---|---|
| 1 | TYPE |
instrument name (PIANO, BASS_DRUM, SNARE_DRUM, STICKS, BASS_GUITAR, …) |
| 2 | TONE |
integer 0–24 (Bukkit Note two-octave id, not a letter). Common references: 0 = F♯ low, 6 = C, 8 = D, 12 = F♯ middle, 18 = C high, 24 = F♯ high. |
PARTICLE — ParticleTypeNames
| Pos | Variable | Typical values |
|---|---|---|
| 1 | TYPE |
any org.bukkit.Particle constant (PORTAL, FLAME, END_ROD, …) |
| 2 | NUMBER |
count (int) |
POTION — PotionTypeNames
| Pos | Variable | Typical values |
|---|---|---|
| 1 | TYPE |
PotionEffectType name (BLINDNESS, SPEED, NIGHT_VISION, …) |
| 2 | DURATION |
ticks (20 = 1 s) |
| 3 | AMPLIFIER |
0 = level I |
| 4 | AMBIENT |
true / false |
| 5 | PARTICLES |
true / false |
| 6 | ICON |
true / false |
SOUND — SoundTypeNames
| Pos | Variable | Typical values |
|---|---|---|
| 1 | TYPE |
any org.bukkit.Sound constant (ENTITY_ENDERMAN_TELEPORT, BLOCK_ANVIL_LAND, …) |
| 2 | VOLUME |
integer, scaled by / 100 (so 100 = volume 1.0) |
| 3 | PITCH |
integer, scaled by / 100 (so 100 = pitch 1.0) |
| 4 | DX |
x-offset from player |
| 5 | DY |
y-offset |
| 6 | DZ |
z-offset |
Missing trailing arguments keep the effect's built-in defaults (see each *Effect class constructor).
Where to find valid values¶
The positional tables above tell you which kind of token each slot accepts (Particle constant, Sound constant, PotionEffectType name, ...). The exhaustive list of constants is server-version-specific and lives in the platform's own reference, not here. Use these as your catalogue:
Bukkit / Paper / Spigot / Folia backends (the rtp.effect.* permission grammar resolves directly against these enums):
| Token type | 1.21 (LTS line) | 26.1 (current) |
|---|---|---|
Particle (PARTICLE.TYPE) |
org.bukkit.Particle - 1.21 |
org.bukkit.Particle - 26.1 |
Sound (SOUND.TYPE) |
org.bukkit.Sound - 1.21 |
org.bukkit.Sound - 26.1 |
PotionEffectType (POTION.TYPE) |
PotionEffectType - 1.21 |
PotionEffectType - 26.1 |
FireworkEffect.Type (FIREWORK.TYPE) |
FireworkEffect.Type - 1.21 |
FireworkEffect.Type - 26.1 |
Color (FIREWORK.COLOR / FADE) |
org.bukkit.Color - 1.21 |
org.bukkit.Color - 26.1 |
Note.Tone reference (NOTE.TONE) |
org.bukkit.Note - 1.21 |
org.bukkit.Note - 26.1 |
Use the enum constant name verbatim in the permission node (e.g. FLAME, ENTITY_ENDERMAN_TELEPORT, NIGHT_VISION, BALL, BLUE). Names are case-sensitive in the underlying valueOf call, but the parser uppercases the token before lookup so casing in the permission node is forgiving.
Fabric / plain Minecraft backends (and any time you want the version-agnostic, vanilla-side catalogue):
| Token type | Reference |
|---|---|
| Particles | Minecraft Wiki - Java Edition particle list |
| Sounds (sound event IDs) | Minecraft Wiki - Sounds.json / sound events |
| Status effects (potion types) | Minecraft Wiki - Effect |
| Firework shapes / colors | Minecraft Wiki - Firework Star |
The wiki tables use lowercase namespaced IDs (e.g. minecraft:flame, minecraft:entity.enderman.teleport). Strip the minecraft: prefix and translate to the Bukkit enum form by uppercasing and replacing . with _ (entity.enderman.teleport -> ENTITY_ENDERMAN_TELEPORT). For Fabric, the same vanilla IDs are what the underlying ParticleType / SoundEvent registries use.
Version drift is real.
ParticleandSoundconstants are renamed, added, or removed between Minecraft versions (notable churn between 1.20.x and 1.21.x, and again into the 26.x line). If a permission node stops working after a server update, check the Javadoc for your running version before assuming a bug. The/particleand/playsoundvanilla commands have tab completion against the running registry and are the fastest sanity check.
Tab completion via your permissions plugin¶
EffectFactory.addPermissions(prefix) (called by rtp-plugin when effectParsing is enabled) auto-registers a Bukkit Permission node for every TYPE enum constant of every built-in effect, under each pipeline-stage prefix. If your permissions plugin (LuckPerms, PEX, GroupManager, ...) reads from the server's PluginManager permission registry, those nodes show up in tab completion:
/lp user <name> permission set rtp.effect.postteleport.PARTICLE.<TAB>
-> FLAME, PORTAL, END_ROD, HEART, ... (every Particle constant on this server)
This is the fastest way to discover valid type names without leaving the game: turn on effectParsing, reload, and tab-complete from your perms plugin. Positional args after the TYPE (NUMBER, DURATION, color, offsets, etc.) are not auto-registered as nodes - those still come from the Javadoc / wiki tables above.
Worked examples¶
Each node is one complete effect. Grant multiple to stack them on the same stage.
# plugin.yml / permissions.yml / LuckPerms / PEX — same grammar everywhere
permissions:
# Classic "teleport whoosh" on arrival
rtp.effect.postteleport.SOUND.ENTITY_ENDERMAN_TELEPORT: true
# Portal particles when the player is about to be moved
rtp.effect.preteleport.PARTICLE.PORTAL.50: true
# Brief blindness while chunks load (immersion for async delay)
rtp.effect.postsetup.POTION.BLINDNESS.40.0: true
# Celebratory firework at the destination (safe = no damage)
# Token order: TYPE.NUMBER.POWER.COLOR.FADE.FLICKER.TRAIL.SAFE.DX.DY.DZ
rtp.effect.postteleport.FIREWORK.BALL.1.1.BLUE.WHITE.true.true.true.0.0.0: true
# Note blip when player enters a queue (TONE is 0-24, see NoteTypeNames table above)
rtp.effect.queuepush.NOTE.PIANO.12: true
# Audible cue when a teleport is cancelled
rtp.effect.cancel.SOUND.BLOCK_ANVIL_LAND.80.100: true
Operator verification commands¶
effects-api registers standalone test commands so operators can preview an effect without triggering a teleport:
| Command | Purpose |
|---|---|
/effectsapi |
Base command (help / listing). |
/firework, /note, /particle, /potion, /sound |
Trigger one instance of that effect on yourself with the given args. |
/effectsapi test (TestCommand) |
Generic parameterised test. |
Use these to validate an enum name and argument order before baking it into a permission node.
Troubleshooting¶
| Symptom | Likely cause |
|---|---|
| Nothing happens, no errors | performance.yml → effectParsing: false. |
| Only some players see effects | Permission is on a group they're not in, or a * wildcard is over-matching and resolving to false. |
| Effect fires at the wrong stage | Stage prefixes are literal — postsetup ≠ postteleport. Check the table above. |
| Unknown enum name in a node | Bukkit Sound / Particle / PotionEffectType constants vary slightly between 1.20.1, 1.21.x, and newer versions. Verify with /sound, /particle, /potion on your actual server. |
rtp.effect.presetup.* seems to fire twice |
Pre-load stage currently reuses the pre-setup prefix (see note above). |
Part 2 — Event listeners (for developers)¶
RTP publishes Bukkit-style events under io.github.dailystruggle.rtp.bukkit.events (module rtp-plugin). An addon consumes them the same way as any other Bukkit event: implement Listener, annotate with @EventHandler, register with the plugin manager.
Reference addon — addons/LeafRTPCountdownAddon¶
A complete, compilable template ships in the repository. It demonstrates the four API touch-points every addon typically needs:
- Config registration —
ConfigParser<CountdownKeys>participates in/rtp reload. - Safety contribution —
GlobalRegionVerifiers.addGlobalRegionVerifier(...)predicate, invoked asynchronously by the teleport pipeline (runs off the main thread and never loads chunks synchronously). - Event handling — a Bukkit
Listenerfor one of RTP's lifecycle events. - Reload hook —
Configs.onReload(Runnable)so operator/rtp reloadpicks up addon changes without a restart.
Relevant files:
| File | Role |
|---|---|
addons/LeafRTPCountdownAddon/src/main/java/io/github/dailystruggle/rtp/countdownaddon/RTPCountdownAddon.java |
Plugin main class, wires all four touch-points. |
.../ExampleTeleportListener.java |
Minimal PostTeleportEvent listener. |
.../CountdownKeys.java |
Enum backing countdown.yml. |
addons/LeafRTPCountdownAddon/README.md |
Step-by-step walkthrough. |
Listener shape (ExampleTeleportListener)¶
public final class ExampleTeleportListener implements Listener {
@EventHandler
public void onPostTeleport(PostTeleportEvent event) {
ConfigParser<CountdownKeys> parser =
(ConfigParser<CountdownKeys>) RTP.configs.getParser(CountdownKeys.class);
if (parser == null) return;
Object flag = parser.getConfigValue(CountdownKeys.announceTeleport, false);
boolean enabled = (flag instanceof Boolean) ? (Boolean) flag
: Boolean.parseBoolean(String.valueOf(flag));
if (!enabled) return;
RTP.log(Level.INFO, "[LeafRTPCountdownAddon] PostTeleportEvent observed: " + event.getDoTeleport());
}
}
Registration (from RTPExampleAddon.onEnable):
Bukkit.getPluginManager().registerEvents(new ExampleTeleportListener(), this);
Public event catalog¶
All events live in rtp-plugin/src/main/java/io/github/dailystruggle/rtp/bukkit/events/. Pipeline-stage events are fired from BukkitEffectsHandler.setupEffects(...); queue events are fired from Region.onPlayerQueuePush / onPlayerQueuePop; command events are fired from the /rtp command pipeline.
| Event | When it fires |
|---|---|
PreSetupTeleportEvent |
Before TeleportPipelineTask setup stage. Cancellable — setting cancelled aborts the teleport. |
PostSetupTeleportEvent |
Setup stage completed successfully. |
PreLoadChunksEvent |
Before async chunk load of the destination. |
PostLoadChunksEvent |
Destination chunks are loaded (still async-safe). |
PreTeleportEvent |
Immediately before the entity teleport call. |
PostTeleportEvent |
Immediately after a successful teleport. |
TeleportCancelEvent |
Player/pipeline cancelled a scheduled teleport (RTPTeleportCancel). |
TeleportCommandSuccessEvent |
/rtp command accepted, pipeline started. |
TeleportCommandFailEvent |
/rtp command rejected (cooldown, parse error, etc.). |
PlayerQueuePushEvent |
Player added to a region's waiting queue. |
PlayerQueuePopEvent |
Player popped off a region's waiting queue. |
RandomSelectQueueEvent |
A queued candidate location was selected from the buffer. |
Developer do / don't¶
RTP's internal safety rules require every listener to follow these:
- Do put chunk / claim / biome checks inside a
GlobalRegionVerifierslambda — it runs asynchronously and never loads chunks on the main thread. - Do log via
RTP.log(Level, msg[, throwable]). NeverBukkit.getLogger(), neverprintStackTrace(). - Don't perform synchronous
world.getChunkAt(...)inside an event handler. - Don't silently
returnon a teleport failure — surface via the event or log. - Don't import
org.bukkit.*fromrtp-coreorrtp-api; keep platform code in the adapter module or in the addon.
Cross-references¶
- Source of truth, effect dispatch:
rtp-plugin/src/main/java/io/github/dailystruggle/rtp/bukkit/effects/BukkitEffectsHandler.java - Source of truth, effect registry:
effects-api/src/main/java/io/github/dailystruggle/effectsapi/EffectFactory.java - Parameter enums:
effects-api/src/main/java/io/github/dailystruggle/effectsapi/LocalEffects/enums/ - Developer walkthrough:
addons/LeafRTPCountdownAddon/README.md - Safety rules every listener must follow: see the do / don't list above.
- Auto-teleport (
rtp.onevent.*) permissions:COMMANDS.md