Skip to content

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.


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.


All properties are read-only unless noted otherwise.

PropertyTypeDescription
namestringThe player’s display name
uuidstringThe player’s UUID as a string
posVec3InstanceCurrent position (x, y, z as doubles)
blockPosanyCurrent block position (integer coordinates)
levelanyThe world/dimension the player is in
healthnumberCurrent health points
maxHealthnumberMaximum health points
isAlivebooleanWhether the player is alive
foodLevelnumberCurrent hunger level (0-20)
experienceLevelnumberCurrent XP level
isCreativebooleanWhether the player is in creative mode
isSpectatorbooleanWhether the player is in spectator mode
isSneakingbooleanWhether the player is sneaking
isSprintingbooleanWhether the player is sprinting
dimensionstringDimension resource key (e.g., "minecraft:overworld")
yawnumberHorizontal rotation (head direction)
pitchnumberVertical rotation (look angle)
lookAngleVec3InstanceNormalized direction vector the player is looking
mainHandItemanyItemStack in main hand
offHandItemanyItemStack in off hand
rawanyThe underlying ServerPlayer Java object
dataPlayerDataProxyPersistent 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);
});

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.');

Sends a message to the action bar (above the hotbar). Supports MiniMessage formatting.

p.sendActionBar('<green>+50 XP');

Plays a sound to the player. The optional opts object controls volume and pitch.

OptionTypeDefaultDescription
volumenumber1.0Volume multiplier
pitchnumber1.0Pitch 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 });

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');

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);

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!');
}

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
}

Applies a potion effect to the player.

OptionTypeDefaultDescription
durationnumber200Duration in ticks (20 ticks = 1 second)
amplifiernumber0Effect level (0 = level I, 1 = level II, etc.)
ambientbooleanfalseWhether particles are translucent (like beacon effects)
visiblebooleantrueWhether 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 });

Removes a potion effect from the player.

p.removeEffect('minecraft:speed');

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');

MethodSignatureReturnsDescription
sendMessage(text: string)voidSend chat message
sendActionBar(text: string)voidSend action bar text
playSound(sound: string, opts?: SoundOpts)voidPlay a sound
teleport(x: number, y: number, z: number, dimension?: string)voidTeleport player
giveItem(item: string, count?: number)voidGive item to inventory
hasItem(item: string, count?: number)booleanCheck if player has item
removeItem(item: string, count?: number)numberRemove item from inventory
addEffect(effect: string, opts?: EffectOpts)voidApply potion effect
removeEffect(effect: string)voidRemove potion effect
executeCommand(command: string)voidRun command as player

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.

MethodSignatureReturnsDescription
get(key: string)anyGet a value by key (returns undefined if not set)
getNumber(key: string)numberGet a value as a number (returns 0 if not set)
getBoolean(key: string)booleanGet a value as a boolean (returns false if not set)
set(key: string, value: any)voidSet a value
has(key: string)booleanCheck if a key exists
remove(key: string)voidDelete a key
cooldown(key: string, durationMs: number)voidStart a cooldown timer (milliseconds)
isOnCooldown(key: string)booleanCheck if a cooldown is active
keys()string[]Get all stored keys
const p = wrapPlayer(player);
// Store and retrieve values
p.data.set('coins', 100);
p.data.set('rank', 'explorer');
p.data.set('tutorial_done', true);
const coins = p.data.getNumber('coins'); // 100
const rank = p.data.get('rank'); // "explorer"
const done = p.data.getBoolean('tutorial_done'); // true

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');
});
// Track and display stats on join
Events.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 respawn
Events.on('playerRespawn', (oldPlayer, newPlayer, alive) => {
if (!alive) {
const p = wrapPlayer(newPlayer);
p.data.set('deaths', p.data.getNumber('deaths') + 1);
}
});

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.

const pd = getPlayerData(player); // Pass the raw ServerPlayer, not wrapped
MethodSignatureReturnsDescription
get(key: string)anyGet a value
getNumber(key: string)numberGet as number (default 0)
getBoolean(key: string)booleanGet as boolean (default false)
getObject(key: string)objectGet as object (default {})
set(key: string, value: any)voidSet a value
setNumber(key: string, value: number)voidSet a number value
setBoolean(key: string, value: boolean)voidSet a boolean value
setObject(key: string, value: object)voidSet an object value
has(key: string)booleanCheck if key exists
remove(key: string)voidDelete a key
clear()voidRemove all keys
keys()string[]Get all keys
increment(key: string)numberIncrement a number by 1, returns new value
decrement(key: string)numberDecrement a number by 1, returns new value
toggle(key: string)booleanToggle a boolean, returns new value
setCooldown(key: string, durationMs: number)voidStart a cooldown
isOnCooldown(key: string)booleanCheck if on cooldown
getRemainingCooldown(key: string)numberMilliseconds remaining on cooldown
clearCooldown(key: string)voidCancel an active cooldown
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 value
Logger.info('New score: {}', newScore);
pd.decrement('lives'); // Subtract 1
const pd = getPlayerData(player);
// Toggle a boolean flag
const isNowEnabled = pd.toggle('notifications_enabled');
// If it was true, it's now false (and vice versa)
const pd = getPlayerData(player);
pd.setCooldown('heal_ability', 60000); // 60 seconds
// Check remaining time
if (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 cooldown
pd.clearCooldown('heal_ability');
const pd = getPlayerData(player);
// Store structured data
pd.setObject('home_location', {
x: 100,
y: 65,
z: -200,
dimension: 'minecraft:overworld'
});
// Retrieve it later
const home = pd.getObject('home_location');
if (home.x !== undefined) {
wrapPlayer(player).teleport(home.x, home.y, home.z, home.dimension);
}

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 mobs
Events.on('entityKilledOther', (level, killer, killed) => {
if (!killer.isPlayer()) return;
const newBalance = addBalance(killer, 10);
wrapPlayer(killer).sendActionBar('<gold>+10 coins <gray>(' + newBalance + ' total)');
});
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 });
}
}
});
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 need increment, decrement, toggle, or advanced cooldown methods.
  • Data keys are strings. Use a consistent naming convention like module_keyname to avoid collisions between scripts.
  • Cooldowns are timestamp-based and survive server restarts.
  • The raw property gives you the underlying ServerPlayer for any Java method calls not covered by the wrapper.