Skip to content

Fly with the Java port of nether-pathfinder - #19

Merged
0Mattias merged 4 commits into
mainfrom
claude/java-nether-pathfinder
Sep 12, 2026
Merged

0Mattias merged 4 commits into
mainfrom
claude/java-nether-pathfinder

Conversation

@0Mattias

@0Mattias 0Mattias commented Sep 12, 2026

Copy link
Copy Markdown
Owner

The Nether pathfinder is now the Java port of babbaj's nether-pathfinder, under cheesecake.process.elytra.pathfinder, in place of the native library and its nested jar. The port was written for babbaj/nether-pathfinder#31 and goes to Baritone next.

Four commits: the package with its tests and oracle files; the seam; a grep in the smoke test that tolerates the log's colour codes; and a fix to the region reader, which took each byte's two-bit block types from the top down while Baritone writes them lowest bit first, so every aligned run of four blocks along x came back mirrored in any chunk a search took from the region cache (the native library has the same bug). NetherPathfinderContext gets a Chunk where it had a pointer; the dependency, the maven repository, the Unsafe fill, the system check and NullElytraProcess go; elytraCustomAllocator stays as a deprecated no-op so that settings files naming it still load.

What the native library made necessary, reconsidered as #18 asked: the read-write lock stays on the same sides as before; destroy() no longer takes the write lock; the thirty-second watchdog rebuilds the context instead of leaking one; UnusableRays stays, for the answers the flight wants. The README's status bullets say what is left of the three upstream issues. CI checks the jar for the classes rather than the nested jar, takes a Cheesecake loaded line the provider now logs as proof the mod ran, and drops the detection of the native exit code.

Verified locally: ./gradlew build green with 100 tests, 34 of them the port's; the four in-world elytra flights passed (above the build limit, auto-jump, Nether below and above the roof).

From the port's branch: rays 0.56 to 0.67 µs against the native 0.75, searches and terrain generation level with native, generated chunks and ray hits bit-identical to the native oracle.

Closes #18.

🤖 Generated with Claude Code

0Mattias and others added 4 commits September 12, 2026 03:35
The whole of babbaj's nether-pathfinder in plain Java, under
cheesecake.process.elytra.pathfinder, as it stands on the java-port branch
of 0Mattias/nether-pathfinder at 6d807d0, written for
babbaj/nether-pathfinder#31. Each class is the C++ file of the same name:
the chunk octree, the raytracer, the A* search over octree cubes, the
Nether terrain generator and the region-file reader. The page allocator,
the thread pool, the JNI layer and the Unsafe accessors have no
counterpart: a chunk is an object that lives as long as it is referenced,
the table is a ConcurrentHashMap, and lookups, inserts, the search and
rays can run at once from any thread.

The tests hold the port to the native library's answers. An FNV-1a hash of
each of 625 generated chunks and the hit and hit position of 4000 rays
over generated terrain, recorded by the oracle in that repository, are
reproduced to the bit. The search, the table, region files, cancel, the
segment time-out and the rays the native library could not take -- a
point, a coordinate that is not finite, an end on a voxel corner -- have
tests of their own. Nothing uses the package yet; the next commit does.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
NetherPathfinderContext asks cheesecake.process.elytra.pathfinder for paths
and lines of sight, with a Chunk object where the native library handed out
a pointer: the chunk packer fills a Chunk, a block update sets a bit on one,
the solver's block lookups cache one, and there is nothing to free. The
Unsafe fill of an all-solid section is Chunk.fillSection. The
dev.babbaj:nether-pathfinder dependency, its maven repository, the nested
jar and the natives it carried for each platform go, and with them the
system check: every system is supported, so NullElytraProcess goes too, and
elytraCustomAllocator is kept only so that a settings file naming it still
loads.

What the native library made necessary is reconsidered, as #18 asked:

- The read-write lock stays, on the same sides as before, so that the
  threads run in the order the flights were tested in. The port needs none
  of it, and dropping it is a follow-up.
- destroy() no longer takes the write lock to free the context. Closing the
  table under a running ray or search only changes what it answers.
- A path calculation that has not answered in thirty seconds rebuilds its
  context instead of leaking one, since there is no hung thread to join.
- UnusableRays stays, for the answers the flight wants: a point is visible
  from itself, a ray that is not finite is not, and an end on a voxel
  corner is decided by the voxel the ray crosses last.

CI checks the jar for the pathfinder's classes instead of the nested jar,
the headless client's proof that the mod ran is a line the provider now
logs, and the detection of the native library's exit(696969) goes. The
README's status bullets say what is left of the three upstream issues.

Closes #18.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The headless client's proof that the mod ran is the "Cheesecake loaded"
line the provider logs, and the first run of it never matched: the client
colours its log lines, and the escape codes sit between the logger's name
in parentheses and the message, so a fixed string with a space between the
two finds nothing. A pattern that allows anything between them matches the
line as a local client wrote it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Baritone's CachedRegion writes a chunk's two bits per block with
BitSet.toByteArray(): bit n sits in byte n / 8 at position n % 8, the
lowest bit first. The native reader took a byte's bits from the top down
(baritone.cpp, get2Bits: `>> (6 - (i % 8))`) and the port copied it, so
block x of every aligned run of four along x came back as block x ^ 3:
each such run mirrored, in every chunk a search took from the region cache
rather than from the game. Hard to see in flight, since the cache only
fills in what the game has not loaded and terrain in runs of four is close
to its own mirror image, but wrong. The test packed its file the same
wrong way and passed; it now builds the chunk with a BitSet as CachedChunk
does, sets one block solid and one water, and checks that the mirror
positions are air. The region directory is Baritone's `cache`, not
`regions`, in the docs and the test, and the oracle's doc names its real
path. The same fix, on the port's own branch, is 0Mattias/nether-pathfinder
4784d69.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@0Mattias
0Mattias merged commit 1bbdb59 into main Sep 12, 2026
8 checks passed
@0Mattias
0Mattias deleted the claude/java-nether-pathfinder branch September 13, 2026 00:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bump nether-pathfinder once the three upstream fixes ship

1 participant