---
title: "Rewriting Minecraft with agents"
date: "2026-10-09"
description: "Ten weeks of coding agents rewriting Minecraft in Rust, and the things that went wrong along the way."
url: "https://matteopolak.com/blog/vibecoding-minecraft"
tags:
  - "rust"
  - "vibecoding"
  - "minecraft"
---

# Rewriting Minecraft with agents

Minecraft is a really big game. One version of the client and server together is about 1.1 million lines of code, and if you count every version people still play, you're well past 2 million.

People have rewritten it before, of course, but almost always only part of it. Maybe it's just the server, or a handful of versions, or everything except redstone. I wanted to know what it would take to do the whole thing, every feature and every version, in Rust, running in a browser. I called it Lodestone, and you can play it right here:

[Lodestone playground: try it in the browser](https://matteopolak.com/playground/minecraft)

Agents wrote almost all of it. I'll go over how that worked, but the parts worth writing about are the parts that went wrong.

## Starting from issues

The nice thing about cloning a game is that somebody else already did the design. So instead of designing anything, I had a few research agents go through the client and server and write down everything they found. Then I went through what they wrote, fixed the parts they got wrong, and turned it all into GitHub issues. 358 of them were opened on Jul 30 alone.

I started with the client, for a pretty simple reason: if my client could connect to a real vanilla server, the server would handle all of the simulation for me, and I could get networking and rendering working before I had a server of my own.

From there, one main session picked issues, handed them out to 5 to 10 subagents, and reviewed and merged whatever came back.

**Commits per week**

|  | Value (commits) |
| --- | --- |
| W31 | 365 |
| W32 | 1179 |
| W33 | 696 |
| W34 | 338 |
| W35 | 39 |
| W36 | 1428 |
| W37 | 615 |
| W38 | 11 |
| W39 | 148 |
| W40 | 284 |
| W41 | 156 |

*ISO weeks from late July to Oct 9, so the last week is partial. The quiet weeks are when I wasn't running agents.*

## More agents isn't faster

You'd think that if ten agents are good, twenty would be better. They're not. Past about ten, they mostly fought over the same files, the same disk and the same build cache, and everything got slower. At one point I asked for the docs to be cleaned up, and the main session started three agents to do it. Those three then started about 20 more. I had to step in and stop it.

Even ten was a lot for one laptop. Each agent had its own `target/` directory, and between them they filled a few hundred GiB of disk, at which point tool calls just started failing. Running too many builds at once would freeze my Mac, and once it took down the app the agents were running in. So now only one agent gets to build at a time.

## When the bug isn't in the code

Some of the bugs that cost me the most time weren't in the game at all.

In one case, an agent committed from a temporary git index while two other agents were committing to the same branch. Its commit quietly reverted both of theirs, about 2,000 lines. Now all commits go through a script that refuses to commit from a checkout that's behind `HEAD`.

Another time, `cargo check` reported 435 missing files. The files were fine! A different agent was deleting the worktree this one was reading from. When the same command ran a few minutes later, it reported 3 errors, and those ones were real.

The weirdest one was the search tool. It had a bug where it cut off the part of the line that actually matched, so an agent would search for a function, get back a result that didn't seem to contain it, and conclude that the function didn't exist. Three different agents nearly went off and made bad decisions because of that before anyone figured out what was going on.

What makes these annoying is that, from the agent's point of view, every one of them looks like broken code. And if you ask an agent to fix broken code, it'll fix it, even when the code was never the problem.

## Slow by default

The agents don't care about performance unless you ask them to. For a while, block data was being passed around as strings _everywhere_, which meant world generation was doing over 200 million string comparisons per chunk. Generating enums at build time brought that down to under 20,000.

They'll also describe every optimization as a win, whether it is one or not. After a few of those, I made a rule: every performance change gets benchmarked, and if it's slower, it gets reverted.

**Performance changes, including reverted ones**

| Date | Event | Category |
| --- | --- | --- |
| 2026-07-27 | Noise stage about 2x faster | Kept |
| 2026-07-29 | Worst world-lock hold 328µs to 41µs | Kept |
| 2026-07-31 | World generation cache (reverted): 4.4% slower on chunk generation. | Measured and reverted |
| 2026-08-05 | Removing the per-ring join barrier (reverted): About 5,000 concurrent lock attempts on one mutex. | Measured and reverted |
| 2026-08-07 | Bounded dependency-edge window: The version of the barrier change that actually helped. | Kept |
| 2026-08-08 | Allocations per chunk column 87,882 to 118 | Kept |
| 2026-10-05 | 26.3 shaped chunk 118ms to about 10ms | Kept |

*7 events from 2026-07-27 to 2026-10-05.*

The barrier one is my favourite. Removing a sync point between chunk rings was supposed to let generation run more in parallel. What it actually did was pile everything onto one mutex, with about 5,000 lock attempts at once. A version of the idea that did help landed two days later.

## Actually playing it

I've played a lot of Minecraft, and just playing Lodestone turned up plenty of things the tests didn't. Mobs spawned with `/summon` didn't collide with anything. Pathfinding was wrong in enough ways that it ended up being rewritten. And until Oct 9, bees, bats, phantoms and ghasts couldn't fly, which is a bit of a problem for most of them.

**Lines of Rust over time**

| Date | Rust (lines) |
| --- | --- |
| 2026-07-28 | 394822 |
| 2026-08-03 | 576079 |
| 2026-08-10 | 1033891 |
| 2026-08-17 | 1286962 |
| 2026-08-24 | 1360942 |
| 2026-08-31 | 1366269 |
| 2026-09-07 | 1707651 |
| 2026-09-14 | 1748004 |
| 2026-09-21 | 1793431 |
| 2026-09-28 | 1811216 |
| 2026-10-05 | 1947154 |
| 2026-10-09 | 1775462 |

*Every line in every .rs file, including tests, at the last commit of each sample day. The drop at the end is the old 26.2 world generator being deleted.*

## Moving to 26.3

In October, I switched the target version to 26.3. Rather than patch the 26.2 world generator in place, I had a separate 26.3 one built and compared against the real game until it matched, which happened on Oct 5. Shaped chunks went from 118 ms to about 10 ms along the way, too. Then the old generator was deleted, which is that drop at the end of the chart.

## Was it worth it?

As an experiment, yeah, definitely. It was a lot easier than I thought it would be.
