Sueta

LeafRTP | Random Teleport

3.3.0

Off-tick random teleport engine with spatial memory

·Plugin
Tested MC versions
1.18, 1.19, 1.20, 1.21, 26.1, 26.2, 26.3

LeafRTP: Random Teleport

Build
Java
Platforms
Web Editor
Coverage
Bugs
Maintainability

What it is

LeafRTP is a random teleport (RTP) plugin. /rtp or /wild sends a player to a random safe spot inside a region you set. It finds and checks spots ahead of time, reading the world files on disk, and keeps a queue of checked spots ready.

I've worked on it in my own time since 2021. The benchmarks and the test harness are in the repo, and every number on this page can be rerun from them.

One jar for Paper and Spigot (1.18+), Folia (1.21.11+), Fabric (1.20.1 and 1.21+, needs Fabric API) and NeoForge (1.21.1+), up to 26.3. It also loads on Velocity for /rtp across a network.

Web editor: a circular region with an inner exclusion zone drawn over the world map

Drawing where players land, in the browser editor (/rtp editor).

The short version

5 ms median from /rtp to arrival over 4,194 paced teleports (Paper).

  • Efficient: the server stayed at full speed through all 4,194 paced teleports, at up to 20 a second.
  • Fast: 40 teleports per second on Paper and 180 per second on Folia, at full server speed.
  • Distinct: across two Paper 26.2 runs of 4,096 teleports, no two landings were within 48 blocks (closest pair: 70); random picks put about 80 pairs that close in each run.
  • Portable: /rtp config import reads your BetterRTP, JustRTP or EzRTP config, previews it, and never overwrites without overwrite=true.
  • One jar: the same core on Paper, Spigot, Folia, Fabric and NeoForge, and on Velocity for /rtp across a network.
More highlights
  • Peak throughput: 249.7 teleports per second peak on Folia.
  • Web workspace: draw region borders on a map in your browser (/rtp editor). No inbound port needed.
  • Action engine: Scripted teleports that start from a trigger, like stepping into a zone or queueing for a duel.
  • Custom terrain: Spots are read from the region files on disk, including Iris, Terra, datapack and upgraded worlds.
  • Claim checks: 16 claim plugins on Bukkit / Paper / Folia plus FTB Chunks and Open Parties and Claims on Fabric / NeoForge, and the teleport is refused if a check cannot answer. Optional PvP combat-tag check.

Benchmark comparisons and hardware metrics are in Performance.

Common setups

These work without extra plugins:

  • Spawn portals & launch pads: Step-in trigger zones over portal frames or launch pads run teleports, with an optional countdown hologram and effects.
  • First-join random spawn: Grant rtp.onevent.firstjoin to send new players to random spots on login. A reserve cache keeps a destination ready.
  • Survival and resource worlds: Players start far from each other's bases: across two Paper 26.2 runs of 4,096 teleports, no two landings were within 48 blocks (closest pair: 70).

More setups: Features, recipes, actions guide.


Install

Requirements: Paper or Spigot 1.18+, Folia 1.21.11+, Fabric 1.20.1 or 1.21+, or NeoForge 1.21.1+, up to 26.3. Java 17+ (Minecraft itself needs Java 21 from 1.20.5 and Java 25 on 26.x).

Nightly CI boots 1.21.11 and 26.3. I take bug reports from 1.21.11 to the latest release; older versions should run, but fixes there are best effort. Folia is the exception: it is experimental and only its current builds are maintained, so Folia older than 1.21.11 is not supported and can crash without RTP's involvement. Exact per-version status is in the support matrix.

  1. Drop LeafRTP-x.y.z.jar into plugins/ (or mods/).
  2. Type /rtp. With the bundled GUI addon it opens a picker listing your regions, with links to the map visualizations and the admin panel; without it, /rtp teleports straight away.

That's enough to run. If you want to tune it, my recommended order is:

  1. /rtp admin setup.
  2. Set each region's radius for how players will use it. The wizard doesn't ask; use e.g. /rtp config regions default shape.radius=10km, or drag the border in /rtp editor.
  3. /rtp scan start to pre-check the terrain.
  4. A stress test if you expect heavy traffic.

Customize (optional):

  • Web workspace: /rtp editor opens it in your browser. Draw region borders on a map, edit settings with each key's docs beside it, and review a diff before applying. Screenshots and a walkthrough: web workspace guide.
  • Chat wizard: /rtp admin setup (guided setup)
  • YAML files: plugins/RTP/ (radius: 10km, teleportCooldown: 30s)
  • Switching from another rtp plugin: /rtp config import finds the old plugin's folder in plugins/ on its own (or point it anywhere with path=<dir>), then previews the translated config and permissions; add overwrite=true to write them (switching guide).

(Advanced database, network proxy, and cache tuning: see the admin guide.)


Features

  • Setting it up: web editor, in-game admin book, chat wizard, /rtp config import, /rtp scan, live map visualizations, 11 languages.
  • For players: /rtp and /wild, region, world and biome picks, /rtp back, menus, per-player queues, effects, landing platforms.
  • Gameplay and worlds: any number of regions in six shapes, arrival schematics, auto-RTP on events, Vault pricing, an optional combat-tag check.
  • Actions: step-in portals, party scatter, 1v1 and 2v2 arenas, anchor drops.
  • Claims and safety: 16 claim plugins on Bukkit-family servers plus FTB Chunks and Open Parties and Claims on Fabric and NeoForge, and a configurable block safety filter.
  • Networks and developers: cross-server /rtp over Velocity; rtp-api addons, SPI-swappable parts, PlaceholderAPI, bStats.

More: Features. Commands and permissions are in the collapsed block near the end.


Performance

I compare throughput under load, latency from /rtp to arrival, and tick stability. On Paper, world ticking runs on a single main thread, while on Folia, world ticking is distributed across independent regional worker threads. LeafRTP checks candidates against region files (.mca and .linear) off-tick and keeps a queue of verified destinations, avoiding synchronous chunk loading and main-thread stalls.

Paper 26.2 & Folia 26.2 Performance Under Load (Linux Threadripper 7970X, 48 bot clients, 16k radius):

These are snapshots of each plugin under one harness on my hardware. The other plugins make different trade-offs, and most don't aim for 100+ TP/s.

PluginPaper safe maxPaper peakFolia safe maxFolia peak
LeafRTP40 TP/s (MSPT 45 ms, min TPS 19.2)103.3 TP/s180 TP/s (MSPT < 50 ms)249.7 TP/s (19.0 avg TPS)
HuskHomes40 TP/s (MSPT 46 ms, min TPS 16.4)46.0 TP/s60 TP/s60.0 TP/s
JakesRTP20 TP/s (MSPT 44 ms, min TPS 18.4)86.6 TP/s (TPS < 5)-Not loaded (no Folia support declared)
BetterRTP20 TP/s (MSPT 43 ms, min TPS 16.2)39.5 TP/s (2% failed)-Did not complete on this rig
JustRTP20 TP/s (MSPT 40 ms, min TPS 19.5)25.7 TP/s10 TP/s18.7 TP/s
EzRTP5 TP/s (MSPT 24 ms, min TPS 20.0)8.5 TP/s< 20 TP/s19.7 TP/s

In the Paper columns, MSPT is the highest per-run p95 at the safe-max stage, and min TPS is the lowest 10 s average in any of those runs.

Method, rig and terms

Most of an rtp plugin's work happens inside server API calls, so a profiler lists it under generic chunk functions. The public harness in helpers/StressTestRTP/ attributes it back to the plugin. Rig: AMD Ryzen Threadripper 7970X (32 cores / 64 threads, 128 GB RAM, NVMe), Ubuntu 24.04 LTS, Java 25, 16 GB heap, 48 scripted bot clients (devstack/clients/bench-swarm.js). On Paper, the server ran under realtime scheduling (SCHED_FIFO, priority 10) with only the plugin under test loaded, and restarted before every run.

Each Paper run steps through 60 s stages at 5, 10, 20, 40, 80, 160 and 320 teleports per second and stops after two failed stages in a row. The other plugins' Paper rows pool 4 runs each from 2026-10-17; LeafRTP's pool 2 runs of the 3.3.0 build (20261018-022257, 20261018-023204). The Folia columns come from separate Folia ramps, and they reproduce on the current build. Each plugin ran on the latest version it supported at the time of the run, with cooldowns, delays and countdowns zeroed and its queue on at its default size where it has one.

The other plugins ran at their documented defaults, because I can't tune settings their authors don't document. LeafRTP's shipped refill, queue and backlog settings (refill every 20 ticks, a 50-spot ready queue, a 1,000-spot backlog, a pause when server speed drops) are sized for typical player traffic, and all they do is limit how fast new requests are taken in. The bench config lifts that limit so the harness can find the break point: refill every tick (period: 1) with no pause, a 10-spot ready queue and no backlog (config and the full list of changes). The cost of each teleport is the same either way.

Terms used on this page: TP/s is teleports per second. TPS is server ticks per second; 20 is full speed and the most it can reach, and normal variation measures a little under it. MSPT is milliseconds per tick; above 50 ms the server falls below 20 TPS and players feel lag. A lag spike is a tick over 50 ms. Off-tick work runs off the server's main tick thread, so it doesn't stall the game loop. A ready queue is a stock of spots checked in advance, so most /rtp calls are answered without a live check. A chunk ticket is a hold that keeps one chunk (a 16 x 16 block column) loaded until the hold is released. Rejection sampling picks a random spot, checks it, and draws again on failure, with no cap on retries. REROLL (the default) and ACCUMULATE are two selection modes: REROLL skips known-bad chunks and draws again, at one in-memory check per known-bad chunk, and ACCUMULATE draws only from chunks not known to be bad, so every pick is a guaranteed O(1) (spacing notes). Safe max is the highest stage a plugin passed in every run, with the same rule on Paper and Folia: it delivered at least 95% of the requested rate, MSPT p95 stayed at or under 50 ms, and at most 1% of teleports failed. p95 / p99 is the time that 95% / 99% of teleports came in under.

  • Paper vs. Folia: Paper places every arriving player on one main thread, so no plugin here gets much past 40 TP/s at 20 TPS. Above its safe max on Paper, LeafRTP still completed every teleport: 21,924 of 21,924 in the 80 and 160 TP/s stages, delivering up to 103.3 TP/s while the server slowed to about 9-10 TPS (MSPT p95 88-105 ms). Folia spreads that work across region threads, and LeafRTP reached 180 TP/s at 19.6 regional TPS (198 ms median latency), peaking at 249.7 TP/s.

Latency at equal load: time from /rtp to arrival, pooled over each plugin's Paper runs. Each cell is p50 / p99 in milliseconds (full table with p95).

Plugin5 TP/s10 TP/s20 TP/s40 TP/s
LeafRTP5 / 65 / 65 / 7158 / 285
JakesRTP [1]0 / 133 / 264 / 234 / 24 (MSPT p95 77-118 ms)
HuskHomes221 / 517224 / 426275 / 474568 / 1,574
BetterRTP156 / 709199 / 806199 / 815510 / 4,323 (127 failed)
EzRTP154 / 905failed (619 of 2,396)--
JustRTP367 / 2,471473 / 2,480476 / 2,477failed (25.5 of 40 TP/s delivered)

- means no result in the pooled runs. For LeafRTP at 5, 10 and 20 TP/s the max was 58 ms over 4,194 teleports, and its MSPT p95 was 21-37 ms per run. [1] JakesRTP had the lowest median at those rates, but its MSPT p95 reached 77-118 ms at 40 TP/s. At 40 TP/s LeafRTP's ready queue runs out in this config, so those teleports wait on a live selection.

What I changed on each plugin so they do the same job

Every plugin used the same 1,024 to 16,384 block circle, with cooldowns, delays and countdowns off. These changes make the work comparable; none of them tune for speed.

PluginChangeWhy
EzRTPWater landings offBy default it can land on water and add ice under the player, which accepts spots the others reject
EzRTPBiome checks on (plugins/EzRTP/rtp.yml)Without them it skipped placement checks and reported 29.47 TP/s it wasn't earning (run 20261006-135938)
EzRTPRun through forcertpSkips its cooldown and limit checks; it also ships a 5 s countdown
BetterRTPMaxRadius: 17408Its circle formula cuts MinRadius off the outer edge, so 16384 would only reach 15,360 blocks
BetterRTPRadius checked on the landing scatterIt reverts config edits on shutdown
HuskHomesCentered on /spawnIt has no center setting
AllCommand checked with /<plugin>:rtp <TAB>When two plugins register /rtp, the prefixed command can reach the wrong one; this voided one earlier run set

Source and the rest of the controls: PRE_WRITEUP.md, sections 3, 6 and 7.

Full benchmark tables, methodology, and per-run analyses: helpers/StressTestRTP/.

Where each plugin lands players (Paper 26.2, 4,096 teleports):

Cross-plugin destination scatter comparison

Where each plugin landed 4,096 players in the Paper 26.2 run (20261008-003310), every one set to the same 1,024 to 16,384 block circle around 0,0. The green box under each panel is the LeafRTP region shape that gives the same distance-from-center spread, so any of these distributions is a config change in LeafRTP. Each config is replayed through LeafRTP's own shape code over the same terrain: LeafRTP and BetterRTP match CIRCLE, EzRTP and HuskHomes match CIRCLE_NORMAL, and JustRTP and JakesRTP match the spiral power curve.

Caveats. The region-file check only works on terrain that exists; any plugin has to generate a chunk that was never generated. The Linux stress test used 48 idle bots with server view distance capped at 2: real connections and chunk sends, with nobody moving or building. Heap reached the 16 GB limit in LeafRTP 100 TP/s runs under G1; separating long-term retention from deferred GC collection is ongoing. Hardware, view distance, world state (how much of it is unsafe, whether it's pregenerated) and other plugins will move the numbers. The other plugins update often; if a number here is out of date, open a GitHub issue with a repro or a doc link and I'll correct it. The harness measures dispatch-to-arrival latency, per-attempt cost, and success rate as defined above; it does not measure claim-plugin compatibility or safety rules.

Full methodology, per-run analyses, and the runs not shown here (equalized-radius Folia, pinned-heap GC profiling, the wide-radius single-plugin run): helpers/StressTestRTP/. Video of /rtp on a custom world generator: youtu.be/V0NyNK9JydM.


How it works

  • Spatial memory: biomes and invalid chunks (with cause and TTL) are remembered at chunk resolution across restarts.
  • Region-file pre-filter: candidates are read from .mca and .linear region files and rejected off-tick.
  • Pre-verified cache: safe destinations wait in background queues; on Paper the median from /rtp to arrival was 5 ms in the paced benchmark.
  • Spacing: a keyed shuffle of the region.

Details, derivations and charts: How it works. Spacing statistics: Spacing between players.

How I test it, and what the tests have found

A plugin can pass one of these tools and fail another.

ToolWhat it drivesWhat it catches
Stress harness (helpers/StressTestRTP/)Scripted bot clients that join like players, in closed-loop, open-loop and paced modes, with spark and JFR attachedThroughput ceiling, MSPT and ticks over 50 ms, chunks and CPU per teleport, and which thread pays for them
Behavior fuzzer (scripts/bench_fuzz.py)Seeded bot programs: /rtp, move, quit, rejoin under a new name, die, console reload. Coverage-guided search, automatic replay and shrinkingMemory the server still holds after a full GC, scored against a control run with no rtp plugin. Every plugin gets the same seeds, bots and server
Jazzer targetsThe Anvil chunk and column readers, the LZ4 d

Similar projects

Plugin →