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.

entry-points.go
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.

players.go
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_ = worldHandle

Snapshot-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.

inventories.go
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_ = snapshot
items-cooldown.go
err := 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.

effects.go
_, err := playerHandle.AddEffect(effect.New("speed").	Seconds(30).	Amplifier(1).	Particles(true))active := playerHandle.Effects()_, err = playerHandle.RemoveEffect(effect.EffectSpeed)err = playerHandle.ClearEffects()_ = active

Use ctx.RegisterEffect(content.DefineEffect(...)) in Init for custom effects.

projectiles.go
_, 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.

blocks-worlds.go
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_ = exists
world-activity.go
placed, 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_ = broken
world-queries.go
players := 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_ = zombies

Custom Blocks

Register blocks in Init and expose behavior via content hooks.

custom-blocks.go
_, 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.

containers.go
_, 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.

custom-items.go
_, 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.

item-result.go
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.

projectiles.go
_, 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.

entities.go
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.

scoreboards.go
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()_ = result

Events 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.