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 · runs in your browser

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

Commits per weekCommits per week. 11 bars; highest W36 (1428 commits), lowest W38 (11 commits).05001K1.5K2KW31W32W33W34W35W36W37W38W39W40W41Commits per weekCommits per week. 11 bars; highest W36 (1428 commits), lowest W38 (11 commits).05001K1.5K2KW31W32W33W34W35W36W37W38W39W40W41
ISO weeks from late July to Oct 9, so the last week is partial. The quiet weeks are when I wasn't running agents.
Commits per week
Value (commits)
W31365
W321179
W33696
W34338
W3539
W361428
W37615
W3811
W39148
W40284
W41156

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

  • Kept
  • Measured and reverted
  1. Noise stage about 2x fasterKept
  2. Worst world-lock hold 328µs to 41µsKept
  3. World generation cache (reverted)Measured and reverted4.4% slower on chunk generation.
  4. Removing the per-ring join barrier (reverted)Measured and revertedAbout 5,000 concurrent lock attempts on one mutex.
  5. Bounded dependency-edge windowKeptThe version of the barrier change that actually helped.
  6. Allocations per chunk column 87,882 to 118Kept
  7. 26.3 shaped chunk 118ms to about 10msKept

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

Lines of Rust over timeLines of Rust over time. Rust goes from 394822 lines (2026-07-28) to 1775462 lines (2026-10-09), peaking at 1947154 lines (2026-10-05).0500K1M1.5M2MAug 2026Aug 2026Aug 2026Aug 2026Aug 2026Sep 2026Sep 2026Sep 2026Sep 2026Oct 2026Lines of Rust over timeLines of Rust over time. Rust goes from 394822 lines (2026-07-28) to 1775462 lines (2026-10-09), peaking at 1947154 lines (2026-10-05).0500K1M1.5M2MAug 2026Sep 2026Oct 2026
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.
Lines of Rust over time
DateRust (lines)
2026-07-28394822
2026-08-03576079
2026-08-101033891
2026-08-171286962
2026-08-241360942
2026-08-311366269
2026-09-071707651
2026-09-141748004
2026-09-211793431
2026-09-281811216
2026-10-051947154
2026-10-091775462

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.