Compatibility
Minecraft: Java Edition
Platforms
Supported environments
Tags
Creators
Details
PayTp Mod Documentation (v2)
*To be noted, due to huge API changes and the fact that Minecraft is no longer using obfuscation for source codes, patches later than v1.2.0 will NOT support versions below 26.1.
*For data migration, please check here.
Overview
PayTp is a lightweight Fabric mod for Minecraft that allows players to teleport by paying a certain amount of in-game currency (items).
It supports flexible teleportation modes, multi-language localization, and fully customizable cost rules.
Features
- Editable command names
- Cross-dimension teleport to a specified location
- Player teleport request system
- Home and Back
- Beacon waypoint (Warp) feature with a categorized, paginated SGUI browser
- Fully customizable JEXL teleportation price and distance algorithm
- Ender Chest / Shulker Box payment support
- Cloth Config API support (client-side)
- Can be used as a server-side only mod
Most features can be disabled by setting their corresponding command names to an empty string.
For example, changing teleport.coordinateCommand in the config file from ptp to empty will disable the coordinate teleport function.
The in-game help guide will automatically adapt.
Commands
All displays show the default command names, where <> indicates required parameters and () indicates optional parameters
Every <player> argument accepts and suggests online player names only; entity selectors such as @a, @p, and @s are not supported.
The /ptp <x> <y> <z> text is a non-selectable format hint rather than a current-coordinate suggestion; ~ and ^ relative coordinates remain supported.
Waypoint names may contain Unicode and special characters without quotes as long as they contain no whitespace. A name containing spaces must be enclosed in double quotes in every waypoint command. Quoted names without spaces remain valid as well. Examples: 主城, 主城#1, and "Main City". Create requires the type before the name, such as /ptpwarp create server 主城; delete keeps the optional suffix form /ptpwarp delete "主城" forced. There are no legacy duplicate command branches. With the cursor immediately after teleport, delete, rename, invite, or exclude, the server lists complete applicable waypoint choices such as "樱花谷"; this is a choice list, not prefix matching after typing part of a name. Online players are then completed after invite/exclude. Clicking a command in /ptphelp inserts only its executable command prefix and never copies placeholder text such as <name> or <player>.
| Command | Description |
|---|---|
/ptphelp |
Get command guide for PayTp |
/ptp (dimension) <x> <y> <z> |
Teleport to specified coordinates (in a specific dimension) |
/ptpto <player> |
Send request to teleport to a player |
/ptphere <player> |
Send request to a player to teleport to you |
/ptpaccept (player) |
Accept a teleport request (from a specific player) |
/ptpdeny (player) |
Deny a teleport request (from a specific player) |
/ptpcancel (player) |
Cancel a pending teleport request (to a specific player) |
/ptpback |
Return to the previous location |
/ptphome |
Teleport to your home (if configured) |
/ptphome set |
Set your home to your current position |
/ptpwarp |
Open the categorized and paginated server-side waypoint SGUI. |
/ptpwarp teleport <name> |
Teleport to the specified waypoint |
/ptpwarp create (private/public/server) <name> |
Create a waypoint with an explicit type; server is permission-controlled. |
/ptpwarp delete <name> (forced) |
Delete your waypoint; forced is permission-controlled. |
/ptpwarp rename <name> <new_name> |
Rename a waypoint you created. |
/ptpwarp invite <name> <player> |
Invite a player to one of your private waypoints. |
/ptpwarp exclude <name> <player> |
Remove an invited player from a private waypoint. |
/ptpwarp list (all/public/owned/invited/server) (page) |
Filter and page through server waypoints. |
Configuration
Configuration File Location:
~/config/paytp.json
Example Structure:
{
"general": {
"language": "en_us",
"helpCommand": "ptphelp",
"safeTeleport": false,
"safeTeleportRange": 5,
"effect": {
"particleEffect": true,
"soundEffect": true
}
},
"teleport": {
"coordinateCommand": "ptp",
"allowCrossDim": true
},
"request": {
"requestCommand": {
"toCommand": "ptpto",
"hereCommand": "ptphere",
"acceptCommand": "ptpaccept",
"denyCommand": "ptpdeny",
"cancelCommand": "ptpcancel"
},
"expireTime": 10
},
"home": {
"homeCommand": "ptphome",
"setRespawnPoint": false
},
"back": {
"backCommand": "ptpback",
"maxBackStack": 10
},
"warp": {
"warpCommand": "ptpwarp",
"serverWarpPermission": 2,
"autoDeleteInactiveWarps": true,
"maxInactiveTicks": 100,
"checkPeriodTicks": 20
},
"price": {
"currencyItem": "minecraft:diamond",
"minPrice": 1,
"maxPrice": 64,
"algorithm": "// Available variables:\n//\n// Variable | Available methods\n// -------- | -----------------------------------------------------------\n// from | .x(), .y(), .z(), .dimension()\n// to | .x(), .y(), .z(), .dimension()\n// context | .coordinate(), .home(), .back(), .request(), .warp()\n// player | .uuid(), .name()\n// callback | .onSuccess(), .onFailure()\n//\n// Java\u0027s built-in Math methods are available through the \"math\" namespace.\n// Minecraft commands are available through minecraft:execute(\"command\").\n// Full system shell access is available through the \"shell\" namespace.\n\nvar basePrice \u003d 1;\nvar baseRadius \u003d 10.0;\nvar pricePerBlock \u003d 0.01;\nvar crossDimensionMultiplier \u003d 1.5;\nvar homeMultiplier \u003d 0.5;\nvar backMultiplier \u003d 0.8;\nvar warpMultiplier \u003d 0.5;\nvar netherCoordinateScale \u003d 8.0;\n\nvar crossDimension \u003d from.dimension() !\u003d to.dimension();\nvar deltaX \u003d from.x() - to.x();\nvar deltaY \u003d from.y() - to.y();\nvar deltaZ \u003d from.z() - to.z();\n\nif (crossDimension) {\n if (from.dimension() \u003d\u003d \"minecraft:the_end\") {\n deltaX \u003d from.x();\n deltaY \u003d from.y();\n deltaZ \u003d from.z();\n } else if (to.dimension() \u003d\u003d \"minecraft:the_end\") {\n deltaX \u003d to.x();\n deltaY \u003d to.y();\n deltaZ \u003d to.z();\n } else if (from.dimension() \u003d\u003d \"minecraft:the_nether\") {\n deltaX \u003d from.x() * netherCoordinateScale - to.x();\n deltaY \u003d from.y() * netherCoordinateScale - to.y();\n deltaZ \u003d from.z() * netherCoordinateScale - to.z();\n } else if (to.dimension() \u003d\u003d \"minecraft:the_nether\") {\n deltaX \u003d from.x() - to.x() / netherCoordinateScale;\n deltaY \u003d from.y() - to.y() / netherCoordinateScale;\n deltaZ \u003d from.z() - to.z() / netherCoordinateScale;\n }\n}\n\nvar distance \u003d math:sqrt(deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ);\nvar multiplier \u003d crossDimension ? crossDimensionMultiplier : 1.0;\n\nif (context.home() !\u003d null) {\n multiplier \u003d multiplier * homeMultiplier;\n} else if (context.back() !\u003d null) {\n multiplier \u003d multiplier * backMultiplier;\n} else if (context.warp() !\u003d null) {\n multiplier \u003d multiplier * warpMultiplier;\n}\n\nvar distanceBeyondBase \u003d distance \u003e baseRadius ? distance - baseRadius : 0;\n\ncallback.onSuccess() +\u003d () -\u003e {\n minecraft:execute(\n \"effect give \" + player.name() + \" minecraft:weakness 1 1\"\n );\n};\n\ncallback.onFailure() +\u003d () -\u003e {\n minecraft:execute(\"say hi\");\n};\n\nmath:round((basePrice + distanceBeyondBase * pricePerBlock) * multiplier).intValue();",
"deduction": {
"allowEnderChest": true,
"prioritizeEnderChest": true,
"allowShulkerBox": false,
"prioritizeShulkerBox": false
}
}
}
Configuration Details
General Settings
| Field | Type | Description |
|---|---|---|
language |
string |
Automatically discovered bundled language locale (for example en_us); affects messages, SGUI, and help text. |
helpCommand |
string |
Command used to display the PayTp guide (default /ptphelp). |
safeTeleport |
boolean |
Move an unsafe destination to the nearest safe position; defaults to false. |
safeTeleportRange |
int |
Maximum horizontal and vertical safe-position search range; defaults to 5 and must be from 1 to 64. |
Adding a Language
Add <locale>.json under src/main/resources/assets/pay-to-teleport/lang/. The locale filename must match [a-z0-9][a-z0-9_-]*, and every file must contain a non-blank paytp.language.name used by the Mod Menu selector. PayTp discovers all matching JSON files from the mod container automatically; adding a language does not require editing a Java enum, Gson adapter, or UI registry. en_us.json is required as the fallback. Missing keys in another language fall back to English and produce a one-time server warning.
Teleport Effects (general.effect)
| Field | Type | Description |
|---|---|---|
particleEffect |
boolean |
Enable teleport particles; defaults to true. |
soundEffect |
boolean |
Enable teleport sounds; defaults to true. |
Coordinate Teleport
| Field | Type | Description |
|---|---|---|
coordinateCommand |
string |
Coordinate teleport command (default /ptp). |
allowCrossDim |
boolean |
Whether any teleport method may cross dimensions; defaults to true. When disabled, the dimension argument is not registered or displayed. |
Teleport Request System
Request Commands
| Field | Type | Description |
|---|---|---|
toCommand |
string |
Command to request teleporting to the target player (default /ptpto). |
hereCommand |
string |
Command to request the target player to teleport to your location (default /ptphere). |
acceptCommand |
string |
Command to accept a request (default /ptpaccept). |
denyCommand |
string |
Command to deny a request (default /ptpdeny). |
cancelCommand |
string |
Command to cancel a sent request (default /ptpcancel). |
Configuration
| Field | Type | Description |
|---|---|---|
expireTime |
int |
Request expiration time in seconds; defaults to 10 and must be non-negative. |
Home System
| Field | Type | Description |
|---|---|---|
homeCommand |
string |
Command to teleport home (default /ptphome). |
setRespawnPoint |
boolean |
Also move the player's respawn point when setting home; defaults to false. When the player respawns at Home, the vanilla bed stand-up position resolver is always used; if it finds no valid position, the player is moved to the world spawn point. |
Back System
| Field | Type | Description |
|---|---|---|
backCommand |
string |
Command to return to previous location (default /ptpback). |
maxBackStack |
int |
Maximum saved historical positions; defaults to 10 and must be greater than zero. |
Waypoint System
| Field | Type | Description |
|---|---|---|
warpCommand |
string |
Command name to teleport to a waypoint (default /ptpwarp). |
serverWarpPermission |
int |
Minecraft permission level required to create server waypoints and force-delete waypoints; ranges from 0 to 4 and defaults to 2. |
autoDeleteInactiveWarps |
boolean |
Delete a waypoint after its beacon exceeds the inactivity timeout; defaults to true. When disabled, it remains unavailable until the beacon reactivates. |
maxInactiveTicks |
int |
Beacon inactivity timeout in ticks; defaults to 100 and must be non-negative. |
checkPeriodTicks |
int |
Waypoint-to-beacon check interval in ticks; defaults to 20 and must be greater than zero. |
Running /ptpwarp without arguments opens a six-filter SGUI for all, server, owned, public, invited, and locked waypoints. The filters are arranged vertically in the first column as a water bucket, netherite ingot, gold ingot, iron ingot, copper ingot, and empty bucket; the second column is a cyan stained-glass divider. In the remaining 7×6 region, the top row contains Previous, the summary book, and Next, the middle 7×4 area displays 28 waypoints per page, and the bottom row places Refresh on the left and Close on the right. Every usable waypoint is an emerald block, while inactive and locked waypoints are redstone blocks. Attempting a locked waypoint reports that access has not been granted instead of claiming the waypoint does not exist. Locked entries do not reveal their owner or coordinates. Every click resolves the waypoint again through the same access, activity, safety, cross-dimension, price, and payment path used by /ptpwarp teleport <name>, so a waypoint deleted or invalidated while the menu is open cannot be used from stale menu data. The SGUI uses vanilla container packets and requires no client mod.
Cost Calculation Settings
Currency
| Field | Type | Description |
|---|---|---|
currencyItem |
string |
A valid currency item ID; defaults to minecraft:diamond. |
Price Range and Algorithm
| Field | Type | Description |
|---|---|---|
minPrice |
int |
Final lower bound for non-negative prices; defaults to 1 and must satisfy 0 <= minPrice <= maxPrice. |
maxPrice |
int |
Final upper bound; defaults to 64. Setting both minPrice and maxPrice to 0 skips the algorithm and fixes the price at 0. |
algorithm |
string |
A JEXL script that calculates distance and raw price and must return an int. |
Payment Deduction (price.deduction)
| Field | Type | Description |
|---|---|---|
allowEnderChest |
boolean |
Allow Ender Chest currency; defaults to true. |
prioritizeEnderChest |
boolean |
Prioritize Ender Chest deduction; defaults to true and requires allowEnderChest. |
allowShulkerBox |
boolean |
Allow Shulker Box currency; defaults to false. |
prioritizeShulkerBox |
boolean |
Prioritize Shulker Box deduction; defaults to false and requires allowShulkerBox. |
JEXL Price Algorithm
The price.algorithm string contains both the distance calculation and the price calculation. There are no fixed Java-side distance formulas or teleport multipliers. A script may be as simple as 10, which returns a fixed price of 10.
Available Variables
| Variable | Type | Available methods |
|---|---|---|
from |
position |
.x(), .y(), .z(), .dimension() |
to |
position |
.x(), .y(), .z(), .dimension() |
context |
context |
.coordinate(), .home(), .back(), .request(), .warp() |
player |
player |
.uuid(), .name() |
callback |
callback |
.onSuccess(), .onFailure() |
context contains five nullable nested records. Exactly one accessor returns a record for
each execution; the other four return null:
| Accessor | Record contents |
|---|---|
context.coordinate() |
Coordinate-command context; currently empty. |
context.home() |
Home context; currently empty. |
context.back() |
Back context; currently empty. |
context.request() |
otherPlayer() returns the other player with uuid() and name();isRequester() is a bool indicating whether the current player initiated the request. |
context.warp() |
name(), accessType(), and owner(); the owner has uuid() and name(). |
isRequester() is true when the current player initiated the request and false when they
accepted a request to teleport to its sender. Warp accessType() is owned, invited, server,
or public; owner() is null for an ownerless server waypoint.
Teleport Callbacks
callback.onSuccess() and callback.onFailure() are independent callback chains for each teleport. Use += to append
any number of JEXL lambdas. Save a lambda to a variable first if it may later be removed with -=:
var audit = () -> {
minecraft:execute("scoreboard players add " + player.name() + " tp_success 1");
};
callback.onSuccess() += audit;
callback.onSuccess() += () -> {
minecraft:execute("tell " + player.name() + " Teleport completed");
};
callback.onSuccess() -= audit;
callback.onFailure() += () -> {
minecraft:execute("scoreboard players add " + player.name() + " tp_failed 1");
};
Callbacks execute in registration order. Removing one requires the same lambda object; repeating
the same lambda expression creates a different object. A callback error is logged without stopping
later callbacks. The price script runs before destination validation so that an unavailable world,
missing safe destination, or disabled cross-dimension teleport can invoke callback.onFailure();
to therefore represents the originally requested destination. No payment is taken until all
destination checks pass. A maxPrice of 0 bypasses the price script, so no callbacks are
registered.
crossDimension is intentionally not provided. Determine it inside the script:
var crossDimension = from.dimension() != to.dimension();
Return Value and Strict Mode
- The last expression must return an
int. Integer literals such as10already satisfy this requirement. - Returning a negative integer cancels the payment and teleport. This allows an algorithm or Minecraft command result to deliberately stop the operation without causing a script error.
- JEXL runs in strict mode, so undefined variables and invalid expressions are treated as errors.
Math Methods
[!TIP] Use
mathfor distance, multiplier, rounding, and boundary calculations directly inside the price algorithm without invoking an external process.
The math namespace exposes Java's built-in Math methods.
| Method | Return type | Description |
|---|---|---|
math:sqrt(value) |
double |
Returns the square root of a value. |
math:max(first, second) |
numeric | Returns the greater of two values. |
math:min(first, second) |
numeric | Returns the smaller of two values. |
math:round(value) |
long |
Returns the closest integer value. |
math:pow(base, exponent) |
double |
Raises base to the supplied power. |
math:abs(value) |
numeric | Returns the absolute value. |
Methods may return a numeric type other than int. Convert the final result explicitly when
necessary:
math:round(rawPrice).intValue();
Minecraft Commands
[!WARNING] The
minecraftnamespace can execute every command registered with the server, including commands added by other mods. Commands run as the server console withALL_PERMISSIONS, so they can perform destructive or administrative operations such asop,ban,data, andstop. Only use algorithms from fully trusted sources.
| Method | Return type | Description |
|---|---|---|
minecraft:execute(command) |
int |
Executes a Minecraft command. The leading / is optional and command feedback is suppressed. |
The server—not the teleported player—is the command source, so @s cannot be used to refer to the
player. Use the algorithm's player.name() when a command needs a player target. Commands involving
locations or dimensions should specify their target, coordinates, and dimension explicitly instead
of relying on player-relative command context:
minecraft:execute("scoreboard players add " + player.name() + " teleport_count 1");
minecraft:execute("effect give " + player.name() + " minecraft:regeneration 5 0");
10;
During configuration validation, minecraft:execute(...) returns 0 without running the command,
so loading or editing an algorithm cannot modify server state.
Shell Commands
[!CAUTION] The
shellnamespace executes arbitrary commands with the same operating-system permissions as the Minecraft server. Only use algorithms from fully trusted sources. Shell commands are synchronous and therefore block the server thread until they finish. They are also executed during algorithm validation, including config loading, editing, and importing.
| Method | Return type | Description |
|---|---|---|
shell:execute(command) |
ShellResult |
Executes through /bin/sh -c on Unix-like systems or cmd.exe /c on Windows. |
shell:run(command) |
string |
Returns standard output, or throws an error when the exit code is non-zero. |
shell:runInt(command) |
int |
Requires standard output to contain exactly one valid integer. |
ShellResult exposes exitCode, stdout, and stderr:
var result = shell:execute("python3 /opt/paytp/price.py");
result.exitCode == 0 ? result.stdoutInt() : 0;
For a price algorithm, runInt is usually the simplest form:
shell:runInt("python3 /opt/paytp/price.py '" + player.name() + "'");
Example Algorithm
// Available variables:
//
// Variable | Available methods
// -------- | -----------------------------------------------------------
// from | .x(), .y(), .z(), .dimension()
// to | .x(), .y(), .z(), .dimension()
// context | .coordinate(), .home(), .back(), .request(), .warp()
// player | .uuid(), .name()
// callback | .onSuccess(), .onFailure()
//
// Java's built-in Math methods are available through the "math" namespace.
// Minecraft commands are available through minecraft:execute("command").
// Full system shell access is available through the "shell" namespace.
var basePrice = 1;
var baseRadius = 10.0;
var pricePerBlock = 0.01;
var crossDimensionMultiplier = 1.5;
var homeMultiplier = 0.5;
var backMultiplier = 0.8;
var warpMultiplier = 0.5;
var netherCoordinateScale = 8.0;
var crossDimension = from.dimension() != to.dimension();
var deltaX = from.x() - to.x();
var deltaY = from.y() - to.y();
var deltaZ = from.z() - to.z();
if (crossDimension) {
if (from.dimension() == "minecraft:the_end") {
deltaX = from.x();
deltaY = from.y();
deltaZ = from.z();
} else if (to.dimension() == "minecraft:the_end") {
deltaX = to.x();
deltaY = to.y();
deltaZ = to.z();
} else if (from.dimension() == "minecraft:the_nether") {
deltaX = from.x() * netherCoordinateScale - to.x();
deltaY = from.y() * netherCoordinateScale - to.y();
deltaZ = from.z() * netherCoordinateScale - to.z();
} else if (to.dimension() == "minecraft:the_nether") {
deltaX = from.x() - to.x() / netherCoordinateScale;
deltaY = from.y() - to.y() / netherCoordinateScale;
deltaZ = from.z() - to.z() / netherCoordinateScale;
}
}
var distance = math:sqrt(deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ);
var multiplier = crossDimension ? crossDimensionMultiplier : 1.0;
if (context.home() != null) {
multiplier = multiplier * homeMultiplier;
} else if (context.back() != null) {
multiplier = multiplier * backMultiplier;
} else if (context.warp() != null) {
multiplier = multiplier * warpMultiplier;
}
callback.onSuccess() += () -> {
minecraft:execute("effect give " + player.name() + " minecraft:weakness 1 1");
};
var distanceBeyondBase = distance > baseRadius ? distance - baseRadius : 0;
math:round((basePrice + distanceBeyondBase * pricePerBlock) * multiplier).intValue();
The default algorithm additionally handles Nether coordinate scaling and The End. It is written into a newly generated configuration file and can be used as a complete starting template.
Validation and Final Price
- If
maxPriceis0, the algorithm is not executed and every teleport costs0. - A negative script result cancels the payment and teleport without being clamped.
- Otherwise, the script result is forcibly clamped to the inclusive range
[minPrice, maxPrice]. - The script is compiled and test-executed when the configuration is validated. In Mod Menu, invalid input is marked red and prevents saving.
- If an algorithm fails during an actual teleport,
calculatePricereturns-1; PayTp logs the error, cancels the teleport, and notifies the player that the payment process failed.
Cloth Config Support
If the Cloth Config API is installed on a client, all settings can be adjusted through Mod Menu. The screen edits only that client's local config/paytp.json; it cannot modify a remote dedicated server. Saving validates and atomically writes UTF-8 JSON but does not hot-reload a running world or server. Leave and reopen a single-player/LAN world, or restart the dedicated server whose file was edited, to apply changes. The price algorithm can be edited in a dedicated multi-line editor or imported from a .jexl file; invalid algorithms cannot be saved.
Compatibility & Deployment
| Type | Supported |
|---|---|
| Fabric Loader | Yes |
| Server Only | Yes; unmodified vanilla clients can connect |
| Client UI (Cloth Config) | Optional; local configuration only |
| Multi-language Support | Automatically discovered bundled JSON locales |
| Minecraft Version | 26.1+ 1.21.4 ~ 1.21.11 (legacy) |
PB4 SGUI is bundled inside the PayTp server JAR and communicates through vanilla container packets. PayTp registers only vanilla Brigadier argument types, so neither waypoint names nor the SGUI introduce a client installation requirement.
Credits
This mod is inspired by early economy-style teleport plugins. The request logic references the Teleport Command mod. The waypoint logic references the Beacon Waypoint mod.
Developed using Fabric API and fully compatible with vanilla saves.
Feel free to submit issues or pull requests on GitHub to improve configuration and calculation algorithms.


