class Rosegold::Bot

Overview

Start here to write a Minecraft bot.

Bot is Rosegold's main scripting API. This page is the starting point for connecting, moving, interacting, reading world state, managing inventory, and reacting to events. Use the task links below to find an operation, then its method documentation for arguments, overloads, and examples.

CivMC bots should use the restricted rosegold-civmc API and its example template. The core API below exposes world observations that CivMC bots cannot use.

Your first bot

Create a project from the example template, select the main dependency branch as shown in the installation guide, then use Bot to connect and run your script:

require "rosegold"

bot = Rosegold::Bot.new("localhost:25565")
begin
  bot.join_game
  bot.chat "Hello from Rosegold."
  bot.wait_ticks 20
ensure
  bot.disconnect("Script finished") if bot.connected?
end

Find an operation

For longer scripts, see the runnable examples and idiom guide.

Execution model

Bot forwards game events from its Client and exposes movement, inventory, interaction, and crafting operations. Most operations that wait for the server yield cooperatively through ticks rather than blocking the process. Event handlers run synchronously; use spawn inside a handler for work that waits for ticks or more packets.

Defined in:

rosegold/bot.cr
rosegold/bot/world_api.cr

Constructors

Class Method Summary

Instance Method Summary

Instance methods inherited from class Rosegold::EventEmitter

emit_event(event : Event) emit_event, event_handlers : Handlers event_handlers, off(event_type : T.class, id : UUID) forall T off, on(event_type : T.class, id : UUID = UUID.random, &block : T -> ) forall T on, once(event_type : T.class, &block : T -> ) forall T once, wait_for(event_type : T.class, timeout : Time::Span = 5.seconds) forall T
wait_for(event_type : T.class, timeout : Time::Span = 5.seconds, &) forall T
wait_for

Constructor Detail

def self.new(address : String) #

Creates a bot for address without connecting it. Call #join_game before issuing world operations.


[View source]
def self.new(client : Rosegold::Client) #

Wraps an existing client. The client may be connected later with #join_game.


[View source]

Class Method Detail

def self.join_game(address : String, timeout_ticks = 1200) #

Connects to address and waits until the player has joined the world. Raises if joining does not complete within timeout_ticks game ticks.


[View source]
def self.run(address : String, port : Int32 = 25565, offline : NamedTuple(uuid: String, username: String) | Nil = nil, timeout_ticks : Int32 = 1200, &) #

Joins address and runs the block with a fresh bot after each reconnect. A server disconnect or network error retries with backoff; authentication failures honor provider cooldowns. Other script errors propagate. Return while connected or call Bot#disconnect to stop. The block must check #connected? or perform network operations to notice a disconnect. Re-establish script-owned state each time the block runs.


[View source]

Instance Method Detail

def attack(target : Vec3d | Nil | Look | Nil = nil) #

Optionally aims at target, then presses and releases attack immediately.


[View source]
def auto_respawn=(auto_respawn : Bool) #

Whether a death automatically starts a background #respawn attempt. Enabled by default.


[View source]
def auto_respawn? : Bool #

Whether a death automatically starts a background #respawn attempt. Enabled by default.


[View source]
def block_at(position : Vec3i) : BlockSnapshot | Nil #

Returns received block state, or nil outside loaded chunks and world height. Properties use the versioned game data's string values.


[View source]
def block_at(position : Vec3d) : BlockSnapshot | Nil #

Floors fractional coordinates, including negative coordinates, to a block.


[View source]
def can_craft?(recipe : RecipeDisplayEntry) : Bool #

Whether the player inventory has one complete set of ingredients for recipe. Returns false for recipe display types that cannot be crafted in a grid.


[View source]
def chat(message : String) #

Sends a chat message or slash command without waiting for a response. Returns true when sent, false if sending fails or a chat message exceeds 256 bytes. A successful send does not confirm server acceptance.


[View source]
def client : Client #

The underlying client for protocol and engine access.


[View source]
def connect #

Opens and authenticates a connection. It returns once packet processing has started, before the player is necessarily spawned. Use #join_game when the next operation needs world state.


[View source]
def connected? #

Whether the connection exists and has not been closed. Returns nil before connecting.


[View source]
def container_type : Menu.class | Nil #

The class of the currently open non-player menu, or nil for the player inventory.


[View source]
def craft(item_name : String, count : Int32 = 1, table : Vec3i | Nil = nil) #

Attempts count recipe placements producing item_name. Each placement may produce several items; count is not an item count.

Waits for recipe placement and output slot updates. Supply table for a recipe that needs a crafting table. Raises CraftingError when no recipe, materials, or required table position is available. It can stop early when no output appears within ten ticks or the inventory cannot accept the output. Inspect inventory counts when the exact produced amount matters.


[View source]
def craft(recipe : RecipeDisplayEntry, count : Int32 = 1, table : Vec3i | Nil = nil) #

Attempts count placements of a specific synchronized recipe. Each placement may produce several result items. Waits for recipe placement and output slot updates. Raises CraftingError if materials or a required crafting table position are unavailable.


[View source]
def craft_all(item_name : String, table : Vec3i | Nil = nil) #

Crafts the maximum possible amount of a craftable recipe producing item_name. Raises CraftingError when no usable recipe, materials, or table position exists.


[View source]
def craft_all(recipe : RecipeDisplayEntry, table : Vec3i | Nil = nil) #

Crafts the maximum possible amount from a specific synchronized recipe. Raises CraftingError if its requirements cannot be met.


[View source]
def craft_pattern(pattern : Array(Array(String | Nil)), count : Int32 = 1, table : Vec3i | Nil = nil) #

Crafts by manually placing the named items from pattern into the grid. Use this for custom recipes or recipes absent from the recipe book.

The pattern is at most 3 by 3; nil leaves a grid cell empty. Provide table for patterns larger than 2 by 2. Raises CraftingError for an invalid pattern, missing item, or missing table position.

bot.craft_pattern([
  ["iron_ingot", "iron_ingot", "iron_ingot"],
  [nil, "stick", nil],
  [nil, "stick", nil],
], table: crafting_table_position)

[View source]
def dead? #

Whether the player's health is zero or below.


[View source]
def dig(ticks : Int32, target : Vec3d | Nil | Look | Nil = nil) #

Optionally aims at target, holds attack for ticks, then releases it. Waits exactly ticks client ticks after starting the dig.


[View source]
def disconnect(reason : String) #

Closes the connection with the server-visible reason.


[View source]
def disconnect_reason #

The server-supplied close reason, or nil while connected or before a connection exists.


[View source]
def drop_hand_full #

Drops the full main-hand stack. Queues packets without waiting.


[View source]
def drop_hand_single #

Drops one item from the main-hand stack. Queues packets without waiting.


[View source]
def eat : Nil #

Best-effort version of #eat!. Logs a warning if eating raises, including when no allowed food is available, and returns nil without re-raising. Uses the same hunger thresholds and food selection as #eat!. This blocks while eating; returning does not guarantee hunger was restored.


[View source]
def eat! #

Eats allowed food from inventory when #should_eat? is true. Does nothing at 18+ food, or at 15+ food with full health. Otherwise, blocks until food reaches 18, the held food runs out, or eating times out. Raises when no allowed food is available and propagates interaction errors. A timeout only logs a warning. Use #eat for best-effort eating instead.


[View source]
def effect_by_name(name) #

Finds an active status effect by its display name, ignoring case and spaces. Returns nil when the effect is absent.


[View source]
def effects #

The active status effects reported by the server.


[View source]
def enchant(spec, *, option : Int32, timeout : Time::Span = 5.seconds) : Slot #

Enchants one matching inventory item using option (0 through 2). The caller must face a reachable table, with no container open and an empty cursor. Opens, supplies, enchants, collects, and closes the table within timeout, returning a detached server-confirmed result. Books expose their enchantments through Slot#stored_enchantments.

enchanted = bot.enchant("diamond_pickaxe", option: 2)

A timeout does not undo a server-applied enchantment. This does not retry, discover a table, or predict enchantments beyond the displayed offer clue. A timed-out opening closes a late table response. Another enchant attempt is rejected until a response, acknowledgement, or disconnect settles it.


[View source]
def enchant(*, option : Int32, timeout : Time::Span = 5.seconds, &spec : Slot -> _) : Slot #

Enchants the first inventory item for which the block returns truthy.


[View source]
def entities : Array(EntitySnapshot) #

Detached snapshots of entities currently tracked in this dimension.


[View source]
def entity(id : Int) : EntitySnapshot | Nil #

Returns a detached entity snapshot, or nil if the entity is not tracked.


[View source]
def estimated_break_ticks(block_name : String, *, buffer_ticks : Int32 = 10) : Int32 #

Estimates how many ticks it will take this bot to break the named block.

Uses the bot's current main-hand item and live player state (Haste, gamemode, on_ground, etc.), so the estimate reflects what the bot would actually experience right now — not a fresh Player.new baseline.

buffer_ticks is added on top of the computed mining time to absorb network latency and server tick variance; tune it down to 0 for a pure client-side estimate, or up if you're seeing the bot release dig too early.

Raises ArgumentError if block_name isn't in the active version's MCData.

bot.estimated_break_ticks("stone")                  # => e.g. 160
bot.estimated_break_ticks("stone", buffer_ticks: 0) # => e.g. 150

[View source]
def experience_level : UInt32 #

The player's server-reported experience level.


[View source]
def experience_progress : Float32 #

The player's server-reported progress toward the next experience level.


[View source]
def eyes #

Current eye position in world coordinates.


[View source]
def feet #

Deprecated alias for #location.

DEPRECATED Use bot.location instead of bot.feet


[View source]
def find_block(name : String, *, radius : Number, center : Vec3d | Vec3i = location) : BlockSnapshot | Nil #

Returns the nearest matching loaded block within the required radius.


[View source]
def find_blocks(name : String, *, radius : Number, center : Vec3d | Vec3i = location, limit : Int32 = 1) : Array(BlockSnapshot) #

Searches loaded blocks within a spherical radius, nearest first. Distances use block coordinates. Equal distances sort by x, y, then z.


[View source]
def food #

Current hunger points, from 0 through 20.


[View source]
def full_health? #

Whether the player has at least 20 health points.


[View source]
def gamemode #

Current game mode ID as supplied by the server.


[View source]
def has_slow_falling? #

Whether Slow Falling is active.


[View source]
def health #

Current health in half-hearts.


[View source]
def host #

The configured Minecraft server hostname.


[View source]
def hotbar_selection #

The selected main-hand hotbar slot, numbered 1 through 9.


[View source]
def hotbar_selection=(index : UInt8) #

Selects a main-hand hotbar slot, numbered 1 through 9. Raises ArgumentError for an index outside that range.


[View source]
def inventory : Inventory #

The inventory facade for the player and currently open menu.


[View source]
def join_game(timeout_ticks = 1200) #

Connects and waits for a spawned player, returning the underlying Client. Raises if joining exceeds timeout_ticks or the connection closes.


[View source]
def join_game(timeout_ticks = 1200, &) #

Connects, yields the underlying Client after spawning, then disconnects when the block returns normally. If it raises, the exception propagates without disconnecting; use an explicit ensure for unconditional cleanup.


[View source]
def jump_boost_level #

Current Jump Boost amplifier plus one, or zero when Jump Boost is absent.


[View source]
def jump_by_height(height = 1, timeout_ticks = 20) #

Queues a jump and waits until the feet are height blocks above their start. Raises if the player cannot rise or does not reach that height before timeout_ticks. It does not wait for landing.


[View source]
def keys #

Mutable movement-key state used by physics on subsequent ticks.


[View source]
def land_on_ground(timeout_ticks = 120) #

Waits until the player's feet y coordinate stops changing. Raises if this does not happen within timeout_ticks.


[View source]
def leave_bed #

Leaves the current bed. Use #use_hand or #place_block_against to enter one.


[View source]
def levitation_level #

Current Levitation amplifier plus one, or zero when Levitation is absent.


[View source]
def location #

The player's current feet position in world coordinates.


[View source]
def look #

The player's current Look direction as a yaw/pitch pair in degrees.


[View source]
def look(&block : Look -> Look) #

Computes the new look from the current look. The block receives the current Look (yaw/pitch in degrees) and must return a new Look, e.g. bot.look { |l| l.with_yaw(l.yaw + 90) }. Waits for the new look to be sent to the server.


[View source]
def look=(look : Look) #

Sets the look direction. Look is a yaw/pitch pair in degrees (see Rosegold::Look for the angle convention and constants like Look::NORTH, Look::SOUTH.down, etc.). Waits for the new look to be sent to the server.


[View source]
def look=(vec : Vec3d) #

Aims the look at a world position. Equivalent to #look_at(vec). Waits for the new look to be sent to the server.


[View source]
def look_at(location : Vec3d) #

Aims from the eyes at a known world position and waits for the look update.


[View source]
def look_at_horizontal(location : Vec3d) #

Ignores y coordinate; useful for looking straight while moving. Waits for the new look to be sent to the server.


[View source]
def main_hand #

The stack currently held in the main hand.


[View source]
def move_to(location : Vec3d, stuck_timeout_ticks : Int32 = 60) #

Moves straight towards the exact #location (decimal coordinates are honored verbatim). Waits for arrival. stuck_timeout_ticks specifies how many consecutive stuck ticks before throwing MovementStuck.


[View source]
def move_to(location : Vec3i, stuck_timeout_ticks : Int32 = 60) #

Moves to the center of the given block coordinate (x + 0.5, z + 0.5), preserving the bot's current feet #y. Use a Vec3d overload if you need to land on an exact non-centered position.


[View source]
def move_to(x : Float, z : Float, stuck_timeout_ticks : Int32 = 60) #

Moves straight towards the exact (x, z) (decimal coordinates are honored verbatim). #y is taken from the bot's current feet location. Waits for arrival. stuck_timeout_ticks specifies how many consecutive stuck ticks before throwing MovementStuck.


[View source]
def move_to(x : Int, z : Int, stuck_timeout_ticks : Int32 = 60) #

Moves to the center of the given block column (x + 0.5, z + 0.5). Use the Float overload to target an exact non-centered position.


[View source]
def move_to(stuck_timeout_ticks : Int32 = 10, &block : Vec3d -> Vec3d) #

Computes the destination location from the current feet location. Moves straight towards the destination. Waits for arrival. stuck_timeout_ticks specifies how many consecutive stuck ticks before throwing MovementStuck.


[View source]
def nearest_entity(*, max_distance : Number | Nil = nil) : EntitySnapshot | Nil #

Returns the nearest tracked entity, optionally bounded by distance.


[View source]
def nearest_entity(*, max_distance : Number | Nil = nil, &predicate : EntitySnapshot -> Bool) : EntitySnapshot | Nil #

Filters detached entity snapshots before choosing the nearest match.


[View source]
def open_container(timeout : Time::Span = 5.seconds, &) #

Opens a container (chest, barrel, etc.) and yields control for interaction. Automatically uses the main hand, waits for container content to load, executes the provided block, then closes the container.

bot.open_container do
  bot.inventory.deposit_at_least(10, "diamond")
  bot.inventory.withdraw_at_least(5, "emerald")
end

Caller must already be looking at the container block. Raises ContainerOpenError if its content does not arrive within timeout. The container is closed even if the block raises.


[View source]
def open_container_handle(timeout : Time::Span = 5.seconds, &) #

Opens a container and yields a ContainerHandle for typed interaction. The handle provides intent-level operations (withdraw, deposit) and typed menu access (as_chest, as_furnace, etc.).

bot.open_container_handle do |handle|
  handle.deposit("diamond", 10)
  handle.withdraw("emerald", 5)
  if chest = handle.as_chest
    chest.contents.each { |slot| puts slot.name }
  end
end

Caller must already be looking at the container block. Raises ContainerOpenError if its content does not arrive within timeout. The handle is closed even if the block raises.


[View source]
def open_container_handle(command : String, timeout : Time::Span = 5.seconds, &) #

Runs command, waits for it to open a container, then yields its handle.

This is for servers that expose a container through a command rather than a block interaction. Raises ContainerOpenError on timeout and closes the handle even if the block raises.


[View source]
def pick_item_from_block(pos : Vec3i, include_data : Bool = false) #

Pick the item at the given block position (middle-click on a block). The server finds the matching item in the player's inventory, moves it to the hotbar, and sends the appropriate slot update packets.


[View source]
def pitch #

The current vertical look angle in degrees.


[View source]
def pitch=(pitch : Float64) #

Sets the vertical look angle in degrees and waits for it to reach the server.


[View source]
def place_block_against(block : Vec3i, face : BlockFace) #

Aims at face of block, then presses and releases use with the main hand. It queues the placement attempt without waiting for confirmation.


[View source]
def port #

The configured Minecraft server port.


[View source]
def recipe_registry #

The synchronized recipe registry for the connected server.


[View source]
def recipes_for(item_name : String) : Array(RecipeDisplayEntry) #

Returns every synchronized recipe whose result matches item_name.


[View source]
def respawn(timeout_ticks = 1200) #

Revives a dead player and waits for its spawn confirmation. Does nothing while alive. Raises if spawning exceeds timeout_ticks.


[View source]
def riding : Mount | Nil #

A snapshot of the direct mount's type and location, or nil when unmounted. Unknown entity type IDs have a nil type. No other entity data is exposed.


[View source]
def riding?(type : String | Nil = nil) : Bool #

Whether the player is riding, optionally matching an exact entity type. Accepts names with or without the minecraft: prefix.


[View source]
def run_command_with_confirmation(command : String, expected_message : String, max_tries : Int32 = 3, inverse_message : String | Nil = nil) #

Runs a slash command and waits for a confirmation message from the server.

Each attempt waits up to five seconds. Retries after a timeout or an inverse message. Use only when replaying the command is acceptable: a missing confirmation does not prove the server failed to execute it.

The expected_message is matched after stripping formatting codes. If inverse_message is provided and received, the command will retry, which is useful for toggle commands where the bot may be in the wrong state.

Returns true if the expected message is received within max_tries attempts, false otherwise.

bot.run_command_with_confirmation(
  "/ignoregroup !",
  "You stopped ignoring !.",
  3,
  "You are now ignoring !"
)

[View source]
def saturation #

Current saturation level.


[View source]
def should_eat? #

Whether #eat! will attempt to consume food at the current health and hunger.


[View source]
def slowness_level #

Current Slowness amplifier plus one, or zero when Slowness is absent.


[View source]
def sneak(sneaking = true) #

Enables or disables sneaking. The next client tick sends the changed input state.


[View source]
def sneaking? #

Whether the player is currently sneaking.


[View source]
def spawned? #

Whether the server has completed player spawn and world state is ready.


[View source]
def speed_level #

Current Speed amplifier plus one, or zero when Speed is absent.


[View source]
def sprint(sprinting = true) #

Enables or disables sprinting. The next client tick sends the changed input state.


[View source]
def sprinting? #

Whether the player is currently sprinting.


[View source]
def start_digging(target : Vec3d | Nil | Look | Nil = nil) #

Optionally aims at target, then holds the attack button to begin digging. Call #stop_digging to release it.


[View source]
def start_jump #

Queues a jump for the next tick on which the player is on the ground.


[View source]
def start_using_hand(hand : Hand = :main_hand) #

Holds the use button for hand. Call #stop_using_hand to release it.


[View source]
def stop_digging #

Releases an active dig action immediately.


[View source]
def stop_jump : Nil #

Clears a pending jump without changing other movement inputs.


[View source]
def stop_moving #

Cancels #move_to, releases movement keys, and clears a pending jump.


[View source]
def stop_using_hand #

Releases an active use action immediately.


[View source]
def subscribe(event_class : Class) #

Forwards events of event_class emitted by the underlying client to this bot.

This is primarily useful when extending Bot; normal consumers subscribe with #on to the events already forwarded during initialization.


[View source]
def swap_hands(timeout : Time::Span = 5.seconds) : Nil #

Closes any container and waits for the server to confirm both hands. Unchanged stacks need no confirmation. A timeout does not undo the swap.


[View source]
def total_experience : UInt32 #

The player's server-reported total experience.


[View source]
def unsneak #

Disables sneaking.


[View source]
def unsprint #

Disables sprinting.


[View source]
def use_hand(target : Vec3d | Nil | Look | Nil = nil, hand : Hand = :main_hand) #

Queues one press and release of use in hand, optionally aiming at target first. Releases any held use action. Repeated calls before a tick coalesce into one press. The target raytrace happens on the next tick eligible under the use cooldown. This does not wait for a world-result confirmation.


[View source]
def username #

The player name, or nil before login completes.


[View source]
def uuid #

The authenticated player UUID, or nil before login completes.


[View source]
def wait_tick #

Waits for one client game tick, returning the event or nil after one second. A timeout does not raise, including when disconnected.


[View source]
def wait_ticks(ticks : Int32) #

Waits for ticks client game ticks. Each tick has the same one-second timeout as #wait_tick.


[View source]
def world : Dimension #

The current dimension, including its live chunks and entities.


[View source]
def x #

The feet-position x coordinate.


[View source]
def y #

The feet-position y coordinate.


[View source]
def yaw #

The current horizontal look angle in degrees.


[View source]
def yaw=(yaw : Float64) #

Sets the horizontal look angle in degrees and waits for it to reach the server.


[View source]
def z #

The feet-position z coordinate.


[View source]