Player Wrapper
Player Wrapper
Section titled “Player Wrapper”Ceremony provides a lightweight wrapper around Minecraft’s ServerPlayer object. The wrapper gives you a clean, script-friendly interface for common player operations — sending messages, managing inventory, teleporting, applying effects, and persisting data.
Obtaining a Wrapped Player
Section titled “Obtaining a Wrapped Player”Use the global wrapPlayer() function to wrap any ServerPlayer instance:
Events.on('playerJoin', (player) => { const p = wrapPlayer(player); p.sendMessage('<green>Welcome, ' + p.name + '!');});You receive raw ServerPlayer instances from event callbacks (player join, block break, entity interaction, etc.). Wrap them to access the simplified API.
Player Properties
Section titled “Player Properties”All properties are read-only unless noted otherwise.
| Property | Type | Description |
|---|---|---|
name | string | The player’s display name |
uuid | string | The player’s UUID as a string |
pos | Vec3Instance | Current position (x, y, z as doubles) |
blockPos | any | Current block position (integer coordinates) |
level | any | The world/dimension the player is in |
health | number | Current health points |
maxHealth | number | Maximum health points |
isAlive | boolean | Whether the player is alive |
foodLevel | number | Current hunger level (0-20) |
experienceLevel | number | Current XP level |
isCreative | boolean | Whether the player is in creative mode |
isSpectator | boolean | Whether the player is in spectator mode |
isSneaking | boolean | Whether the player is sneaking |
isSprinting | boolean | Whether the player is sprinting |
dimension | string | Dimension resource key (e.g., "minecraft:overworld") |
yaw | number | Horizontal rotation (head direction) |
pitch | number | Vertical rotation (look angle) |
lookAngle | Vec3Instance | Normalized direction vector the player is looking |
mainHandItem | any | ItemStack in main hand |
offHandItem | any | ItemStack in off hand |
raw | any | The underlying ServerPlayer Java object |
data | PlayerDataProxy | Persistent data storage proxy |
Events.on('playerJoin', (player) => { const p = wrapPlayer(player);
Logger.info('Player: {} ({})', p.name, p.uuid); Logger.info('Position: {}, {}, {}', p.pos.x, p.pos.y, p.pos.z); Logger.info('Health: {}/{}', p.health, p.maxHealth); Logger.info('Dimension: {}', p.dimension); Logger.info('Creative: {}', p.isCreative);});Player Methods
Section titled “Player Methods”sendMessage(text)
Section titled “sendMessage(text)”Sends a chat message to the player. Supports MiniMessage formatting.
p.sendMessage('<gold>Quest complete!');p.sendMessage('<red>You have been warned.');p.sendMessage('Plain text works too.');sendActionBar(text)
Section titled “sendActionBar(text)”Sends a message to the action bar (above the hotbar). Supports MiniMessage formatting.
p.sendActionBar('<green>+50 XP');playSound(sound, opts?)
Section titled “playSound(sound, opts?)”Plays a sound to the player. The optional opts object controls volume and pitch.
| Option | Type | Default | Description |
|---|---|---|---|
volume | number | 1.0 | Volume multiplier |
pitch | number | 1.0 | Pitch multiplier |
p.playSound('minecraft:entity.player.levelup');p.playSound('minecraft:entity.experience_orb.pickup', { volume: 0.5, pitch: 1.5 });p.playSound('minecraft:block.note_block.pling', { volume: 1.0, pitch: 2.0 });teleport(x, y, z, dimension?)
Section titled “teleport(x, y, z, dimension?)”Teleports the player to the specified coordinates. Optionally specify a dimension to teleport cross-dimension.
p.teleport(100, 65, 200);p.teleport(0, 100, 0, 'minecraft:the_nether');p.teleport(0, 64, 0, 'minecraft:the_end');giveItem(item, count?)
Section titled “giveItem(item, count?)”Gives the player an item. The count parameter defaults to 1.
p.giveItem('minecraft:diamond', 5);p.giveItem('minecraft:diamond_sword');p.giveItem('cobblemon:poke_ball', 20);hasItem(item, count?)
Section titled “hasItem(item, count?)”Checks whether the player has at least count of the specified item (default 1). Returns a boolean.
if (p.hasItem('minecraft:diamond', 10)) { p.sendMessage('<green>You have enough diamonds!');}removeItem(item, count?)
Section titled “removeItem(item, count?)”Removes up to count of the specified item from the player’s inventory (default 1). Returns the number of items actually removed.
const removed = p.removeItem('minecraft:diamond', 5);if (removed === 5) { p.sendMessage('<green>Payment accepted.');} else { p.sendMessage('<red>Not enough diamonds!'); p.giveItem('minecraft:diamond', removed); // Refund}addEffect(effect, opts?)
Section titled “addEffect(effect, opts?)”Applies a potion effect to the player.
| Option | Type | Default | Description |
|---|---|---|---|
duration | number | 200 | Duration in ticks (20 ticks = 1 second) |
amplifier | number | 0 | Effect level (0 = level I, 1 = level II, etc.) |
ambient | boolean | false | Whether particles are translucent (like beacon effects) |
visible | boolean | true | Whether particles are visible |
p.addEffect('minecraft:speed', { duration: 600, amplifier: 1 });p.addEffect('minecraft:regeneration', { duration: 200, amplifier: 0 });p.addEffect('minecraft:night_vision', { duration: 6000, visible: false });removeEffect(effect)
Section titled “removeEffect(effect)”Removes a potion effect from the player.
p.removeEffect('minecraft:speed');executeCommand(command)
Section titled “executeCommand(command)”Executes a command as the player. The command string should not include the leading slash.
p.executeCommand('give @s minecraft:golden_apple 1');p.executeCommand('title @s title {"text":"Boss Fight","color":"red"}');p.executeCommand('playsound minecraft:entity.ender_dragon.growl master @s');Methods Reference Table
Section titled “Methods Reference Table”| Method | Signature | Returns | Description |
|---|---|---|---|
sendMessage | (text: string) | void | Send chat message |
sendActionBar | (text: string) | void | Send action bar text |
playSound | (sound: string, opts?: SoundOpts) | void | Play a sound |
teleport | (x: number, y: number, z: number, dimension?: string) | void | Teleport player |
giveItem | (item: string, count?: number) | void | Give item to inventory |
hasItem | (item: string, count?: number) | boolean | Check if player has item |
removeItem | (item: string, count?: number) | number | Remove item from inventory |
addEffect | (effect: string, opts?: EffectOpts) | void | Apply potion effect |
removeEffect | (effect: string) | void | Remove potion effect |
executeCommand | (command: string) | void | Run command as player |
PlayerDataProxy
Section titled “PlayerDataProxy”The PlayerDataProxy is accessed through player.data on any wrapped player. It provides a simple key-value store that persists across sessions, server restarts, and (with a database backend) across servers.
Methods
Section titled “Methods”| Method | Signature | Returns | Description |
|---|---|---|---|
get | (key: string) | any | Get a value by key (returns undefined if not set) |
getNumber | (key: string) | number | Get a value as a number (returns 0 if not set) |
getBoolean | (key: string) | boolean | Get a value as a boolean (returns false if not set) |
set | (key: string, value: any) | void | Set a value |
has | (key: string) | boolean | Check if a key exists |
remove | (key: string) | void | Delete a key |
cooldown | (key: string, durationMs: number) | void | Start a cooldown timer (milliseconds) |
isOnCooldown | (key: string) | boolean | Check if a cooldown is active |
keys | () | string[] | Get all stored keys |
Basic Usage
Section titled “Basic Usage”const p = wrapPlayer(player);
// Store and retrieve valuesp.data.set('coins', 100);p.data.set('rank', 'explorer');p.data.set('tutorial_done', true);
const coins = p.data.getNumber('coins'); // 100const rank = p.data.get('rank'); // "explorer"const done = p.data.getBoolean('tutorial_done'); // trueCooldown System
Section titled “Cooldown System”The built-in cooldown system is perfect for ability cooldowns, interaction throttling, and timed mechanics.
Events.on('useBlock', (player, pos, hand, hitResult, level) => { const p = wrapPlayer(player);
// 30-second cooldown on special block interaction if (p.data.isOnCooldown('special_block')) { p.sendMessage('<red>This is on cooldown!'); return; }
p.data.cooldown('special_block', 30000); // 30,000ms = 30 seconds p.sendMessage('<green>Activated!'); p.giveItem('minecraft:diamond', 1); p.playSound('minecraft:block.beacon.activate');});Persistent Player Stats
Section titled “Persistent Player Stats”// Track and display stats on joinEvents.on('playerJoin', (player) => { const p = wrapPlayer(player);
const kills = p.data.getNumber('mob_kills'); const deaths = p.data.getNumber('deaths'); const playtime = p.data.getNumber('play_hours');
p.sendMessage('<gray>--- Your Stats ---'); p.sendMessage('<yellow>Mob Kills: <white>' + kills); p.sendMessage('<yellow>Deaths: <white>' + deaths); p.sendMessage('<yellow>Play Time: <white>' + playtime + 'h');});
// Increment deaths on respawnEvents.on('playerRespawn', (oldPlayer, newPlayer, alive) => { if (!alive) { const p = wrapPlayer(newPlayer); p.data.set('deaths', p.data.getNumber('deaths') + 1); }});PlayerData (MoLang Context)
Section titled “PlayerData (MoLang Context)”PlayerData is a separate, richer interface obtained via the global getPlayerData(player) function. It provides the same persistent storage as PlayerDataProxy but with additional convenience methods and is designed for integration with Journey’s MoLang context system.
Obtaining PlayerData
Section titled “Obtaining PlayerData”const pd = getPlayerData(player); // Pass the raw ServerPlayer, not wrappedMethods
Section titled “Methods”| Method | Signature | Returns | Description |
|---|---|---|---|
get | (key: string) | any | Get a value |
getNumber | (key: string) | number | Get as number (default 0) |
getBoolean | (key: string) | boolean | Get as boolean (default false) |
getObject | (key: string) | object | Get as object (default {}) |
set | (key: string, value: any) | void | Set a value |
setNumber | (key: string, value: number) | void | Set a number value |
setBoolean | (key: string, value: boolean) | void | Set a boolean value |
setObject | (key: string, value: object) | void | Set an object value |
has | (key: string) | boolean | Check if key exists |
remove | (key: string) | void | Delete a key |
clear | () | void | Remove all keys |
keys | () | string[] | Get all keys |
increment | (key: string) | number | Increment a number by 1, returns new value |
decrement | (key: string) | number | Decrement a number by 1, returns new value |
toggle | (key: string) | boolean | Toggle a boolean, returns new value |
setCooldown | (key: string, durationMs: number) | void | Start a cooldown |
isOnCooldown | (key: string) | boolean | Check if on cooldown |
getRemainingCooldown | (key: string) | number | Milliseconds remaining on cooldown |
clearCooldown | (key: string) | void | Cancel an active cooldown |
Increment and Decrement
Section titled “Increment and Decrement”const pd = getPlayerData(player);
// These are equivalent:pd.set('score', pd.getNumber('score') + 1);pd.increment('score'); // Shorter, returns new value
const newScore = pd.increment('score'); // Returns the incremented valueLogger.info('New score: {}', newScore);
pd.decrement('lives'); // Subtract 1Toggle
Section titled “Toggle”const pd = getPlayerData(player);
// Toggle a boolean flagconst isNowEnabled = pd.toggle('notifications_enabled');// If it was true, it's now false (and vice versa)Advanced Cooldowns
Section titled “Advanced Cooldowns”const pd = getPlayerData(player);
pd.setCooldown('heal_ability', 60000); // 60 seconds
// Check remaining timeif (pd.isOnCooldown('heal_ability')) { const remaining = pd.getRemainingCooldown('heal_ability'); const seconds = Math.ceil(remaining / 1000); wrapPlayer(player).sendMessage('<red>Heal on cooldown: ' + seconds + 's remaining');} else { // Use the ability wrapPlayer(player).addEffect('minecraft:regeneration', { duration: 100, amplifier: 1 }); pd.setCooldown('heal_ability', 60000);}
// Admin command to clear someone's cooldownpd.clearCooldown('heal_ability');Storing Complex Objects
Section titled “Storing Complex Objects”const pd = getPlayerData(player);
// Store structured datapd.setObject('home_location', { x: 100, y: 65, z: -200, dimension: 'minecraft:overworld'});
// Retrieve it laterconst home = pd.getObject('home_location');if (home.x !== undefined) { wrapPlayer(player).teleport(home.x, home.y, home.z, home.dimension);}Practical Examples
Section titled “Practical Examples”Economy System
Section titled “Economy System”function getBalance(player) { return wrapPlayer(player).data.getNumber('balance');}
function addBalance(player, amount) { const p = wrapPlayer(player); const current = p.data.getNumber('balance'); p.data.set('balance', current + amount); return current + amount;}
function removeBalance(player, amount) { const p = wrapPlayer(player); const current = p.data.getNumber('balance'); if (current < amount) return false; p.data.set('balance', current - amount); return true;}
// Earn money from killing mobsEvents.on('entityKilledOther', (level, killer, killed) => { if (!killer.isPlayer()) return; const newBalance = addBalance(killer, 10); wrapPlayer(killer).sendActionBar('<gold>+10 coins <gray>(' + newBalance + ' total)');});Inventory Check Gate
Section titled “Inventory Check Gate”Events.on('useBlock', (player, pos, hand, hitResult, level) => { const p = wrapPlayer(player);
// Check if player is at a specific location (a gate) if (pos.getX() === 100 && pos.getY() === 65 && pos.getZ() === 200) { if (p.hasItem('minecraft:nether_star', 1)) { p.removeItem('minecraft:nether_star', 1); p.sendMessage('<green>The gate opens...'); p.teleport(500, 80, 500); p.playSound('minecraft:block.end_portal.spawn'); } else { p.sendMessage('<red>You need a Nether Star to pass.'); p.playSound('minecraft:block.note_block.bass', { pitch: 0.5 }); } }});Daily Reward with Cooldown
Section titled “Daily Reward with Cooldown”Events.on('playerJoin', (player) => { const p = wrapPlayer(player); const pd = getPlayerData(player);
if (!pd.isOnCooldown('daily_reward')) { // Give daily reward p.giveItem('minecraft:diamond', 3); p.giveItem('cobblemon:poke_ball', 10);
const streak = pd.getNumber('daily_streak') + 1; pd.setNumber('daily_streak', streak);
p.sendMessage('<gold>Daily reward claimed! Streak: ' + streak); p.playSound('minecraft:entity.player.levelup');
// 24 hour cooldown pd.setCooldown('daily_reward', 24 * 60 * 60 * 1000); } else { const remaining = pd.getRemainingCooldown('daily_reward'); const hours = Math.floor(remaining / (60 * 60 * 1000)); const minutes = Math.floor((remaining % (60 * 60 * 1000)) / (60 * 1000)); p.sendMessage('<gray>Next daily reward in ' + hours + 'h ' + minutes + 'm'); }});- Use
wrapPlayer()freely — it is a lightweight proxy and does not allocate significant resources. - Prefer
player.data(PlayerDataProxy) for simple get/set operations inside event callbacks. - Use
getPlayerData(player)(PlayerData) when you needincrement,decrement,toggle, or advanced cooldown methods. - Data keys are strings. Use a consistent naming convention like
module_keynameto avoid collisions between scripts. - Cooldowns are timestamp-based and survive server restarts.
- The
rawproperty gives you the underlyingServerPlayerfor any Java method calls not covered by the wrapper.
Related
Section titled “Related”- Events Reference — Event callbacks that provide player instances
- Helpers & Registries — Vec3, Logger, State, and more
- Ceremony Integration — Cross-server player data sync