Skip to content

[pull] 1.21.4 from cabaletta:1.21.4 - #25

Merged
pull[bot] merged 10 commits into
CCBlueX:1.21.4from
cabaletta:1.21.4
Sep 16, 2026
Merged

pull[bot] merged 10 commits into
CCBlueX:1.21.4from
cabaletta:1.21.4

Conversation

@pull

@pull pull Bot commented Sep 16, 2026

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

pirateben820 and others added 10 commits September 16, 2026 06:45
With Baritone installed the client does not exit when the window closes: it
sits in shutdown until Mojang's ClientShutdownWatchdog kills it, about
fifteen seconds later, and on Windows the leftover javaw holds a lock on the
mods folder until then.

The shared pool is built with the default thread factory, so every worker is
non-daemon, and none of them ever exit. The core threads (corePoolSize 4, and
allowCoreThreadTimeOut is never set) park in take() for the life of the JVM
whether or not they have work, and two of them are permanently busy anyway
with CachedWorld's chunk packer and its ten-minute save loop. Once
Minecraft.main returns, those are the only non-daemon threads left.

Daemon threads fix the exit. They also mean a periodic cache save that is
mid-write at exit can be cut off; #5116, which predates this PR, addresses
that with a shutdown hook.

The threads are named Baritone-N so a dump reads as such rather than
pool-4-thread-N.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
sameBlockstate compares each property's value with !=. The values come out of
BlockState.getValues() as boxed Comparable, so this is reference identity. It
is correct for enum and Boolean properties, whose values are singletons, and
for an IntegerProperty it happens to work today because every vanilla integer
property stays inside the -128..127 range the JVM's Integer cache interns
(the largest, age, tops out at 25). Nothing in the comparison guarantees that,
and identity is not what is meant here; equality is.

Objects.equals to match the three other comparisons in this file. Plain
equals would also be correct: both states share one StateDefinition, so the
lookups cannot return null.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
world.dimensionType() goes through a Holder every call and was being hit
twice per block lookup in BlockStateInterface.get0, plus once per node in
the A* loop. Cache minY/maxY/height once at construction instead.

Also split CalculationContext so everything that needs a live player or
client world is computed up front and passed to a protected constructor.
That lets a context (and a BlockStateInterface subclass) be built without
a running client, which is what the offline pathing benchmark needs. No
behaviour change; the y bound check in A* now uses the real maxY instead
of the dimension height.
Four changes to the search loop itself, none of which alter which path
comes out:

- Nodes now live in 16x8x16 cells (a PathNode[2048] per cell, indexed by
  the low coordinate bits) and only the cells are in a hash map. The flat
  position -> node map was the single hottest thing in the profile, since
  every lookup was a cache miss into a multi megabyte key array. Successors
  are within a few blocks of the node being expanded so most land in the
  cell we just used.
- The heap keeps combinedCost in a double[] next to the node array, so
  sift-up and sift-down compare primitives instead of chasing a pointer
  per level.
- Favoring is looked up once per node when it's first reached and cached
  on the PathNode, instead of once per incoming edge. The multiplier only
  depends on the destination so this is exactly equivalent.
- The seven partial-path coefficients are multiplied by precomputed
  reciprocals instead of divided.
The cost functions read roughly 150 blocks and evaluate about 30 mining
durations per expanded node, and most of that is the same handful of
blocks over and over. This makes each of those cheaper or rarer:

- canPlaceAgainst went through Block.isShapeFullBlock, a guava
  LoadingCache lookup that allocates a queue node on every hit. It was
  ~8% of the search (and ~45% of all allocation) just to check whether
  air is a full cube. It's now a bit in the precomputed flags, as is the
  block-only half of avoidBreaking, which was an ArrayList scan.
- PrecomputedData is shared across searches and only rebuilt when one of
  the settings it depends on changes, instead of starting cold (and
  allocating 25kB) every tick.
- The flags are stored on the BlockState itself through a mixin field,
  stamped with the table's generation, so a predicate is one field load
  rather than a Reference2IntOpenHashMap lookup. Falls back to a small
  identity cache and then the registry when the mixin isn't applied.
- The pathing thread's BlockStateInterface has a 64k entry direct mapped
  position -> state cache in front of the chunk/section/palette chain
  (~80% hit rate on the benchmark).
- getMiningDurationTicks is memoized by position for the search thread's
  context; the block under a node was being evaluated from four descends,
  a downward and the neighbours' descends, each reading five neighbours
  for the avoidBreaking check.

Together with the previous commit this is 1.2x to 1.6x more nodes per
second on an offline benchmark, depending on terrain.
…reads

Let the game exit: daemon threads for the shared pool, and wait for the final cache save
…uality

Compare block state property values with equals, not reference identity
the identity cache was only there for running the pathfinder outside the
game (benchmarks), where the mixin can't apply. that case just goes
through the registry array now, which is what it used to do anyway.
Make the pathfinder consider more movements per second
@pull pull Bot locked and limited conversation to collaborators Sep 16, 2026
@pull pull Bot added the ⤵️ pull label Sep 16, 2026
@pull
pull Bot merged commit 592f157 into CCBlueX:1.21.4 Sep 16, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants