Rosegold

Minecraft botting client written in Crystal, designed for CivMC and compliant with its botting rules.

bot = Rosegold::Bot.join_game("play.civmc.net")

bot.move_to(100, 200)           # walk to coordinates
bot.inventory.pick! "diamond_sword"  # equip a sword
bot.attack                       # swing
bot.eat!                         # auto-eat when hungry
bot.craft("stick", 4)           # craft items by name

Rosegold handles the protocol, physics, and inventory management so you can focus on what your bot does.

Getting Started

1. Install Crystal

Follow the official Crystal installation guide for your platform.

Windows users: Crystal works best under WSL (Windows Subsystem for Linux).

shards (Crystal's package manager, like npm) is included with Crystal.

2. Use the Template

The fastest way to start is with the example template:

  1. Click Use this template to create your own repo — it includes GitHub Actions that build Linux and Windows binaries automatically
  2. Clone your new repo and install dependencies:
git clone https://github.com/YOUR_USERNAME/my-bot.git
cd my-bot
shards install

3. Build and Run

shards build
./bin/attack

On first run, you'll see a message like:

To sign in, use a web browser to open https://microsoft.com/devicelogin and enter the code XXXXXXXX

Open that URL, enter the code, and sign in with the Microsoft account that owns Minecraft Java Edition. After that, your token is cached and future runs connect automatically.

4. Watch Your Bot (SpectateServer)

All the examples start a SpectateServer on localhost:25566. Open Minecraft, add a server with that address, and connect to see through your bot's eyes in real time — its position, inventory, health, and everything happening around it. No auth required.

API Quick Reference

Here's what you can do with a Rosegold::Bot. For the full API, see the docs.

Connection

# Connect and wait for spawn
bot = Rosegold::Bot.join_game("play.civmc.net")

# Check connection state
bot.connected?
bot.health
bot.food
bot.dead?

# Respawn after death
bot.respawn

Movement

# Move to coordinates (straight line, no pathfinding)
bot.move_to(100, 200)

# Move to a block center
bot.move_to(Vec3i.new(100, 64, 200))

# Relative movement
bot.move_to { |pos| pos + Vec3d.new(5, 0, 0) }

# Stop moving
bot.stop_moving

# Sprint and sneak
bot.sprint
bot.sneak

move_to walks in a straight line and auto-steps up 0.6-block ledges. There is no built-in pathfinding — it will get stuck on walls. Raises Physics::MovementStuck if no progress is made.

Look Direction

# Look at a position
bot.look_at(Vec3d.new(100, 65, 200))

# Set yaw/pitch directly (yaw: 0=South, 90=West, 180=North, 270=East)
bot.yaw = 180
bot.pitch = -10

# Look horizontally (useful while walking)
bot.look_at_horizontal(target)

Combat and Mining

# Attack whatever you're looking at
bot.attack

# Hold attack for N ticks (for mining)
bot.dig(40)

# Start/stop continuous digging
bot.start_digging
bot.stop_digging

# Place a block
bot.place_block_against(block_pos, :top)

# Auto-eat when hungry
bot.eat!

Inventory

# Select an item by name
bot.inventory.pick! "diamond_sword"

# ...or by predicate — pick the most-damaged usable axe
bot.inventory.pick! { |s| s.name.ends_with?("_axe") }

# Count items
bot.inventory.count("diamond")

# Check main hand
bot.main_hand.name        # => "diamond_sword"
bot.main_hand.durability  # => 1561
bot.main_hand.enchantments # => {"sharpness" => 5}

# Drop items
bot.drop_hand_full
bot.inventory.throw_all_of("cobblestone")

# Equipment
bot.inventory.helmet
bot.inventory.chestplate

pick is durability-aware: it cycles through matching tools from most-damaged to least-damaged and refuses to hand you an enchanted diamond/netherite tool that's about to break, so looping over pick will auto-rotate fresh tools in from your inventory. Tune Rosegold::Slot.max_repair_cost to control when heavily-repaired tools get retired. See the Inventory#pick docs for details.

Containers (Chests, Furnaces, etc.)

# Look at a chest, then:
bot.open_container_handle do |handle|
  handle.withdraw("diamond", 10)
  handle.deposit("cobblestone", 64)
  handle.count_in_container("emerald")
end

Crafting

# Craft by name (auto-selects recipe)
bot.craft("stick", 4)

# Craft as many as possible
bot.craft_all("iron_ingot")

# Manual grid pattern (needs crafting table position for 3x3)
bot.craft_pattern([
  ["iron_ingot", "iron_ingot", "iron_ingot"],
  [nil, "stick", nil],
  [nil, "stick", nil],
], table: crafting_table_pos)

Chat and Events

# Send a chat message or command
bot.chat "Hello!"
bot.chat "/msg someone Hi"

# Listen for chat
bot.on Rosegold::Clientbound::SystemChatMessage do |event|
  puts event.message.to_s
end

bot.on Rosegold::Clientbound::PlayerChatMessage do |event|
  puts "[#{event.network_name}] #{event.message}"
end

# Wait for a specific response
bot.wait_for(Rosegold::Clientbound::SystemChatMessage, timeout: 5.seconds) do
  bot.chat "/time query daytime"
end

# Tick-based timing
bot.wait_ticks 20  # wait 1 second (20 ticks)

Event Types

Events you can subscribe to on bot:

| Event | Description | |-------|-------------| | Clientbound::SystemChatMessage | Server messages, command responses | | Clientbound::PlayerChatMessage | Player chat | | Clientbound::DisguisedChatMessage | /say, /me style messages | | Event::Tick | Every game tick (~50ms) | | Event::ContainerOpened | A container UI opened | | Clientbound::SetContainerContent | Container contents updated | | Clientbound::SetSlot | Single slot updated |

For other packets (health updates, entity spawns, etc.), subscribe on bot.client directly.

Features

Contributing

  1. Fork it (https://github.com/RosegoldMC/rosegold.cr/fork)
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request