Ceremony scripts react to the game world through events. Every time a block breaks, a player joins, an entity dies, or a Cobblemon is captured, an event fires. Your scripts subscribe to these events with callbacks, and Ceremony handles the rest.
Use Events.on() to subscribe to an event. It returns a subscription ID that you can later pass to Events.off() to unsubscribe.
const id = Events . on ( ' blockBreak ' , ( player , pos , state , level ) => {
Logger . info ( ' Block broken by {} at {} ' , player . getName () . getString () , pos ) ;
Many events come in pairs: a before variant and an after variant.
Pattern Timing Cancellable Example eventNameBeforeBefore the action happens Yes blockBreakBeforeeventNameAfter the action happened No blockBreakeventNameCanceledAfter a before-event was cancelled No blockBreakCanceled
Before events let you prevent the action from happening by returning false (or a falsy value) from the callback:
Events . on ( ' blockBreakBefore ' , ( player , pos , state , level ) => {
// Prevent breaking diamond ore
if ( state . getBlock () . toString () . includes ( ' diamond_ore ' )) {
player . sendSystemMessage (
Component . literal ( ' You cannot break diamond ore here! ' )
return false ; // Cancel the block break
return true ; // Allow everything else
After events fire once the action is complete. They cannot be cancelled — the action already happened.
Events . on ( ' blockBreak ' , ( player , pos , state , level ) => {
// React to the block that was already broken
Logger . info ( ' {} broke a block at {} ' , player . getName () . getString (), pos );
Event Name Callback Signature Description blockBreak(player, pos, state, level)After a block is broken blockBreakBefore(player, pos, state, level)Before a block is broken (cancellable) blockBreakCanceled(player, pos, state, level)After a block break was cancelled attackBlock(player, pos, direction, level)Player left-clicks a block useBlock(player, pos, hand, hitResult, level)Player right-clicks a block useItem(player, hand, stack, level)Player right-clicks with an item blockEntityLoad(blockEntity, level)A block entity is loaded into the world blockEntityUnload(blockEntity, level)A block entity is unloaded from the world
Parameter Type Description playerServerPlayer The player performing the action posBlockPos The block position (pos.getX(), pos.getY(), pos.getZ()) stateBlockState The block state (use state.getBlock() for the block type) levelServerLevel The world/dimension directionDirection The face of the block that was clicked handInteractionHand MAIN_HAND or OFF_HANDhitResultBlockHitResult Detailed hit information (exact position, face, etc.) stackItemStack The item stack used blockEntityBlockEntity The block entity (chest, sign, etc.)
Event Name Callback Signature Description attackEntity(player, entity, hand, level)Player left-clicks an entity useEntity(player, entity, hand, hitResult, level)Player right-clicks an entity
Events . on ( ' useEntity ' , ( player , entity , hand , hitResult , level ) => {
const name = entity . getName () . getString ();
Logger . info ( ' {} interacted with {} ' , player . getName () . getString (), name );
Event Name Callback Signature Description playerJoin(player)Player joins the server playerLeave(player)Player disconnects from the server playerRespawn(oldPlayer, newPlayer, alive)After a player respawns playerCopyData(oldPlayer, newPlayer, alive)Data copy on respawn or dimension change playerChangeDimension(player, origin, destination)After a player changes dimension
Parameter Type Description playerServerPlayer The player instance oldPlayerServerPlayer The player instance before respawn/copy newPlayerServerPlayer The new player instance after respawn/copy aliveboolean true if the player was alive (e.g., dimension change), false if they diedoriginResourceKey The dimension they came from destinationResourceKey The dimension they went to
Event Name Callback Signature Description entityLoad(entity, level)Entity added to the world entityUnload(entity, level)Entity removed from the world entityChangeDimension(originalEntity, newEntity, origin, destination)Entity changes dimension entityDamage(entity, source, amount)Living entity about to take damage entityDeath(entity, source, amount)Living entity about to die entityDeathAfter(entity, source)Living entity has died entityKilledOther(level, killer, killed)An entity kills another entity
Parameter Type Description entityEntity / LivingEntity The entity involved sourceDamageSource The damage source (.getEntity() for attacker, .type() for damage type) amountnumber The damage amount originalEntityEntity The entity before dimension change newEntityEntity The entity after dimension change originResourceKey The origin dimension destinationResourceKey The destination dimension levelServerLevel The world killerEntity The entity that dealt the killing blow killedLivingEntity The entity that was killed
Event Name Callback Signature Description worldLoad(server, level)A world/dimension is loaded worldUnload(server, level)A world/dimension is unloaded chunkLoad(level, chunk)A chunk is loaded chunkUnload(level, chunk)A chunk is unloaded
Parameter Type Description serverMinecraftServer The server instance levelServerLevel The world/dimension chunkLevelChunk The chunk that was loaded or unloaded
Event Name Callback Signature Description serverStarted(server)Server has fully started serverStopping(server)Server is shutting down dataPackReload(server, success)Datapack reload finished beforeSave(server, flush, force)Before the world is saved afterSave(server, flush, force)After the world is saved serverTick(server)Fires every server tick worldTick(level)Fires every world tick
Parameter Type Description serverMinecraftServer The server instance successboolean Whether the datapack reload succeeded flushboolean Whether this is a flush save forceboolean Whether the save was forced levelServerLevel The world being ticked
// Run logic every 5 seconds (100 ticks) instead of every tick
Events . on ( ' serverTick ' , ( server ) => {
if ( tickCount % 100 !== 0 ) return ;
// Your logic here, runs every 5 seconds
Event Name Callback Signature Description chatMessage(message, player, params)A player sends a chat message commandMessage(message, source, params)A command message is sent (/say, /me, /msg) gameMessage(server, message, overlay)A system message (death, join, advancement)
Parameter Type Description messagePlayerChatMessage / Component The message content playerServerPlayer The player who sent the message sourceCommandSourceStack The command source paramsChatDecorator parameters Decoration/formatting parameters serverMinecraftServer The server instance overlayboolean Whether this is an overlay (action bar) message
Cobblemon events differ from vanilla Minecraft events. They pass a single event object as the callback parameter, with properties you access directly from that object.
Event Name Callback Signature Description battleVictory(event)A battle was won battleFled(event)A player fled from battle battleStarted(event)A battle has started pokeBallCaptureCalculated(event)Capture chance calculated (modify odds) pokemonCaptured(event)A Pokemon was captured pokemonSpawned(event)A wild Pokemon spawned shinyChance(event)Shiny chance is being calculated (modify rate) fishingBobberLand(event)Fishing bobber lands in water fishingBite(event)Fish bites the bobber evolutionComplete(event)A Pokemon finished evolving evolutionAccepted(event)Player accepted an evolution eggHatch(event)An egg has hatched eggCollect(event)An egg was collected from the daycare rideApplyStamina(event)Ride stamina is being applied
Exploring Cobblemon Event Objects
Use Logger.info(JSON.stringify(Object.keys(event))) to inspect the properties available on any Cobblemon event object. The properties vary per event type.
Events . on ( ' blockBreak ' , ( player , pos , state , level ) => {
const playerName = player . getName () . getString ();
const blockName = state . getBlock () . getName () . getString ();
Logger . info ( ' {} broke {} at {}, {}, {} ' , playerName , blockName , x , y , z );
Events . on ( ' playerJoin ' , ( player ) => {
const wrapped = wrapPlayer ( player );
wrapped . sendMessage ( ' <gold>Welcome to the server, ' + wrapped . name + ' ! ' );
wrapped . playSound ( ' minecraft:entity.player.levelup ' , {
// Track join count in player data
const joins = wrapped . data . getNumber ( ' total_joins ' ) + 1 ;
wrapped . data . set ( ' total_joins ' , joins );
wrapped . sendActionBar ( ' Login # ' + joins );
Events . on ( ' entityKilledOther ' , ( level , killer , killed ) => {
// Only care about player kills
if ( ! killer . isPlayer ()) return ;
const player = wrapPlayer ( killer );
const entityType = killed . getType () . toString ();
// Increment kill counter
const key = ' kills_ ' + entityType ;
const kills = player . data . getNumber ( key ) + 1 ;
player . data . set ( key , kills );
// Reward every 50 kills of the same type
player . sendMessage ( ' <green>Milestone: ' + kills + ' ' + entityType + ' defeated! ' );
player . giveItem ( ' minecraft:diamond ' , 1 );
player . playSound ( ' minecraft:ui.toast.challenge_complete ' );
Events . on ( ' battleVictory ' , ( event ) => {
const battle = event . getBattle ();
const winners = event . getWinners ();
winners . forEach ( ( actor ) => {
// Get the player entity from the battle actor
const playerEntity = actor . getPlayerEntity ();
if ( ! playerEntity ) return ;
const player = wrapPlayer ( playerEntity );
const wins = player . data . getNumber ( ' battle_wins ' ) + 1 ;
player . data . set ( ' battle_wins ' , wins );
player . sendActionBar ( ' <yellow>Wins: ' + wins );
// Bonus reward every 10 wins
player . giveItem ( ' cobblemon:rare_candy ' , 3 );
player . sendMessage ( ' <gold>Battle milestone! +3 Rare Candy ' );
Start simple: subscribe to one event and log its parameters to understand the data before writing complex logic.
Use blockBreakBefore (not blockBreak) if you need to prevent the action. The after event cannot be cancelled.
Avoid heavy computation in serverTick and worldTick. Use tick counters or timers instead.
Cobblemon events use a single event object parameter — use Object.keys(event) to discover available fields.
Always check killer.isPlayer() in entityKilledOther since the killer can be any entity (mob, arrow, etc.).