API reference
Every script gets one global: ps. It is the only bridge between your code and the server. Types ship with @paperscript/sdk — with them installed your editor autocompletes everything on this page.
Chat strings. Every sendMessage / broadcast accepts MiniMessage on the modern line (<red>, <gradient:#a:#b>, hover/click) and legacy &-codes on legacy hosts.
Lifecycle
ps.onEnable(fn: () => void): voidps.onDisable(fn: () => void): voidonEnable runs after the script is evaluated — register commands and listeners here. onDisable runs on /ps reload or server stop — cancel your timers and flush state there.
ps.logger
ps.logger.info(message: string): voidps.logger.warn(message: string): voidps.logger.error(message: string): voidWrites to the server console, prefixed with your script name.
ps.events
ps.events.onPlayerJoin(handler: (event: { player: Player }) => void): voidps.events.onPlayerQuit(handler: (event: { player: Player }) => void): voidps.events.onPlayerJoin((event) => {
event.player.sendMessage("<gold>Welcome, " + event.player.name + "!</gold>");
ps.server.broadcast("<gray>" + event.player.name + " joined</gray>");
});ps.commands
ps.commands.register(name, handler, description?, usage?): voidThe handler receives a CommandContext:
| Member | Type | Meaning |
|---|---|---|
| ctx.sender.name | string | Player name, or "CONSOLE" |
| ctx.sender.op | boolean | Server operator? |
| ctx.sender.player | boolean | Is an in-game player (not console) |
| ctx.sender.sendMessage(msg) | void | Reply to the sender |
| ctx.label | string | Command label as typed |
| ctx.args | string[] | All arguments |
| ctx.arg(i) | string | null | Argument at index, or null |
ps.commands.register(
"spawn",
(ctx) => {
if (!ctx.sender.player) {
ctx.sender.sendMessage("<red>Players only.</red>");
return;
}
const p = ps.players.get(ctx.sender.name);
const w = p && ps.worlds.get(p.location.world);
if (p && w) {
p.teleport(w.spawnLocation);
ctx.sender.sendMessage("<green>Teleported to spawn.</green>");
}
},
"Teleport to spawn", // description
"/spawn" // usage shown in /help
);Legacy caveat: on Nashorn hosts ctx.args is a Java array — index access and .length work, but prefer ctx.arg(i) for portability.
ps.scheduler
ps.scheduler.runTask(fn: () => void): numberps.scheduler.runTaskLater(fn: () => void, delayTicks: number): numberps.scheduler.runTaskTimer(fn: () => void, delayTicks: number, periodTicks: number): numberps.scheduler.cancelTask(taskId: number): voidAll tasks run on the server main thread — never touch Bukkit state from anything else. 20 ticks = 1 second. Every call returns a task id for cancellation.
// once, next tick
ps.scheduler.runTask(() => ps.logger.info("tick"));
// after 5 seconds (20 ticks = 1s)
const id = ps.scheduler.runTaskLater(() => {
ps.server.broadcast("<gray>5 seconds passed</gray>");
}, 20 * 5);
// repeating: start after 20t, then every 100t
const timer = ps.scheduler.runTaskTimer(
() => ps.server.broadcast("<aqua>Still running</aqua>"),
20,
100
);
ps.scheduler.cancelTask(timer);ps.players & Player
ps.players.online(): Player[]ps.players.get(name: string): Player | null| Member | Type |
|---|---|
| name, uniqueId, online | string, string, boolean (readonly) |
| health, foodLevel | number (read/write) |
| location | Location (readonly snapshot) |
| teleport(loc) | boolean — false if the teleport failed |
| heal() / feed() | void — full health / full hunger |
| allowFlight, flying | boolean (read/write) |
| gameMode | string (readonly) |
| setGameMode(mode) | boolean — false for an unknown mode name |
| sendMessage(msg) | void |
const p = ps.players.get("Steve"); // null if offline
if (p) {
p.sendMessage("<gold>Hi, " + p.name + "</gold>");
p.heal();
p.feed();
p.allowFlight = true;
p.flying = true;
p.setGameMode("creative"); // false if the mode name is unknown
const here = p.location; // { world, x, y, z, yaw, pitch }
p.teleport(ps.loc.create(here.world, here.x, here.y + 10, here.z));
}
for (const online of ps.players.online()) {
ps.logger.info(online.name + " @ " + online.location.world);
}ps.worlds & World
ps.worlds.all(): World[]ps.worlds.get(name: string): World | null| Member | Type |
|---|---|
| name | string (readonly) |
| time | number (read/write, ticks 0–24000) |
| playerCount | number (readonly) |
| spawnLocation | Location (readonly) |
| setSpawnLocation(loc) | void |
ps.loc
ps.loc.create(world: string, x, y, z): Locationps.loc.create(world: string, x, y, z, yaw, pitch): LocationA Location is a plain immutable object: { world, x, y, z, yaw, pitch } — safe to JSON-serialize into storage and pass back to teleport.
ps.server
ps.server.version: string // server implementation versionps.server.minecraftVersion: string // e.g. "1.21.1"ps.server.onlineCount: numberps.server.maxPlayers: numberps.server.broadcast(message: string): voidps.storage
ps.storage.get(key: string): string | nullps.storage.set(key: string, jsonValue: string): voidps.storage.remove(key: string): voidps.storage.save(): voidPer-script persistent key-value store. Values are JSON strings — serialize with JSON.stringify. Flushed automatically when the script is disabled; save() forces an early flush.
// values are JSON strings — serialize yourself
ps.storage.set("kills", JSON.stringify({ Steve: 3 }));
const raw = ps.storage.get("kills");
const kills = raw ? JSON.parse(raw) : {};
kills.Steve = (kills.Steve ?? 0) + 1;
ps.storage.set("kills", JSON.stringify(kills));
ps.storage.remove("kills");
ps.storage.save(); // force flush (auto-saved on disable anyway)