A Minecraft pathfinder bot. This is a fork of Baritone by
leijurv, tracking the Fabric branches of
MeteorDevelopment/baritone, with fixes aimed at the
one thing anarchy servers ask of a pathfinder: travelling a very long way, unattended, without
dying. Everything upstream Baritone does — #goto, #mine, #follow, #build, #farm — works
here unchanged.
Every fix lands on every supported Minecraft version, except the handful of bugs that only ever existed on one of them.
| Minecraft | Branch | Java | Release tag |
|---|---|---|---|
| 1.21.4 | 1.21.4 |
21 | v<version>-1.21.4 |
| 1.21.5 | 1.21.5 |
21 | v<version>-1.21.5 |
| 1.21.8 | 1.21.8 |
21 | v<version>-1.21.8 |
| 1.21.11 | 1.21.11 |
21 | v<version>-1.21.11 |
| 26.1 | 26.1 |
25 | v<version>-26.1 |
| 26.2 | 26.2 |
25 | v<version>-26.2 |
| 26.3 | 26.3 |
25 | v<version>-26.3 |
Each Minecraft version has its own branch and its own release. Fabric only.
- Grab the jar for your Minecraft version from Releases.
baritone-api-fabric-*.jar— use this one if another mod (Meteor Client, an addon, your own mod) drives Baritone through its API. This is what most people 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.
- Drop it in
mods/next to Fabric API. - Type
#goto 1000 500in chat.
Verify your download against checksums.txt on the release. See SETUP.md for anything
more involved, and USAGE.md for the chat commands.
#elytra is the reason this fork exists. Upstream's implementation is good at flying a computed
path and bad at everything around it: getting into the air, and getting out of trouble once the
world stops matching the path.
- Takeoff from anywhere.
elytraAutoJumponly knew how to walk to an overhang and step off it. With no overhang in reach — most of the overworld, and most of a nether highway — it spent a couple of seconds pathfinding and then told you to go find a cliff. It now falls back to jumping on the spot, opening the elytra on the way up the way a vanilla double tap does, and lighting a firework the instant it opens. Three ways it could previously stand still forever are fixed as well, including landing at exactly y=31 in the nether, where the old fixed takeoff goal was already satisfied where you stood. - A survival fallback. When the solver ran out of options it returned a heading with no pitch, which in practice means holding the last pitch straight into the terrain. That is exactly the case that matters: the world changed under the path, a wall appeared, something knocked you off course. It now simulates the pitch range and flies whatever trajectory stays clear the longest, preferring the one that ends higher, and forces a firework when nothing else buys enough time.
- Physics that match the server. The flight simulation had drifted from
LivingEntity.updateFallFlyingMovement: hardcoded gravity instead of theGRAVITYattribute, no Slow Falling, a cosine term fromMth's lookup table with a factor vanilla does not have, a missing divide-by-zero guard that producedNaNwhen looking straight up or down, and drag asfloatwhere vanilla usesdouble. Every tick of error compounds across the horizon the solver picks a pitch from. - Recalculating instead of hoping. A path node buried in terrain used to be noticed and ignored. The segment is now recomputed, rejoining the existing path at the last node still in open air.
elytraPathLookaheadexposes how far ahead the solver looks for a node to aim at (was a hardcoded 20). Higher cuts corners harder at firework speed.elytraStandingTakeoffturns the standing takeoff on and off. On by default, capped at three attempts so a bad spot cannot burn a stack of rockets.- Taking off from a hole. Where a nether flight ends when the rockets run out is where the next one starts, 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 asked for a path from your feet, and the pathfinder starts every path from the nearest 4×4×4 cube of air, found by a search that walks straight through walls — from a hole, very often a cube on the far side of one. The rocket flew at it, hit the wall and dropped back in. The path now starts from the first clear cube straight overhead. With a rocket burning, the survival solver takes the pitch that ends highest rather than the first one that survives, and it forces a rocket as soon as an impact is anywhere in its horizon instead of a dozen ticks out.
- The takeoff rocket was never accepted. It was lit in the same tick the elytra opened, which no vanilla client can do — its use packet goes out before the glide packet — and it carried the camera's rotation while that tick's movement packet carried the takeoff aim, two looks the server's movement checks compare. The rocket goes the tick after now, every use packet carries the rotation the movement packet will, and the solver assumes a rocket it has just lit is burning instead of planning an unboosted glide into the nearest wall while the entity is in transit. A takeoff the server refuses is reported as such.
- Lava. An elytra opens in lava but does nothing there — vanilla moves you by the fluid rules — so the rocket is the only thrust, and it was aimed at a path that leads through the pond's wall while the landing logic asked for a new path from inside the pool twenty times a second. In lava the process now swims up, opens the elytra, aims straight up and lights rockets until it is out; the landing search waits.
- Unfinished segments. A recomputation around an obstacle that ran out of time short of its rejoin node had the old path stitched on anyway, leaving a blind jump through whatever lay between; every recomputation ended the same way, and the solver circled next to a path drawn through solid ground. An unfinished segment now stands on its own and is continued from where the search got to.
- A cache that disagrees with the world. Everything the solver does runs against the pathfinder's copy of the world, and when that copy was wrong nothing noticed, because the raytraces look at the same copy. A chunk packet the client discarded was packed as a real chunk made of air, and a chunk whose packing was missed stayed missed until a cancel. Discarded chunks are skipped now, and a collision, a "we are inside a block" verdict or a failed recomputation re-reads the chunks around the player.
- A hung teardown. The pathfinder context's teardown waited without limit for a native search
that the cancel cannot interrupt. On 26.2, where the teardown hands back the semaphore the next
engage needs, one hung search silently killed every later
#elytrafor the session; elsewhere 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. - Cancelling could crash the client. The nether pathfinder's native context was freed once its own executors had drained, but the solver and the game thread raytrace through it outside those executors, and the octree interface caches a raw pointer into it. Running one of those against freed memory is a segfault, not an exception. It is freed under the lock they hold now, and every native call is fenced behind a flag that fails safe.
- The XaeroPlus path overlay. XaeroPlus finds the elytra path by field name, and the obfuscated jars renamed those fields, so the path never showed on its map. They keep their names now.
- Region files were rewritten in place, from the cache save thread, while everything else was
reading them. A read that landed mid-rewrite got a truncated gzip stream and threw away a
512×512 region's worth of cached terrain. Saves are written to a temp file and renamed over the
old one, so a reader sees either the previous region or the new one. On 26.2 this is what the
nether pathfinder reads with
elytraUseCacheon, and a region it fails to parse is gone for the rest of the flight. - Waypoint files were written the same way, in place, and a save cut off mid-write — a crash, or the game exiting while the file was still being written — left a truncated file that the next load silently discarded, taking every waypoint of that tag with it. They are written to a temp file and renamed over the old one too, so an interrupted save keeps the previous waypoints.
- RAM is bounded. Regions were only dropped from RAM after the ten minute save, and only by
distance from the player, so nothing capped how much of the cache stayed resident on a long trip.
A maintenance thread now saves changed regions every 30 seconds (
cachedRegionsSaveIntervalSeconds), drops regions nothing has touched for three minutes (cachedRegionsIdleSeconds), and then the least recently used ones while the cache is over 192 MiB (cachedRegionsRamBudgetMB). A region with unsaved changes is kept while they can still be saved; withchunkCachingoff or no cache directory nothing ever saves them, so those regions are evicted like any other instead of pinning the whole session's terrain in RAM. A path calculation that reaches a region on disk but not in RAM loads it back in the background (cachedRegionsLoadOnDemand), so terrain seen in an earlier session counts again. - Less packing. The check meant to skip empty sections tested for
null, which modern chunks never are, so every all-air section cost 4096 block lookups to pack to zeros: most of an overworld column above the surface and the nether above the roof. Those are skipped now, and a chunk that unloads unchanged since it was packed on load is not packed again (repackUnchangedChunksOnUnloadbrings that back). - Server folder names. Only Windows had the colon of a
host:portserver replaced, so on an NTFS drive mounted under Linux such a server never got a cache directory and every region save failed. Names are sanitised on every platform now, and an existing directory under the old name keeps being used.
- Falls were charged one tick of gravity that never happens, because
velocity(0)is zero. - Descending onto a block counted one block of fall too many; the fall ends on top of the block.
GoalRunAwayfolded its bounding box's max corner against the running minimum, so with more than one position the box collapsed towards the minimum corner.- The dimension height check compared a y coordinate against a block count, letting the search climb 64 blocks above the overworld build limit.
- Waterlogged fences, walls, panes, bars, stairs and leaves were treated as plain water and walked into.
fullyPassablelisted the blocks you cannot jump through one at a time, so powder snow, honey, berry bushes, dripstone and anything inblocksToAvoidwere considered jumpable. The hitbox is the same width in the air as on the ground.- A diagonal's second corner was tested for lava but its magma check read the first corner again.
backtrackCostFavoringCoefficientat 0 or below produced a non-positive action cost, which A* rejects, failing the whole calculation instead of favoring nothing.GoalGetToBlockreported an ETA that never counted down to zero.GoalNearandGoalRunAwaysquaredintcoordinate differences, which overflow: a point 65,536 blocks away could satisfy a two-blockGoalNear.GoalRunAwayalso truncated its radius and threw when hashed without a y constraint.#findsearched the cache around the player's y where it meant their z.
#litematicacould report Litematica as missing with it installed: the check for it pulled in one of Litematica's world classes before the game could load it.- With
buildIgnoreDirectionorbuildIgnorePropertiesset, the comparison between the placed block and the schematic checked each property value by reference rather than by value. Right for every vanilla property by luck — small integers happen to be interned — and nothing guaranteed it stayed that way; it compares by value now.
ToolSetread the live inventory from the calculation thread — a data race with the game thread — and cached break speeds in a plainHashMap. The hotbar and selected slot are now snapshotted on construction and the cache is concurrent.assumeWalkOnWaterwas read off the settings mid-calculation, so flipping it made the cost function inconsistent with the nodes already expanded. It is read once perBlockStateInterface.cancelRequestedis written by the game thread and read by the calculation thread, and was notvolatile.bestPathSoFarallocated aBetterBlockPosper node on every tick a calculation was running, before the cheap checks that almost always decide the outcome;PathExecutorbuilt a wholeCalculationContextper tick to read two fields off it.
- Nether pathfinder chunk packing wrote solid sections as air, so
#elytraplanned paths straight through terrain. The 26.2 port had replaced the removedLevelChunkSection.SECTION_SIZE(4096, blocks per section) with a local16, and the all-solid fast path then memset 2 bytes instead of 512. - Baritone's threadpool threads are daemon threads. As of 26.2 the game waits on non-daemon threads at shutdown and files a crash report if any are still alive.
- Cancelling
#elytrafroze the game. 26.2 keeps one pathfinder context alive across engages, and re-engaging blocked the game thread on the old one until it was freed. A path calculation cannot be interrupted — nether-pathfinder'scancel()sets a flag its search loop does not read — so the client hung for whatever the in-flight calculation had left, up to ten seconds. The engage is deferred onto the teardown now, and the calculation timeout is three seconds. - The endless
Failed to compute path to destination. That same persistent context is fed Baritone's region cache, and a region it fails to parse is never retried, so one bad read leaves a permanent hole in its world and every later calculation fails against it. Three consecutive failures discard the context so the next attempt starts from a clean cache; skipped while flying, where a reset would drop the elytra. - The above-build-limit fallback divided by a horizontal distance of zero, making every step of a
straight-up path
NaN, and compared a squared distance against an unsquared 32. - Segfault in
getChunkOrDefault. The native path calculation ran under the read lock whenever terrain prediction was off, on the theory that it only reads the cache. WithelytraUseCacheit also parses region files into it, and an insert that rehashes the map under the solvers' lookups is heap corruption. A path calculation is always a writer now. #elytragoing idle mid-engage. An engage deferred behind the old context's teardown left the process without a behavior, so it reported itself inactive and dropped its destination; the pathing manager stopped ticking it and anything polling for the goal issued it again. It stays active and keeps its destination until the engage lands.#minestopped picking up items. Upstream's #5058 resolved loot tables through the client's registries, which never contain them, so every block had no drops. Reverted, as upstream did.
Java 21 for the 1.21.x branches, Java 25 for 26.x (see the table above).
./gradlew build # jars land in dist/, with checksums.txt
./gradlew testTagging v<version>-<mc> on a version branch builds it and publishes the release.
The API is heavily documented. Anything outside the baritone.api package is not supported by the
API release jar.
BaritoneAPI.getSettings().allowSprint.value = true;
BaritoneAPI.getSettings().primaryTimeoutMS.value = 2000L;
BaritoneAPI.getProvider().getPrimaryBaritone().getCustomGoalProcess().setGoalAndPath(new GoalXZ(10000, 20000));Baritone is by leijurv and its contributors; it grew out of MineBot. The Fabric branches this fork tracks are maintained by MeteorDevelopment. The elytra pathfinder is nether-pathfinder by babbaj.
For help with Baritone itself, the Baritone Discord is the place to ask. Issues with this fork belong here.
LGPL-3.0, same as upstream. See LICENSE.