EssentialsC API / RTP API

RTP API

Random teleportation - requests, warmups, world search, and results. Package net.godlycow.org.essc.api.rtp.

Overview

The RTP module runs the whole random-teleport pipeline: validate the request, check permissions and cooldowns, start a warmup, search a world for a safe spot, teleport - with 9 events along the way to veto or extend any stage. Two entry points matter most: requestRtp() for the full async flow, and the synchronous state/cooldown queries.

RTP is per-world. RtpWorldSettings describes each configured world (radii, blocked biomes, enabled flag), and per-world permissions like essentialsc.rtp.world.overworld control who may use it.

RtpManager

interface net.godlycow.org.essc.api.rtp.RtpManager

Obtain via api.getRtpManager().

System & config

Method Returns Description
boolean isRtpSystemEnabled()booleanWhether the RTP system is enabled in config.
Collection<RtpWorldSettings> getConfiguredWorlds()Collection<RtpWorldSettings>Settings for every RTP-enabled world.
RtpWorldSettings getWorldSettings(String worldName)RtpWorldSettingsSettings for a world, or null.
boolean isWorldEnabled(String worldName)booleanWhether a world is configured and enabled for RTP.
boolean isWorldBorderGloballyEnabled()booleanGlobal config flag for clamping to world borders.
long getGlobalCooldownSeconds()longDefault cooldown applied to every world.
long getGlobalWarmupSeconds()longDefault warmup applied to every world.
boolean isCancelOnMovementEnabled()booleanWhether moving during the warmup cancels the teleport.
boolean areParticlesEnabled()booleanWhether destination particles are shown.
int getMaxSearchAttempts()intMax safe-location search attempts before giving up.
int getGlobalMinY()intGlobal minimum Y for destination search.
int getGlobalMaxY()intGlobal maximum Y for destination search.

Player state & permissions

Method Returns Description
boolean isRtpInProgress(Player player)booleanWhether the player has an active RTP (warmup or search).
boolean isOnCooldown(Player player)booleanWhether the player is inside the RTP cooldown.
long getRemainingCooldownSeconds(Player player)longSeconds left on the cooldown, or 0.
boolean hasBypassPermission(Player player, String type)booleanChecks a bypass permission by type, e.g. cooldown, warmup, movement.
boolean hasWorldPermission(Player player, String worldName)booleanChecks essentialsc.rtp.world.<world> (and wildcard).
RtpPlayerState getPlayerState(Player player)RtpPlayerStateSnapshot of a player's RTP state.
RtpRequest getActiveRequest(Player player)RtpRequestThe player's current request, or null.
List<String> getAvailableWorldNamesFor(Player player)List<String>Worlds the player has permission to RTP in.
int getPlayerCountInWorld(String worldName)intOnline players in a world.

Actions

Method Returns Description
CompletableFuture<RtpResult> requestRtp(Player player, World world)RtpResultRuns the full RTP flow for a player in a world. The result tells you if it worked and why it failed.
void cancelPendingRtp(Player player)voidAborts the player's pending warmup/search.
RequestRtp.java
RtpManager rtp = api.getRtpManager();

// Guard with the same checks EssentialsC uses.
if (!rtp.isRtpSystemEnabled()) return;
if (rtp.isRtpInProgress(player)) {
    player.sendMessage("An RTP is already in progress.");
    return;
}
if (rtp.isOnCooldown(player) && !rtp.hasBypassPermission(player, "cooldown")) {
    player.sendMessage("Cooldown: " + rtp.getRemainingCooldownSeconds(player) + "s");
    return;
}

World target = player.getWorld();
if (!rtp.isWorldEnabled(target.getName())
        || !rtp.hasWorldPermission(player, target.getName())) {
    player.sendMessage("RTP is not available here.");
    return;
}

rtp.requestRtp(player, target).thenAccept(result -> {
    if (result.wasSuccessful()) {
        player.sendMessage("RTP complete at " + result.getDestination().getBlockX() + ", "
            + result.getDestination().getBlockZ());
    } else {
        player.sendMessage("RTP failed: " + result.getFailureReason());
    }
});

RtpWorldSettings

interface net.godlycow.org.essc.api.rtp.RtpWorldSettings

Configuration snapshot for one RTP world.

Method Returns Description
String getWorldName()StringBukkit world name.
String getDisplayName()StringFriendly name shown to players.
int getMinRadius()intMinimum distance from world origin.
int getMaxRadius()intMaximum distance from world origin.
List<String> getBlockedBiomes()List<String>Biome ids excluded from the search.
boolean isEnabled()booleanWhether this world accepts RTP requests.
WorldSettingsExample.java
RtpWorldSettings settings = rtp.getWorldSettings("world");
if (settings == null) return;

player.sendMessage("RTP range for " + settings.getDisplayName() + ": "
    + settings.getMinRadius() + " - " + settings.getMaxRadius() + " blocks");

if (settings.getBlockedBiomes().contains("minecraft:deep_ocean")) {
    player.sendMessage("Careful - deep oceans are off limits!");
}

RtpPlayerState

interface net.godlycow.org.essc.api.rtp.RtpPlayerState

Snapshot of a player's RTP state at a given moment.

Method Returns Description
UUID getPlayerId()UUIDThe player.
boolean isRtpInProgress()booleanWhether an RTP is currently running.
boolean isOnCooldown()booleanWhether the player is on cooldown.
long getRemainingCooldownSeconds()longSeconds left on the cooldown.
long getLastRtpTimestamp()longMillis of the last completed RTP.
int getTotalRtpCount()intLifetime RTP count for the player.
boolean hasPendingWarmup()booleanWhether a warmup is currently counting down.
StateExample.java
RtpPlayerState state = rtp.getPlayerState(player);

String status = state.isRtpInProgress()
    ? "in progress" : "idle";
if (state.isOnCooldown()) {
    status += " (cooldown " + state.getRemainingCooldownSeconds() + "s)";
}
player.sendMessage("RTP status: " + status
    + " | total: " + state.getTotalRtpCount());

RtpRequest

interface net.godlycow.org.essc.api.rtp.RtpRequest

Describes an in-flight RTP request, fetched with getActiveRequest().

Method Returns Description
UUID getRequestId()UUIDUnique id for this request.
Player getPlayer()PlayerThe requesting player.
World getTargetWorld()WorldWorld being searched.
long getRequestTimestamp()longMillis when the request was created.
boolean wasWarmupRequired()booleanWhether a warmup applies (false if bypassed).
long getWarmupSeconds()longWarmup length in seconds.

RtpResult

interface net.godlycow.org.essc.api.rtp.RtpResult

The result of requestRtp(). Always check wasSuccessful() before trusting the destination.

Method Returns Description
boolean wasSuccessful()booleanWhether the RTP completed.
Location getDestination()LocationFinal location (null on failure).
World getWorld()WorldWorld searched.
String getFailureReason()StringHuman-readable failure detail, empty on success.
long getRequestTimestamp()longMillis the request started.
long getCompletionTimestamp()longMillis the RTP finished (success or fail).
int getSearchAttempts()intHow many safe-spot candidates were tried.
ResultHandling.java
rtp.requestRtp(player, world).thenAccept(result -> {
    if (result.wasSuccessful()) {
        // getDestination() is safe to use here.
        long elapsed = result.getCompletionTimestamp() - result.getRequestTimestamp();
        getLogger().info(player.getName() + " RTP'd in " + result.getWorld().getName()
            + " after " + result.getSearchAttempts() + " attempts (" + elapsed + "ms)");
    } else {
        getLogger().info(player.getName() + " RTP failed: " + result.getFailureReason());
    }
});

The RTP lifecycle

What happens when you call requestRtp():

1

Request validation

RtpRequestEvent (cancellable). Permission, world-permission, cooldown, and in-progress checks run; you can veto the whole request.

2

Warmup

RtpWarmupStartEvent (cancellable, adjustable length). Movement/leave aborts it via RtpWarmupCancelEvent.

3

Search

RtpSearchStartEvent (cancellable) fires before the world scan; RtpSearchCompleteEvent fires after a spot is found (location may be null on total failure).

4

Teleport

RtpTeleportEvent (cancellable, setDestination() to redirect) then RtpPostTeleportEvent.

5

Outcome

Success returns an RtpResult. Any failure - no permission, world disabled, no safe location, cancelled warmup - fires RtpFailEvent with a FailureReason.


Events

All nine RTP events live in net.godlycow.org.essc.api.rtp.event.

RtpRequestEvent - cancellable

RtpRequestListener.java
@EventHandler
public void onRtpRequest(RtpRequestEvent event) {
    // Block RTP during events.
    if (isServerEventRunning()) {
        event.setCancelled(true);
        event.setCancelReason("RTP is disabled during server events.");
    }
}

RtpWarmupStartEvent / RtpWarmupCancelEvent

RtpWarmupListener.java
@EventHandler
public void onRtpWarmupStart(RtpWarmupStartEvent event) {
    // 3x warmup in the nether, instant for donors.
    if (event.getWorld().getEnvironment() == World.Environment.NETHER) {
        event.setWarmupSeconds(event.getWarmupSeconds() * 3);
    } else if (event.getPlayer().hasPermission("vip.instantrtp")) {
        event.setWarmupSeconds(0);
    }
}

@EventHandler
public void onRtpWarmupCancel(RtpWarmupCancelEvent event) {
    switch (event.getReason()) {
        case PLAYER_MOVED -> event.getPlayer().sendMessage("RTP cancelled - you moved!");
        case PLAYER_OFFLINE -> getLogger().info("RTP warmup dropped for offline player.");
        case EVENT_CANCELLED -> { /* vetoed elsewhere */ }
    }
}

RtpSearchStartEvent / RtpSearchCompleteEvent

RtpSearchListener.java
@EventHandler
public void onRtpSearchStart(RtpSearchStartEvent event) {
    // Refuse to search worlds with no configured settings.
    if (rtp.getWorldSettings(event.getWorld().getName()) == null) {
        event.setCancelled(true);
        event.setCancelReason("World has no RTP settings.");
    }
}

@EventHandler
public void onRtpSearchComplete(RtpSearchCompleteEvent event) {
    Location loc = event.getLocation();
    if (loc == null) {
        getLogger().info("No safe spot found in " + event.getWorld().getName()
            + " after " + event.getAttempts() + " attempts.");
    }
}

RtpTeleportEvent - cancellable, re-targetable

RtpTeleportListener.java
@EventHandler
public void onRtpTeleport(RtpTeleportEvent event) {
    // Snap the destination to a spawn platform if one is configured.
    Location dest = event.getDestination();
    Location platform = findPlatform(dest.getWorld());
    if (platform != null) {
        event.setDestination(platform);
    }

    // Veto teleports into a locked region.
    if (isLocked(dest)) {
        event.setCancelled(true);
        event.setCancelReason("Destination is locked.");
    }
}

RtpPostTeleportEvent

RtpPostTeleportListener.java
@EventHandler
public void onRtpPostTeleport(RtpPostTeleportEvent event) {
    Location dest = event.getDestination();
    event.getPlayer().getWorld().spawnParticle(
        Particle.CLOUD, dest, 40, 0.5, 1, 0.5, 0.01
    );
}

RtpFailEvent

The reason is one of NO_PERMISSION, NO_WORLD_PERMISSION, ALREADY_IN_PROGRESS, COOLDOWN_ACTIVE, WORLD_DISABLED, NO_SAFE_LOCATION, TELEPORT_FAILED, WARMUP_CANCELLED, or EVENT_CANCELLED.

RtpFailListener.java
@EventHandler
public void onRtpFail(RtpFailEvent event) {
    switch (event.getReason()) {
        case NO_PERMISSION -> event.getPlayer().sendMessage("You can't use /rtp.");
        case NO_SAFE_LOCATION -> event.getPlayer().sendMessage("No safe spot found - try again.");
        case COOLDOWN_ACTIVE -> event.getPlayer().sendMessage("You're on RTP cooldown.");
        case WARMUP_CANCELLED -> { /* already messaged */ }
        default -> getLogger().info(event.getPlayer().getName() + " RTP failed: "
            + event.getReason() + " " + event.getDetailMessage());
    }
}

RtpCooldownExpireEvent

RtpCooldownListener.java
@EventHandler
public void onRtpCooldownExpire(RtpCooldownExpireEvent event) {
    event.getPlayer().sendActionBar("You can /rtp again!");
}

Worked examples

RTP to a random enabled world

RandomWorldRtp.java
List<String> worlds = rtp.getAvailableWorldNamesFor(player);
if (worlds.isEmpty()) {
    player.sendMessage("No worlds available for RTP.");
    return;
}

String chosen = worlds.get(ThreadLocalRandom.current().nextInt(worlds.size()));
World world = Bukkit.getWorld(chosen);
if (world != null) {
    rtp.requestRtp(player, world);
}

Show a live cooldown in a scoreboard

CooldownDisplay.java
// Called from your scoreboard update task (sync or async).
public String rtpLine(Player player) {
    RtpPlayerState state = rtp.getPlayerState(player);
    if (state.isOnCooldown()) {
        return "RTP: " + state.getRemainingCooldownSeconds() + "s";
    }
    return "RTP: ready";
}

Cancel a stuck warmup

ForceCancel.java
if (rtp.isRtpInProgress(player)) {
    rtp.cancelPendingRtp(player);
    player.sendMessage("Your pending RTP was cancelled.");
}