Gameplay API
The plugin-facing gameplay API for players, items, inventories, effects, blocks, worlds, and entities.
In this section
Related section pages:
Entry Points
This page documents the plugin-facing gameplay API for players, items, inventories, effects, blocks, worlds, and entities. Use ctx.Gameplay() and let server services own gameplay state mutations.
func (p *Plugin) Init(ctx plugin.Context) error { gameplay := ctx.Gameplay() players := gameplay.Players() worlds := gameplay.Worlds() entities := gameplay.Entities() _ = players _ = worlds _ = entities return nil}Players
Resolve players by id, name, XUID, or UUID and then access state through handle methods.
playerHandle, ok := ctx.Gameplay().Players().Resolve("Steve")if !ok { return fmt.Errorf("player not found")}identity, _ := playerHandle.Identity()position, _ := playerHandle.Position()worldHandle, _ := playerHandle.World()sessionHandle, hasSession := playerHandle.Session()err := playerHandle.SendMessage("Welcome")err = playerHandle.SendPopup("Round starting")err = playerHandle.SendTip("3...")err = playerHandle.SendToast("Duel", "Opponent found")err = playerHandle.Teleport(entity.Position{X: 0, Y: 80, Z: 0})err = playerHandle.Transfer("arena-1")err = playerHandle.SetHealth(20)err = playerHandle.SetMaxHealth(20)err = playerHandle.SetGameMode("creative")if hasSession { protocol := sessionHandle.ProtocolID() extra := sessionHandle.ExtraData() _ = protocol _ = extra}_ = identity_ = position_ = worldHandleSnapshot-based getters may return copies. Mutating raw low-level objects directly does not always sync to clients.
Inventories And Items
Build stacks with core/item builders, call PlayerHandle.Inventory() for live inventory mutations, and use ctx.Items() for aliases and cooldowns.
inventory := playerHandle.Inventory()_, err := inventory.Give(item.New("diamond_sword"). Enchant(item.Sharpness(5)). NameTag("Arena Blade"). Lore("Bound to the arena"). NBTTag("pulse:owner", "Steve"). Component("minecraft:foil", true). Stack())_, err = inventory.Take(item.New("diamond").Count(1).Stack())err = inventory.Set(0, item.New("stone").Count(64).Stack())_, err = inventory.Move(0, 1, 16)_, err = inventory.Swap(1, 2)_, err = inventory.ClearSlot(2)err = inventory.SetHeldSlot(1)err = inventory.Resync()held, err := inventory.HeldItem()slot, err := inventory.HeldSlot()snapshot, err := inventory.Snapshot()_ = held_ = slot_ = snapshoterr := ctx.Items().Alias("pearl", "minecraft:ender_pearl")ctx.Items().SetCooldown(playerHandle.ID(), "pearl", 20)if ctx.Items().Ready(playerHandle.ID(), "pearl") { // allow use}Effects
Effects are server-owned and synchronized to Bedrock through the effect service.
_, err := playerHandle.AddEffect(effect.New("speed"). Seconds(30). Amplifier(1). Particles(true))active := playerHandle.Effects()_, err = playerHandle.RemoveEffect(effect.EffectSpeed)err = playerHandle.ClearEffects()_ = activeUse ctx.RegisterEffect(content.DefineEffect(...)) in Init for custom effects.
_, err := ctx.RegisterEffect(content.DefineEffect("practice:focus"). BedrockID(200). Spec())Blocks And Worlds
Use world handles for block reads/writes, world-local queries, and world effects.
worldHandle, ok := ctx.Gameplay().Worlds().ByName("Overworld")if !ok { return fmt.Errorf("world not found")}pos := world.Pos{X: 10, Y: 64, Z: 10}blockState := block.New("chest"). String("minecraft:cardinal_direction", "north"). Block()current, exists, err := worldHandle.Block(pos)err = worldHandle.SetBlock(pos, blockState)err = worldHandle.UpdateProperties(pos, map[string]any{ "open_bit": true,})_ = current_ = existsplaced, err := worldHandle.PlaceBlock(playerHandle.ID(), pos, block.New("stone").Block())broken, err := worldHandle.BreakBlock(playerHandle.ID(), pos, item.New("diamond_pickaxe").Stack())err = worldHandle.AddSound(entity.Position{X: 10, Y: 64, Z: 10}, gameplay.SoundEffect{Type: 1})err = worldHandle.AddParticle(entity.Position{X: 10, Y: 64, Z: 10}, gameplay.ParticleEffect{Type: 2001})err = worldHandle.LevelEvent(entity.Position{X: 10, Y: 64, Z: 10}, gameplay.LevelEvent{Type: 2001})_ = placed_ = brokenplayers := worldHandle.Players()nearbyPlayers := worldHandle.PlayersNear(entity.Position{X: 0, Y: 64, Z: 0}, 16, 5)nearbyEntities := worldHandle.NearbyEntities(entity.Position{X: 0, Y: 64, Z: 0}, 16, 10)zombies := worldHandle.EntitiesByType("minecraft:zombie")_ = players_ = nearbyPlayers_ = nearbyEntities_ = zombiesCustom Blocks
Register blocks in Init and expose behavior via content hooks.
_, err := ctx.RegisterBlock(content.DefineBlock("practice:ruby_block"). Hardness(4). Tool("pickaxe"). Drop(item.New("practice:ruby").Stack()). OnPlace(func(event content.BlockContext) error { if event.Player != nil { if handle, ok := ctx.Gameplay().Players().Get(event.Player.Identity().ID); ok { return handle.SendMessage("Placed ruby") } } return nil }). OnBreak(func(event content.BlockContext) error { return nil }). OnInteract(func(event content.BlockContext) error { if event.World == nil { return nil } worldHandle, ok := ctx.Gameplay().Worlds().Get(event.World.ID()) if !ok { return nil } return worldHandle.SetBlock(event.Pos, block.New("gold_block").Block()) }). Spec())BlockContext provides player, world, position, block and item details when interactions happen.
_, err := ctx.RegisterBlock(content.DefineBlock("practice:crate"). Container(27). Drop(item.New("practice:crate").Stack()). Spec())container := worldHandle.ContainerAt(pos, 27)_, err := container.Give(item.New("diamond").Count(8).Stack())err = container.OpenFor(playerHandle.ID())err = container.Resync()Custom Items
Items use content registration with hook handlers for use/use-on-block/attack flows.
_, err := ctx.RegisterItem(content.DefineItem("practice:wand"). MaxCount(1). Component("minecraft:foil", true). OnUse(func(event content.ItemContext) error { event.Result.Consume(1) if event.Player == nil { return nil } playerHandle, ok := ctx.Gameplay().Players().Get(event.Player.Identity().ID) if !ok { return nil } return playerHandle.SendMessage("Used wand") }). OnUseOnBlock(func(event content.ItemContext) error { if event.World == nil { return nil } worldHandle, ok := ctx.Gameplay().Worlds().Get(event.World.ID()) if !ok { return nil } return worldHandle.SetBlock(event.ClickedBlock, block.New("gold_block").Block()) }). OnAttack(func(event content.ItemContext) error { return nil }). Spec())ItemContext carries player/world/position/block state and mutable action result.
event.Result.Cancel("not allowed")event.Result.Consume(1)event.Result.Damage(1)event.Result.Replace(item.New("bucket").Stack())event.Result.Return(item.New("glass_bottle").Stack())Projectile Items
Projectile item specs define throw force, projectile type, cooldown and hit handler behavior.
_, err := ctx.RegisterItem(content.DefineProjectileItem("practice:fireball"). MaxCount(16). Cooldown(20). ThrowForce(1.5). Projectile("practice:fireball_projectile"). OnHit(func(event content.ProjectileHitContext) error { if event.World == nil { return nil } worldHandle, ok := ctx.Gameplay().Worlds().Get(event.World.ID()) if !ok { return nil } return worldHandle.LevelEvent(entity.Position{ X: float64(event.Pos.X), Y: float64(event.Pos.Y), Z: float64(event.Pos.Z), }, gameplay.LevelEvent{Type: 2001}) }). Spec())Entities
Spawn entities from world handles and perform movement, health, effect, and lifecycle operations.
mob, err := worldHandle.SpawnMob(entity.Zombie(). At(12, 65, 10). Health(20))err = mob.SetNameTag("Target")err = mob.SetProperty("pulse:arena", "duel-1")err = mob.Teleport(entity.Position{X: 20, Y: 65, Z: 20})_, err = mob.Knockback(entity.Position{X: 0, Y: 65, Z: 0}, 0.4, 0.36)err = mob.Damage(2, "practice")err = mob.Despawn()Scoreboards And Forms
Use scoreboards for quick on-screen state and forms for structured player interaction.
board := playerHandle.Scoreboard()err := board.Set("Duel", "Map: nodebuff", "Ping: 20ms")err = board.SetTitle("Ranked Duel")err = board.SetLine(1, "Ping: 18ms")err = board.Remove()result, err := playerHandle.SendForm(formRef)err = playerHandle.CloseForms()_ = resultEvents And Hooks
- Content hooks: OnUse, OnUseOnBlock, OnAttack, OnPlace, OnBreak, OnInteract, and projectile hit hooks.
- Event bus:
ctx.On(...),ctx.OnPriority(...),ctx.OnWithOptions(...)for cross-cutting behavior. - Prefer content hooks when behavior is local to one item/block; prefer event bus for broad behavior across many gameplay objects.
Compatibility Methods
Request-style methods on plugin.Gameplay remain for existing plugins.
- GiveStack
- SetSlot
- SetHeldSlot
- ResyncInventory
- SetBlockAt
- PlaceBlock
- BreakBlock
- AddPlayerEffect
- RemoveEffect
- ClearEffects
- Teleport
- SetHealth
- SetGameMode
Stability Notes
- Player/world/entity handles are server-service facades, not bare object pointers.
- Snapshot methods return cloned data; changing a snapshot does not mutate server state.
- Custom content definitions should be registered during
Init. - Direct mutation can become server-visible but remain unsynced to Bedrock clients without the runtime path.