Skip to content

Releases: dekrom/baritone

Baritone 1.3.0 for Minecraft 26.2

Choose a tag to compare

@github-actions github-actions released this 05 Sep 02:18

1.2.0 fixed what went wrong after hours in the air. 1.3.0 is about the two minutes at either end
of a flight: getting off the ground from wherever the last flight ended, and getting out of the
places a flight can end up in — a crevice in the basalt deltas, the hollow beside a lava pond, the
pond itself. Along the way it closes the two remaining ways a path could be drawn through solid
ground, and one more way #elytra could go silent.

The same fixes are on every supported Minecraft version, except the one marked 26.2 only.

Taking off from a hole

Where a nether flight ends when the rockets run out is where the next one has to start, and in the
basalt deltas that is a crevice with walls a couple of blocks away on every side and open sky
straight up. The standing takeoff from 1.1.0 asked the pathfinder for a path from your feet. The
pathfinder starts every path from the nearest 4×4×4 cube of air, and its search for that cube walks
straight through walls, so from a hole the first node was very often on the far side of one. The
takeoff rocket flew at it, hit the wall, dropped back into the hole, and did that three times
before giving up with Took off from here 3 times and ended up back on the ground every time.

The path now starts from the first clear cube directly overhead, up to 32 blocks up, which is about
what a rocket lit straight up is good for. The survival solver is changed to match. With a rocket
burning it no longer settles for the first pitch that survives its horizon but takes the one that
ends highest, since the boost is the one chance to get above whatever is boxing you in, and it
forces a rocket as soon as an impact is anywhere inside the horizon rather than within twelve ticks
of it — which, from the top of a climb out of a hole, meant falling most of the way back in first.

The takeoff rocket was never accepted

Logs from 2b2t showed every standing takeoff opening the elytra and landing again within half a
second, with no push from the rocket at all. The rocket was lit in the same tick the elytra opened,
and the server never accepted it. No vanilla client can use an item in the tick it starts gliding —
its use packet goes out before its glide packet, while the elytra is still shut — and the use
packet carried the camera's rotation while that tick's movement packet carried the takeoff aim. Two
different looks in one tick is exactly what the server's movement checks compare.

The rocket goes the tick after the elytra opens now, once the solver has aimed, and every use packet
carries the rotation the movement packet is about to. The solver also assumes a rocket it has just
lit is burning from the moment it lit it: the entity takes a round trip to show up, several ticks on
a busy server, and planning an unboosted trajectory in the meantime — a shallow glide, since nothing
else goes anywhere without thrust — is what pointed the takeoff at the nearest wall. If the server
shuts the elytra again while you are still in the air right after a takeoff, that is the server
refusing the glide, and it is reported as The server closed the elytra N ticks after takeoff.
rather than left to look like a takeoff that went nowhere.

Lava

An elytra opens in lava — vanilla only refuses in water — but it does nothing there. In a fluid,
glide or not, vanilla moves you by the fluid rules, so the wings are inert and a rocket is the only
thrust, at a fraction of its usual push. The solver aimed that rocket at the path, and from a lava
pond the path leads through the pond's basalt wall, so the rocket spent itself against rock. On the
ground the process had no idea it was in lava at all: the landing logic saw a complete path, went
looking for a landing spot, and every path it asked for from inside the pool failed on the spot —
twenty times a second, Path complete, searching for safe landing spot... and unable to land
each time.

In lava everything else waits. The process swims up, opens the elytra the moment it is off the pool
floor, and the solver aims for the steepest climb the simulation keeps clear, with a rocket every
time; neither the setback delay nor a landing in progress holds that rocket back any more, and the
landing search is skipped until you are out.

Paths through terrain, and circling next to them

When a node of the path turned out to be inside a block, the segment up to a rejoin node further
along was recomputed and the rest of the old path stitched on after it — whether or not the search
had reached the rejoin node. The native search gives up half a second after its last progress, and
a recomputation around an obstacle is often that kind of search. When it stopped short, the result
was a new segment, then a straight jump to the rejoin node through whatever lay between, and the old
path from there. No obstacle check catches that jump, since both of its ends are in open air, so
every recomputation ended the same way and the solver circled the last node it could reach, next to
a path drawn through solid ground. An unfinished segment now replaces the path on its own and is
continued from where the search got to.

A cache that disagrees with the world

Everything the solver does — the pitch simulation, the hitbox raytraces, the obstacle checks —
runs against the pathfinder's own copy of the world. When that copy was wrong, the solver aimed at
terrain it believed was air and nothing noticed, because the raytraces that would have caught it
look at the same copy. Cancelling and re-engaging fixed it, and nothing else did.

Two sources. A chunk packet the client discards, because it is outside the view distance, still
raised the chunk event, and the world handed back its shared empty chunk for it; that was packed
as a real chunk of pure air, and the path went straight through whatever is actually there. Those
are skipped now. The other is a chunk whose packing was simply missed, and the fix for that is to
notice and re-read: on a horizontal collision, which the simulation never plans, when the cache says
you are inside a block while you are plainly flying, and when a recomputation fails, the chunks
around you are packed again, at most once every two seconds.

A hung teardown

The pathfinder context's teardown waited without limit for its native search to finish, and the
native cancel is a no-op: the search runs to its own timeout, and that timeout does not cover the
breadth-first search for a start cube that runs before it. On 26.2 the teardown is what hands back
the semaphore the next engage needs, so one hung search meant every later #elytra deferred itself
onto a completion that never came and was dropped by the next cancel — silently dead for the rest
of the session, with nothing in the log to say so. On the other versions it pinned a thread and
leaked the context. The wait is bounded now, and a context whose search is still wedged after it is
abandoned rather than freed underneath it.

Segfault in getChunkOrDefault — 26.2 only

The native path calculation ran under the read lock whenever terrain prediction was off, on the
theory that a search which only reads the chunk cache can share it with the solvers. With
elytraUseCache on it also parses Baritone's region files into the cache the first time a search
touches them, and every insert can rehash the map. The solvers' chunk lookups run under that same
read lock, with no mutex of their own on the native side, and a rehash under a lookup's bucket walk
is heap corruption. It surfaced as a JVM segfault in getChunkOrDefault on the render thread, mid
flight, with terrain prediction off — the one configuration that used the read lock. A path
calculation is always a writer now; the prediction flag only decides whether an unloaded chunk
counts as air. The render thread's solver skips its tick while a recomputation runs, which is what
the prediction path always did.

Why this fork

This is MeteorDevelopment's Fabric Baritone with fixes aimed at one workload: travelling a very long
way, unattended, without dying. Everything upstream does — #goto, #mine, #follow, #build,
#farm, the whole API — works here unchanged, and nothing in the fork touches how movement is sent
to the server.

That workload is unforgiving in a way ordinary Baritone use is not. A hunt is a program that flies
for hours with nobody at the keyboard and writes down what it sees, over terrain that no longer
matches anybody's cache because someone has been through it with TNT. It is decided by two things
that upstream is careless about: whether it stops, and whether the record it writes is worth
reading afterwards. Everything in 1.1.0, 1.2.0 and 1.3.0 is one of those two.

Not stopping. Upstream's #elytra is good at flying a path it has already computed and bad at
everything around it. It could only take off by walking to an overhang and stepping off — which on
a nether highway or a flat overworld does not exist, and you land where the rockets ran out, not
next to a cliff. When the solver ran out of options it returned a heading with no pitch, which in
practice holds the last pitch straight into whatever is in front of you, exactly when it matters:
a wall appeared, the world changed under the path, something knocked you off course. Its flight
simulation had drifted from vanilla's in five separate ways, and every tick of that error compounds
across the horizon it picks a pitch from — when rockets are the budget, that is the budget.
A path node buried in terrain was noticed and then ignored. 1.1.0 fixed all of that; 1.2.0 removed
the last three ways an unattended run could end in a freeze, a segfault or a message repeating
every two seconds; 1.3.0 is the takeoff, from the places a flight actually ends, and the rocket
the server was refusing.

The record. The chunk cache is the output of a hunt, not scratch space, and since 1.2.0 it is no
longer rewritten in place under its own readers.

The rest is the walking half, from 1.1.0: falls char...

Read more

Baritone 1.3.0 for Minecraft 26.1

Choose a tag to compare

@github-actions github-actions released this 05 Sep 02:19

1.2.0 fixed what went wrong after hours in the air. 1.3.0 is about the two minutes at either end
of a flight: getting off the ground from wherever the last flight ended, and getting out of the
places a flight can end up in — a crevice in the basalt deltas, the hollow beside a lava pond, the
pond itself. Along the way it closes the two remaining ways a path could be drawn through solid
ground, and one more way #elytra could go silent.

The same fixes are on every supported Minecraft version, except the one marked 26.2 only.

Taking off from a hole

Where a nether flight ends when the rockets run out is where the next one has to start, and in the
basalt deltas that is a crevice with walls a couple of blocks away on every side and open sky
straight up. The standing takeoff from 1.1.0 asked the pathfinder for a path from your feet. The
pathfinder starts every path from the nearest 4×4×4 cube of air, and its search for that cube walks
straight through walls, so from a hole the first node was very often on the far side of one. The
takeoff rocket flew at it, hit the wall, dropped back into the hole, and did that three times
before giving up with Took off from here 3 times and ended up back on the ground every time.

The path now starts from the first clear cube directly overhead, up to 32 blocks up, which is about
what a rocket lit straight up is good for. The survival solver is changed to match. With a rocket
burning it no longer settles for the first pitch that survives its horizon but takes the one that
ends highest, since the boost is the one chance to get above whatever is boxing you in, and it
forces a rocket as soon as an impact is anywhere inside the horizon rather than within twelve ticks
of it — which, from the top of a climb out of a hole, meant falling most of the way back in first.

The takeoff rocket was never accepted

Logs from 2b2t showed every standing takeoff opening the elytra and landing again within half a
second, with no push from the rocket at all. The rocket was lit in the same tick the elytra opened,
and the server never accepted it. No vanilla client can use an item in the tick it starts gliding —
its use packet goes out before its glide packet, while the elytra is still shut — and the use
packet carried the camera's rotation while that tick's movement packet carried the takeoff aim. Two
different looks in one tick is exactly what the server's movement checks compare.

The rocket goes the tick after the elytra opens now, once the solver has aimed, and every use packet
carries the rotation the movement packet is about to. The solver also assumes a rocket it has just
lit is burning from the moment it lit it: the entity takes a round trip to show up, several ticks on
a busy server, and planning an unboosted trajectory in the meantime — a shallow glide, since nothing
else goes anywhere without thrust — is what pointed the takeoff at the nearest wall. If the server
shuts the elytra again while you are still in the air right after a takeoff, that is the server
refusing the glide, and it is reported as The server closed the elytra N ticks after takeoff.
rather than left to look like a takeoff that went nowhere.

Lava

An elytra opens in lava — vanilla only refuses in water — but it does nothing there. In a fluid,
glide or not, vanilla moves you by the fluid rules, so the wings are inert and a rocket is the only
thrust, at a fraction of its usual push. The solver aimed that rocket at the path, and from a lava
pond the path leads through the pond's basalt wall, so the rocket spent itself against rock. On the
ground the process had no idea it was in lava at all: the landing logic saw a complete path, went
looking for a landing spot, and every path it asked for from inside the pool failed on the spot —
twenty times a second, Path complete, searching for safe landing spot... and unable to land
each time.

In lava everything else waits. The process swims up, opens the elytra the moment it is off the pool
floor, and the solver aims for the steepest climb the simulation keeps clear, with a rocket every
time; neither the setback delay nor a landing in progress holds that rocket back any more, and the
landing search is skipped until you are out.

Paths through terrain, and circling next to them

When a node of the path turned out to be inside a block, the segment up to a rejoin node further
along was recomputed and the rest of the old path stitched on after it — whether or not the search
had reached the rejoin node. The native search gives up half a second after its last progress, and
a recomputation around an obstacle is often that kind of search. When it stopped short, the result
was a new segment, then a straight jump to the rejoin node through whatever lay between, and the old
path from there. No obstacle check catches that jump, since both of its ends are in open air, so
every recomputation ended the same way and the solver circled the last node it could reach, next to
a path drawn through solid ground. An unfinished segment now replaces the path on its own and is
continued from where the search got to.

A cache that disagrees with the world

Everything the solver does — the pitch simulation, the hitbox raytraces, the obstacle checks —
runs against the pathfinder's own copy of the world. When that copy was wrong, the solver aimed at
terrain it believed was air and nothing noticed, because the raytraces that would have caught it
look at the same copy. Cancelling and re-engaging fixed it, and nothing else did.

Two sources. A chunk packet the client discards, because it is outside the view distance, still
raised the chunk event, and the world handed back its shared empty chunk for it; that was packed
as a real chunk of pure air, and the path went straight through whatever is actually there. Those
are skipped now. The other is a chunk whose packing was simply missed, and the fix for that is to
notice and re-read: on a horizontal collision, which the simulation never plans, when the cache says
you are inside a block while you are plainly flying, and when a recomputation fails, the chunks
around you are packed again, at most once every two seconds.

A hung teardown

The pathfinder context's teardown waited without limit for its native search to finish, and the
native cancel is a no-op: the search runs to its own timeout, and that timeout does not cover the
breadth-first search for a start cube that runs before it. On 26.2 the teardown is what hands back
the semaphore the next engage needs, so one hung search meant every later #elytra deferred itself
onto a completion that never came and was dropped by the next cancel — silently dead for the rest
of the session, with nothing in the log to say so. On the other versions it pinned a thread and
leaked the context. The wait is bounded now, and a context whose search is still wedged after it is
abandoned rather than freed underneath it.

Segfault in getChunkOrDefault — 26.2 only

The native path calculation ran under the read lock whenever terrain prediction was off, on the
theory that a search which only reads the chunk cache can share it with the solvers. With
elytraUseCache on it also parses Baritone's region files into the cache the first time a search
touches them, and every insert can rehash the map. The solvers' chunk lookups run under that same
read lock, with no mutex of their own on the native side, and a rehash under a lookup's bucket walk
is heap corruption. It surfaced as a JVM segfault in getChunkOrDefault on the render thread, mid
flight, with terrain prediction off — the one configuration that used the read lock. A path
calculation is always a writer now; the prediction flag only decides whether an unloaded chunk
counts as air. The render thread's solver skips its tick while a recomputation runs, which is what
the prediction path always did.

Why this fork

This is MeteorDevelopment's Fabric Baritone with fixes aimed at one workload: travelling a very long
way, unattended, without dying. Everything upstream does — #goto, #mine, #follow, #build,
#farm, the whole API — works here unchanged, and nothing in the fork touches how movement is sent
to the server.

That workload is unforgiving in a way ordinary Baritone use is not. A hunt is a program that flies
for hours with nobody at the keyboard and writes down what it sees, over terrain that no longer
matches anybody's cache because someone has been through it with TNT. It is decided by two things
that upstream is careless about: whether it stops, and whether the record it writes is worth
reading afterwards. Everything in 1.1.0, 1.2.0 and 1.3.0 is one of those two.

Not stopping. Upstream's #elytra is good at flying a path it has already computed and bad at
everything around it. It could only take off by walking to an overhang and stepping off — which on
a nether highway or a flat overworld does not exist, and you land where the rockets ran out, not
next to a cliff. When the solver ran out of options it returned a heading with no pitch, which in
practice holds the last pitch straight into whatever is in front of you, exactly when it matters:
a wall appeared, the world changed under the path, something knocked you off course. Its flight
simulation had drifted from vanilla's in five separate ways, and every tick of that error compounds
across the horizon it picks a pitch from — when rockets are the budget, that is the budget.
A path node buried in terrain was noticed and then ignored. 1.1.0 fixed all of that; 1.2.0 removed
the last three ways an unattended run could end in a freeze, a segfault or a message repeating
every two seconds; 1.3.0 is the takeoff, from the places a flight actually ends, and the rocket
the server was refusing.

The record. The chunk cache is the output of a hunt, not scratch space, and since 1.2.0 it is no
longer rewritten in place under its own readers.

The rest is the walking half, from 1.1.0: falls char...

Read more

Baritone 1.3.0 for Minecraft 1.21.8

Choose a tag to compare

@github-actions github-actions released this 05 Sep 02:22
v1.3.0-1.21.8

Baritone 1.3.0 for Minecraft 1.21.8

Baritone 1.3.0 for Minecraft 1.21.5

Choose a tag to compare

@github-actions github-actions released this 05 Sep 02:21
v1.3.0-1.21.5

Baritone 1.3.0 for Minecraft 1.21.5

Baritone 1.3.0 for Minecraft 1.21.4

Choose a tag to compare

@github-actions github-actions released this 05 Sep 02:22
v1.3.0-1.21.4

Baritone 1.3.0 for Minecraft 1.21.4

Baritone 1.3.0 for Minecraft 1.21.11

Choose a tag to compare

@github-actions github-actions released this 05 Sep 02:20
v1.3.0-1.21.11

Baritone 1.3.0 for Minecraft 1.21.11

Baritone 1.2.0 for Minecraft 26.2

Choose a tag to compare

@github-actions github-actions released this 02 Sep 00:18

1.1.0 fixed everything that stopped #elytra getting off the ground. 1.2.0 fixes what goes wrong
once it has been in the air for a few hours with nobody watching: a chunk cache that corrupted
itself, a pathfinder that could never recover from reading it, and a cancel that could hang or
crash the client.

The same fixes are on every supported Minecraft version, except the three marked 26.2 only —
those are bugs in the persistent nether pathfinder context that only 26.2 has.

Region files were rewritten in place

CachedRegion.save opened a region file and wrote straight over it, from the cache save thread,
while everything else was still reading it. A read that landed mid-rewrite got a truncated gzip
stream and threw the whole region away.

That file is the record of a hunt. Baritone's chunk cache keeps, for every chunk you have ever
loaded, the position of every chest, trapped chest, ender chest, shulker box, spawner, furnace and
end portal frame in it — that is what #find chest and #find shulker_box read. Losing a 512×512
region to a torn write throws away everything you flew over there, and you do not find out until
you go looking for it.

On 26.2 it is worse than lost data. With elytraUseCache on, those same files are handed to the
nether pathfinder, which parses each region lazily, once, and writes it off permanently if the
parse throws. One torn read leaves a hole in the world the elytra solver plans through for the rest
of that context's life.

Saves now go to a temp file that is renamed over the old one, so a reader sees either the previous
region or the new one and never half of each. The temp name carries the pid, so two clients sharing
a cache directory cannot corrupt each other's writes.

Cancelling #elytra could crash the client

The nether pathfinder's native context was freed as soon as its own executors had drained. That
accounts for the packing thread and the solver, and for nobody else — and the game thread is
somebody else. A finished path calculation hands its result to the game thread, and that hand-off
can still be sitting in the game thread's task queue when the free happens; the first thing it does
with the result is raytrace through the context to trim it. Running that against freed memory is a
segfault, not an exception, so it takes the whole client with it and leaves a hs_err file rather
than a stack trace.

The context is freed on the game thread now, after everything else that can touch it is gone, and
every native call is fenced behind a flag that fails safe — nothing is visible, nothing has a
chunk, every raytrace reports a hit — so a caller that arrives late gets a useless answer instead
of a crash. On 26.2, where the context outlives the flight, it is freed under the same lock the
solver and the game thread hold.

Cancelling #elytra could also freeze the game — 26.2 only

26.2 keeps one pathfinder context alive across engages, and the next engage blocked the game thread
on the old one until it had been freed. A native path calculation cannot be interrupted —
nether-pathfinder's cancel() sets a flag its search loop never reads — so the client hung for
however long the in-flight calculation had left. Anything that re-issues an elytra goal shortly
after cancelling it, which is what an automated hunt does every couple of seconds, froze the game
for up to ten seconds at a time.

The engage is deferred onto the teardown now instead of waiting on it, guarded by an epoch counter
so that a cancel, a new goal or leaving the world drops the deferred engage rather than resurrecting
a stale one. The native calculation's timeout is three seconds instead of ten, since with cancel()
being a no-op that timeout is the only thing bounding how long a teardown can take.

Failed to compute path to destination, forever — 26.2 only

A region the pathfinder failed to parse is never retried, so one bad read poisons every later
calculation in that context. Because a cancel deliberately keeps the context, and a driver that
re-issues its goal on a timer keeps asking, the result was that message on a loop for the rest of
the session with no way out short of a relog.

Three consecutive failures now discard the context so the next attempt starts from a clean cache.
Skipped while flying, where dropping the context would disengage the elytra outright; the retries
after landing pick it up.

Two arithmetic bugs above the build limit — 26.2 only

BuildLimitPathFinder.generateDirectPath divides 8 by the horizontal distance to its destination to
get a step vector, so a destination directly above or below made every step NaN and the path with
it. Separately, the fallback for a failed transition compared a squared distance against an
unsquared 32, so anything past 6 blocks took the wrong branch and the route over the roof was never
used.

Why this fork

This is MeteorDevelopment's Fabric Baritone with fixes aimed at one workload: travelling a very long
way, unattended, without dying. Everything upstream does — #goto, #mine, #follow, #build,
#farm, the whole API — works here unchanged, and nothing in the fork touches how movement is sent
to the server.

That workload is unforgiving in a way ordinary Baritone use is not. A hunt is a program that flies
for hours with nobody at the keyboard and writes down what it sees, over terrain that no longer
matches anybody's cache because someone has been through it with TNT. It is decided by two things
that upstream is careless about: whether it stops, and whether the record it writes is worth
reading afterwards. Everything in 1.1.0 and 1.2.0 is one of those two.

Not stopping. Upstream's #elytra is good at flying a path it has already computed and bad at
everything around it. It could only take off by walking to an overhang and stepping off — which on
a nether highway or a flat overworld does not exist, and you land where the rockets ran out, not
next to a cliff. When the solver ran out of options it returned a heading with no pitch, which in
practice holds the last pitch straight into whatever is in front of you, exactly when it matters:
a wall appeared, the world changed under the path, something knocked you off course. Its flight
simulation had drifted from vanilla's in five separate ways, and every tick of that error compounds
across the horizon it picks a pitch from — when rockets are the budget, that is the budget.
A path node buried in terrain was noticed and then ignored. 1.1.0 fixed all of that; 1.2.0 removes
the last three ways an unattended run could end in a freeze, a segfault or a message repeating
every two seconds.

The record. The chunk cache is the output of a hunt, not scratch space, and until this release
it was being rewritten in place under its own readers.

The rest is the walking half, from 1.1.0: falls charged a tick of gravity that never happens and a
block of fall that does not exist, waterlogged fences and panes treated as open water, powder snow
and berry bushes considered jumpable, a build limit check that let the search climb 64 blocks past
it, GoalRunAway collapsing its own bounding box, and a set of races between the pathing thread and
the game thread — ToolSet reading the live inventory mid-calculation, assumeWalkOnWater read per
node instead of per calculation, a non-volatile cancel flag.

Install

Take the jar for your Minecraft version and drop it in mods/ next to Fabric API.

  • baritone-api-fabric-*.jar — use this one if another mod drives Baritone through its API.
    Alongside Meteor Client and its addons, this is the one you want.
  • baritone-standalone-fabric-*.jar — everything exposed, for using Baritone on its own.
  • baritone-unoptimized-fabric-*.jar — not obfuscated. Use it when reporting a bug so the stack
    trace is readable.

Verify your download against checksums.txt. Java 21 for the 1.21.x releases, Java 25 for 26.x.

Baritone 1.2.0 for Minecraft 26.1

Choose a tag to compare

@github-actions github-actions released this 02 Sep 00:18

1.1.0 fixed everything that stopped #elytra getting off the ground. 1.2.0 fixes what goes wrong
once it has been in the air for a few hours with nobody watching: a chunk cache that corrupted
itself, a pathfinder that could never recover from reading it, and a cancel that could hang or
crash the client.

The same fixes are on every supported Minecraft version, except the three marked 26.2 only —
those are bugs in the persistent nether pathfinder context that only 26.2 has.

Region files were rewritten in place

CachedRegion.save opened a region file and wrote straight over it, from the cache save thread,
while everything else was still reading it. A read that landed mid-rewrite got a truncated gzip
stream and threw the whole region away.

That file is the record of a hunt. Baritone's chunk cache keeps, for every chunk you have ever
loaded, the position of every chest, trapped chest, ender chest, shulker box, spawner, furnace and
end portal frame in it — that is what #find chest and #find shulker_box read. Losing a 512×512
region to a torn write throws away everything you flew over there, and you do not find out until
you go looking for it.

On 26.2 it is worse than lost data. With elytraUseCache on, those same files are handed to the
nether pathfinder, which parses each region lazily, once, and writes it off permanently if the
parse throws. One torn read leaves a hole in the world the elytra solver plans through for the rest
of that context's life.

Saves now go to a temp file that is renamed over the old one, so a reader sees either the previous
region or the new one and never half of each. The temp name carries the pid, so two clients sharing
a cache directory cannot corrupt each other's writes.

Cancelling #elytra could crash the client

The nether pathfinder's native context was freed as soon as its own executors had drained. That
accounts for the packing thread and the solver, and for nobody else — and the game thread is
somebody else. A finished path calculation hands its result to the game thread, and that hand-off
can still be sitting in the game thread's task queue when the free happens; the first thing it does
with the result is raytrace through the context to trim it. Running that against freed memory is a
segfault, not an exception, so it takes the whole client with it and leaves a hs_err file rather
than a stack trace.

The context is freed on the game thread now, after everything else that can touch it is gone, and
every native call is fenced behind a flag that fails safe — nothing is visible, nothing has a
chunk, every raytrace reports a hit — so a caller that arrives late gets a useless answer instead
of a crash. On 26.2, where the context outlives the flight, it is freed under the same lock the
solver and the game thread hold.

Cancelling #elytra could also freeze the game — 26.2 only

26.2 keeps one pathfinder context alive across engages, and the next engage blocked the game thread
on the old one until it had been freed. A native path calculation cannot be interrupted —
nether-pathfinder's cancel() sets a flag its search loop never reads — so the client hung for
however long the in-flight calculation had left. Anything that re-issues an elytra goal shortly
after cancelling it, which is what an automated hunt does every couple of seconds, froze the game
for up to ten seconds at a time.

The engage is deferred onto the teardown now instead of waiting on it, guarded by an epoch counter
so that a cancel, a new goal or leaving the world drops the deferred engage rather than resurrecting
a stale one. The native calculation's timeout is three seconds instead of ten, since with cancel()
being a no-op that timeout is the only thing bounding how long a teardown can take.

Failed to compute path to destination, forever — 26.2 only

A region the pathfinder failed to parse is never retried, so one bad read poisons every later
calculation in that context. Because a cancel deliberately keeps the context, and a driver that
re-issues its goal on a timer keeps asking, the result was that message on a loop for the rest of
the session with no way out short of a relog.

Three consecutive failures now discard the context so the next attempt starts from a clean cache.
Skipped while flying, where dropping the context would disengage the elytra outright; the retries
after landing pick it up.

Two arithmetic bugs above the build limit — 26.2 only

BuildLimitPathFinder.generateDirectPath divides 8 by the horizontal distance to its destination to
get a step vector, so a destination directly above or below made every step NaN and the path with
it. Separately, the fallback for a failed transition compared a squared distance against an
unsquared 32, so anything past 6 blocks took the wrong branch and the route over the roof was never
used.

Why this fork

This is MeteorDevelopment's Fabric Baritone with fixes aimed at one workload: travelling a very long
way, unattended, without dying. Everything upstream does — #goto, #mine, #follow, #build,
#farm, the whole API — works here unchanged, and nothing in the fork touches how movement is sent
to the server.

That workload is unforgiving in a way ordinary Baritone use is not. A hunt is a program that flies
for hours with nobody at the keyboard and writes down what it sees, over terrain that no longer
matches anybody's cache because someone has been through it with TNT. It is decided by two things
that upstream is careless about: whether it stops, and whether the record it writes is worth
reading afterwards. Everything in 1.1.0 and 1.2.0 is one of those two.

Not stopping. Upstream's #elytra is good at flying a path it has already computed and bad at
everything around it. It could only take off by walking to an overhang and stepping off — which on
a nether highway or a flat overworld does not exist, and you land where the rockets ran out, not
next to a cliff. When the solver ran out of options it returned a heading with no pitch, which in
practice holds the last pitch straight into whatever is in front of you, exactly when it matters:
a wall appeared, the world changed under the path, something knocked you off course. Its flight
simulation had drifted from vanilla's in five separate ways, and every tick of that error compounds
across the horizon it picks a pitch from — when rockets are the budget, that is the budget.
A path node buried in terrain was noticed and then ignored. 1.1.0 fixed all of that; 1.2.0 removes
the last three ways an unattended run could end in a freeze, a segfault or a message repeating
every two seconds.

The record. The chunk cache is the output of a hunt, not scratch space, and until this release
it was being rewritten in place under its own readers.

The rest is the walking half, from 1.1.0: falls charged a tick of gravity that never happens and a
block of fall that does not exist, waterlogged fences and panes treated as open water, powder snow
and berry bushes considered jumpable, a build limit check that let the search climb 64 blocks past
it, GoalRunAway collapsing its own bounding box, and a set of races between the pathing thread and
the game thread — ToolSet reading the live inventory mid-calculation, assumeWalkOnWater read per
node instead of per calculation, a non-volatile cancel flag.

Install

Take the jar for your Minecraft version and drop it in mods/ next to Fabric API.

  • baritone-api-fabric-*.jar — use this one if another mod drives Baritone through its API.
    Alongside Meteor Client and its addons, this is the one you want.
  • baritone-standalone-fabric-*.jar — everything exposed, for using Baritone on its own.
  • baritone-unoptimized-fabric-*.jar — not obfuscated. Use it when reporting a bug so the stack
    trace is readable.

Verify your download against checksums.txt. Java 21 for the 1.21.x releases, Java 25 for 26.x.

Baritone 1.2.0 for Minecraft 1.21.8

Choose a tag to compare

@github-actions github-actions released this 02 Sep 00:20

1.1.0 fixed everything that stopped #elytra getting off the ground. 1.2.0 fixes what goes wrong
once it has been in the air for a few hours with nobody watching: a chunk cache that corrupted
itself, a pathfinder that could never recover from reading it, and a cancel that could hang or
crash the client.

The same fixes are on every supported Minecraft version, except the three marked 26.2 only —
those are bugs in the persistent nether pathfinder context that only 26.2 has.

Region files were rewritten in place

CachedRegion.save opened a region file and wrote straight over it, from the cache save thread,
while everything else was still reading it. A read that landed mid-rewrite got a truncated gzip
stream and threw the whole region away.

That file is the record of a hunt. Baritone's chunk cache keeps, for every chunk you have ever
loaded, the position of every chest, trapped chest, ender chest, shulker box, spawner, furnace and
end portal frame in it — that is what #find chest and #find shulker_box read. Losing a 512×512
region to a torn write throws away everything you flew over there, and you do not find out until
you go looking for it.

On 26.2 it is worse than lost data. With elytraUseCache on, those same files are handed to the
nether pathfinder, which parses each region lazily, once, and writes it off permanently if the
parse throws. One torn read leaves a hole in the world the elytra solver plans through for the rest
of that context's life.

Saves now go to a temp file that is renamed over the old one, so a reader sees either the previous
region or the new one and never half of each. The temp name carries the pid, so two clients sharing
a cache directory cannot corrupt each other's writes.

Cancelling #elytra could crash the client

The nether pathfinder's native context was freed as soon as its own executors had drained. That
accounts for the packing thread and the solver, and for nobody else — and the game thread is
somebody else. A finished path calculation hands its result to the game thread, and that hand-off
can still be sitting in the game thread's task queue when the free happens; the first thing it does
with the result is raytrace through the context to trim it. Running that against freed memory is a
segfault, not an exception, so it takes the whole client with it and leaves a hs_err file rather
than a stack trace.

The context is freed on the game thread now, after everything else that can touch it is gone, and
every native call is fenced behind a flag that fails safe — nothing is visible, nothing has a
chunk, every raytrace reports a hit — so a caller that arrives late gets a useless answer instead
of a crash. On 26.2, where the context outlives the flight, it is freed under the same lock the
solver and the game thread hold.

Cancelling #elytra could also freeze the game — 26.2 only

26.2 keeps one pathfinder context alive across engages, and the next engage blocked the game thread
on the old one until it had been freed. A native path calculation cannot be interrupted —
nether-pathfinder's cancel() sets a flag its search loop never reads — so the client hung for
however long the in-flight calculation had left. Anything that re-issues an elytra goal shortly
after cancelling it, which is what an automated hunt does every couple of seconds, froze the game
for up to ten seconds at a time.

The engage is deferred onto the teardown now instead of waiting on it, guarded by an epoch counter
so that a cancel, a new goal or leaving the world drops the deferred engage rather than resurrecting
a stale one. The native calculation's timeout is three seconds instead of ten, since with cancel()
being a no-op that timeout is the only thing bounding how long a teardown can take.

Failed to compute path to destination, forever — 26.2 only

A region the pathfinder failed to parse is never retried, so one bad read poisons every later
calculation in that context. Because a cancel deliberately keeps the context, and a driver that
re-issues its goal on a timer keeps asking, the result was that message on a loop for the rest of
the session with no way out short of a relog.

Three consecutive failures now discard the context so the next attempt starts from a clean cache.
Skipped while flying, where dropping the context would disengage the elytra outright; the retries
after landing pick it up.

Two arithmetic bugs above the build limit — 26.2 only

BuildLimitPathFinder.generateDirectPath divides 8 by the horizontal distance to its destination to
get a step vector, so a destination directly above or below made every step NaN and the path with
it. Separately, the fallback for a failed transition compared a squared distance against an
unsquared 32, so anything past 6 blocks took the wrong branch and the route over the roof was never
used.

Why this fork

This is MeteorDevelopment's Fabric Baritone with fixes aimed at one workload: travelling a very long
way, unattended, without dying. Everything upstream does — #goto, #mine, #follow, #build,
#farm, the whole API — works here unchanged, and nothing in the fork touches how movement is sent
to the server.

That workload is unforgiving in a way ordinary Baritone use is not. A hunt is a program that flies
for hours with nobody at the keyboard and writes down what it sees, over terrain that no longer
matches anybody's cache because someone has been through it with TNT. It is decided by two things
that upstream is careless about: whether it stops, and whether the record it writes is worth
reading afterwards. Everything in 1.1.0 and 1.2.0 is one of those two.

Not stopping. Upstream's #elytra is good at flying a path it has already computed and bad at
everything around it. It could only take off by walking to an overhang and stepping off — which on
a nether highway or a flat overworld does not exist, and you land where the rockets ran out, not
next to a cliff. When the solver ran out of options it returned a heading with no pitch, which in
practice holds the last pitch straight into whatever is in front of you, exactly when it matters:
a wall appeared, the world changed under the path, something knocked you off course. Its flight
simulation had drifted from vanilla's in five separate ways, and every tick of that error compounds
across the horizon it picks a pitch from — when rockets are the budget, that is the budget.
A path node buried in terrain was noticed and then ignored. 1.1.0 fixed all of that; 1.2.0 removes
the last three ways an unattended run could end in a freeze, a segfault or a message repeating
every two seconds.

The record. The chunk cache is the output of a hunt, not scratch space, and until this release
it was being rewritten in place under its own readers.

The rest is the walking half, from 1.1.0: falls charged a tick of gravity that never happens and a
block of fall that does not exist, waterlogged fences and panes treated as open water, powder snow
and berry bushes considered jumpable, a build limit check that let the search climb 64 blocks past
it, GoalRunAway collapsing its own bounding box, and a set of races between the pathing thread and
the game thread — ToolSet reading the live inventory mid-calculation, assumeWalkOnWater read per
node instead of per calculation, a non-volatile cancel flag.

Install

Take the jar for your Minecraft version and drop it in mods/ next to Fabric API.

  • baritone-api-fabric-*.jar — use this one if another mod drives Baritone through its API.
    Alongside Meteor Client and its addons, this is the one you want.
  • baritone-standalone-fabric-*.jar — everything exposed, for using Baritone on its own.
  • baritone-unoptimized-fabric-*.jar — not obfuscated. Use it when reporting a bug so the stack
    trace is readable.

Verify your download against checksums.txt. Java 21 for the 1.21.x releases, Java 25 for 26.x.

Baritone 1.2.0 for Minecraft 1.21.5

Choose a tag to compare

@github-actions github-actions released this 02 Sep 00:19

1.1.0 fixed everything that stopped #elytra getting off the ground. 1.2.0 fixes what goes wrong
once it has been in the air for a few hours with nobody watching: a chunk cache that corrupted
itself, a pathfinder that could never recover from reading it, and a cancel that could hang or
crash the client.

The same fixes are on every supported Minecraft version, except the three marked 26.2 only —
those are bugs in the persistent nether pathfinder context that only 26.2 has.

Region files were rewritten in place

CachedRegion.save opened a region file and wrote straight over it, from the cache save thread,
while everything else was still reading it. A read that landed mid-rewrite got a truncated gzip
stream and threw the whole region away.

That file is the record of a hunt. Baritone's chunk cache keeps, for every chunk you have ever
loaded, the position of every chest, trapped chest, ender chest, shulker box, spawner, furnace and
end portal frame in it — that is what #find chest and #find shulker_box read. Losing a 512×512
region to a torn write throws away everything you flew over there, and you do not find out until
you go looking for it.

On 26.2 it is worse than lost data. With elytraUseCache on, those same files are handed to the
nether pathfinder, which parses each region lazily, once, and writes it off permanently if the
parse throws. One torn read leaves a hole in the world the elytra solver plans through for the rest
of that context's life.

Saves now go to a temp file that is renamed over the old one, so a reader sees either the previous
region or the new one and never half of each. The temp name carries the pid, so two clients sharing
a cache directory cannot corrupt each other's writes.

Cancelling #elytra could crash the client

The nether pathfinder's native context was freed as soon as its own executors had drained. That
accounts for the packing thread and the solver, and for nobody else — and the game thread is
somebody else. A finished path calculation hands its result to the game thread, and that hand-off
can still be sitting in the game thread's task queue when the free happens; the first thing it does
with the result is raytrace through the context to trim it. Running that against freed memory is a
segfault, not an exception, so it takes the whole client with it and leaves a hs_err file rather
than a stack trace.

The context is freed on the game thread now, after everything else that can touch it is gone, and
every native call is fenced behind a flag that fails safe — nothing is visible, nothing has a
chunk, every raytrace reports a hit — so a caller that arrives late gets a useless answer instead
of a crash. On 26.2, where the context outlives the flight, it is freed under the same lock the
solver and the game thread hold.

Cancelling #elytra could also freeze the game — 26.2 only

26.2 keeps one pathfinder context alive across engages, and the next engage blocked the game thread
on the old one until it had been freed. A native path calculation cannot be interrupted —
nether-pathfinder's cancel() sets a flag its search loop never reads — so the client hung for
however long the in-flight calculation had left. Anything that re-issues an elytra goal shortly
after cancelling it, which is what an automated hunt does every couple of seconds, froze the game
for up to ten seconds at a time.

The engage is deferred onto the teardown now instead of waiting on it, guarded by an epoch counter
so that a cancel, a new goal or leaving the world drops the deferred engage rather than resurrecting
a stale one. The native calculation's timeout is three seconds instead of ten, since with cancel()
being a no-op that timeout is the only thing bounding how long a teardown can take.

Failed to compute path to destination, forever — 26.2 only

A region the pathfinder failed to parse is never retried, so one bad read poisons every later
calculation in that context. Because a cancel deliberately keeps the context, and a driver that
re-issues its goal on a timer keeps asking, the result was that message on a loop for the rest of
the session with no way out short of a relog.

Three consecutive failures now discard the context so the next attempt starts from a clean cache.
Skipped while flying, where dropping the context would disengage the elytra outright; the retries
after landing pick it up.

Two arithmetic bugs above the build limit — 26.2 only

BuildLimitPathFinder.generateDirectPath divides 8 by the horizontal distance to its destination to
get a step vector, so a destination directly above or below made every step NaN and the path with
it. Separately, the fallback for a failed transition compared a squared distance against an
unsquared 32, so anything past 6 blocks took the wrong branch and the route over the roof was never
used.

Why this fork

This is MeteorDevelopment's Fabric Baritone with fixes aimed at one workload: travelling a very long
way, unattended, without dying. Everything upstream does — #goto, #mine, #follow, #build,
#farm, the whole API — works here unchanged, and nothing in the fork touches how movement is sent
to the server.

That workload is unforgiving in a way ordinary Baritone use is not. A hunt is a program that flies
for hours with nobody at the keyboard and writes down what it sees, over terrain that no longer
matches anybody's cache because someone has been through it with TNT. It is decided by two things
that upstream is careless about: whether it stops, and whether the record it writes is worth
reading afterwards. Everything in 1.1.0 and 1.2.0 is one of those two.

Not stopping. Upstream's #elytra is good at flying a path it has already computed and bad at
everything around it. It could only take off by walking to an overhang and stepping off — which on
a nether highway or a flat overworld does not exist, and you land where the rockets ran out, not
next to a cliff. When the solver ran out of options it returned a heading with no pitch, which in
practice holds the last pitch straight into whatever is in front of you, exactly when it matters:
a wall appeared, the world changed under the path, something knocked you off course. Its flight
simulation had drifted from vanilla's in five separate ways, and every tick of that error compounds
across the horizon it picks a pitch from — when rockets are the budget, that is the budget.
A path node buried in terrain was noticed and then ignored. 1.1.0 fixed all of that; 1.2.0 removes
the last three ways an unattended run could end in a freeze, a segfault or a message repeating
every two seconds.

The record. The chunk cache is the output of a hunt, not scratch space, and until this release
it was being rewritten in place under its own readers.

The rest is the walking half, from 1.1.0: falls charged a tick of gravity that never happens and a
block of fall that does not exist, waterlogged fences and panes treated as open water, powder snow
and berry bushes considered jumpable, a build limit check that let the search climb 64 blocks past
it, GoalRunAway collapsing its own bounding box, and a set of races between the pathing thread and
the game thread — ToolSet reading the live inventory mid-calculation, assumeWalkOnWater read per
node instead of per calculation, a non-volatile cancel flag.

Install

Take the jar for your Minecraft version and drop it in mods/ next to Fabric API.

  • baritone-api-fabric-*.jar — use this one if another mod drives Baritone through its API.
    Alongside Meteor Client and its addons, this is the one you want.
  • baritone-standalone-fabric-*.jar — everything exposed, for using Baritone on its own.
  • baritone-unoptimized-fabric-*.jar — not obfuscated. Use it when reporting a bug so the stack
    trace is readable.

Verify your download against checksums.txt. Java 21 for the 1.21.x releases, Java 25 for 26.x.