Ferrite
A performance mod for Minecraft 26.3 (Fabric, JDK 25). Java handles integration and mixins; native Rust does the heavy per-tick math where crossing the JNI boundary actually pays. Server-side compatible: install on a server, players don't need it.
Headline numbers, all measured on real worlds:
- Cramming (default on): ~65% entity-tick reduction at 1000+ packed mobs. Bit-for-bit vanilla push math.
- Redstone (
/ferrite redstone ac on): Alternate Current algorithm plus a Rust BFS kernel. A lag machine that held vanilla at 1.4-1.8 TPS recovered to a flat 20.00 TPS. Zero mismatches across 150,000+ oracle checks. Off by default so contraptions tuned to vanilla update order keep working. - Hoppers (default on): extract loops skip drained slots, up to ~85% cheaper on partially-emptied chests. Opt-in hopper highway multiplies chain throughput ~3x for storage systems.
- Idle sign and furnace tickers suppressed (default on): ~70% block-entity tick reduction at scale, self-healing, mod-subclass safe.
- Pre-gen and predictive chunk forcing (opt-in): 90-118 chunks/s spawn pre-generation with resume, and a generation ring that leads your flight path so you never see terrain loading.
Worldgen math (noise, biomes, density functions) is ported bit-exact and validated every release; parity checks run 63/63 noise and 50/50 density on 26.2.
Every 5 seconds the mod logs where your tick time goes, so optimization targets real bottlenecks. Runtime toggle: /ferrite log monitors off.
Requirements
- Minecraft 26.2 (26.1.2 and 1.21.11 builds available as older releases)
- Java 25, Fabric Loader 0.19.3+, Fabric API 0.154.2+26.2
- Singleplayer and multiplayer
Platforms
Natives bundled for Windows x86_64, Linux x86_64, Linux aarch64 (Not fully tested on a Raspberry Pi 4B), and macOS (universal). If the native fails to load, Ferrite falls back to vanilla behavior automatically: no crashes, no broken worlds.
How to help
Play 10+ minutes with mob farms or crowded worlds, search latest.log for [ferrite], and share the [cramming-dispatch] and [entity-tick] lines in a GitHub issue. Low-end hardware reports are especially useful.
Full measurement tables, methodology, and source: github.com/VoiceLessQ/Ferrite
Credits
- Redstone wire algorithm adapted from Space Walker's Alternate Current (MIT); the design and algorithm remain entirely Space Walker's.
- JNI scaffolding originally forked from Brayan-724/rust-mod-probe.
-
- Linux aarch64 support contributed and tested on real hardware by cwright814.
MIT licensed.
All Versions
+ Add Version### Added
- Ore placement kernel, on by default.** Same blocks and random
4-core server (pregen about 6% sooner). An oracle replays 1 in 64
veins; a full check of 1.5M veins in the overworld and nether found
no differences. Off:
/ferrite ore off or -Dferrite.orekernel=off.Counters:
/ferrite ore report. Odd ore in new chunks? Open anissue with seed, coordinates and the report line.
- Entity tracker probe.**
/ferrite probe tracker on|report|reset
zombies on a 4-core server: 0.45 to 0.66 ms/tick, under 3%.
- Surface rule compiler, on by default.** The 26.3 material rule
vanilla rules in vanilla order, so datapacks work as written.
Surface time drops 16-27% on a 24-core desktop and 18-33% on a
4-core server; pregens finish 3-4% sooner. An oracle reruns vanilla
on 1 in 64 chunks and keeps vanilla blocks on any mismatch; a full
check of 240M blocks over five world types and three dimensions
found none. Steps aside when another mod changes the material rule
classes. Off:
/ferrite rules off or -Dferrite.rules=off; forceon:
-Dferrite.rules=on. Counters: /ferrite rules report. Oddsurface blocks in new chunks? Open an issue with seed, coordinates
and the report line.
- Ore kernel and rule compiler together.** Pregens finish about 8%
flat because workers already sit partly idle, but worker CPU drops
9%. Both oracles ran at 1 in 64 and found no mismatches.
### Fixed
- Biome validator counts exact ties as ties.** Over 2000 samples on
- Dead toggles now say so.**
/ferrite surface dispatch,
/ferrite aquifer rust, /ferrite aquifer parity and/ferrite biome route report inactive instead of success, and-Dferrite.bulkChunkDensity=true logs a boot warning.### Changed
- Targets Minecraft 26.3.** Built against 26.3 final with Fabric API
rewrote stays gated out at build time, which changes nothing at
runtime since every Rust chunkgen path was already default off.
Measured on 26.3: a 1021-zombie nether farm on a 4-core dedicated
server holds 20 TPS at 17 ms/tick mean over 3.5 min, and cramming
reads 10.3 ms against 16.4 ms with the Rust path off at 1106
zombies on a desktop dedicated server. Entity query and collider
oracles report zero mismatches on both.
### Fixed
- Declared loader minimum was wrong** (#17). 0.7.3 was built against
@Redirect at as anarray. MixinExtras 0.5.4, bundled in Loader 0.19.3 and 0.19.4, cannot
read that shape and the game crashed at launch with a
ClassCastException on the first Entity mixin. Loader 0.19.5 bundles
MixinExtras 0.5.5, which reads it. The manifest now requires
fabricloader >=0.19.5, so an older loader reports a dependencyerror instead of crashing.
### Added
- Chunkforce auto mode, default off.** Engages forced generation
/ferrite chunkforce auto on.Off by default: a 10-run interleaved bench at 90 blocks/s measured
8.1 missing chunks against vanilla's 0.9, worse in every pair.
/ferrite arrivalmonitor,/ferrite arrival benchand
/ferrite arrival suite.** Counts unloaded chunks inside each viewdistance every 5 s (~50 us/tick at view distance 10), plus an
automated flight bench.
-Pferrite.autobench=x,z,speed,seconds,runsruns a suite headless.
/ferrite probe stagesand/ferrite pregen order.** Per-stage
order (ring, scan, diag). Diagnostics only, default off; readings
are in docs/JOURNEY.md.
### Fixed
- Own nametag missing in F5 with nametag mods** (#15). A leftover
### Changed
- Entity query index and collider skip are now default on.** Opt-in
a 1022-zombie farm on a shared 4-core server going from 15.5 TPS to
a locked 20 TPS. Kill switches:
-Dferrite.entityquery.cache=falseand
-Dferrite.entityquery.colliderskip=false. With Lithiuminstalled the gain is smaller; the two coexist.
- Lower default-config footprint** (26.2.x dev build, JDK 25, jcmd;
- ~4.4 MB of off-heap physics buffers no longer allocate while
- Rust worker pool starts on first use instead of at the title
- Cramming reuses its spatial hash buckets across ticks.
- Two leaks closed: parity-capture lists cleared on server stop, and
- Monitors stop timing entities while reports are off, removing
24-core desktop; the saving is GC pressure and slow-CPU time.
- Fabric Loader 0.19.5** in the dev and CI build. Minimum stays
### Fixed
- Moonrise compatibility crash at boot** (#12). Moonrise replaces
targets of the two ThreadedLevelLightEngine diagnostic mixins and
hard-crashed the game at init. A mixin config plugin now detects
Moonrise and skips those two mixins; for now the
[light] monitorreports no data when Moonrise is installed, since Moonrise owns
lighting at that point. A Moonrise-aware light probe may come
later. Verified against Moonrise 1.1.0: boot, existing-world play,
and fresh world creation all clean.
### Changed
- Monitor logging defaults off on small heaps.** Max heap of 3 GB
with the periodic monitor reports silenced instead of paying ~5
log lines/sec. Counters still run;
/ferrite log monitors on or-Dferrite.log.monitors.on=true re-enables at any time. Normalheaps keep the old default.
- Dispatch and oracle telemetry respects the small-heap monitor
[cramming-dispatch], [physics-dispatch], [redstone-oracle],and
[chunkgen-features] periodic lines predate MonitorLog andbypassed it; on a Pi-class server they were most of the log file.
Now routed through MonitorLog, so heaps of 3 GB or less boot with
them silenced and
/ferrite log monitors on re-enables them.### Added
- Per-category log muting** (#14).
/ferrite log <category> off
physics-dispatch, cramming-dispatch,redstone-oracle, and every other bracket tag) without touchingthe rest;
on resumes it on the next report window and/ferrite log status lists what is muted. Counters keep runningwhile muted. The global
/ferrite log monitors switch isunchanged.
- Module toggles persist across restarts** (#13). Cramming, the
categories now save to
config/ferrite.properties on everytoggle and reload at boot. The file stores only deviations from
defaults, so it stays empty (absent, in fact) until something is
changed, and reverting a toggle removes its line. Diagnostic and
experiment flags stay session-only on purpose, as does prewarm,
since enabling it during boot breaks spawn loading.
- Entity spatial query index** (opt-in,
-Dferrite.entityquery.cache=true). Sections holding 32+ entitiesget a per-section bitset grid (4-block cells); box queries visit
only candidate entities in vanilla iteration order, so consumer
order, abort semantics, and mid-tick liveness are preserved
exactly. At a 1022-zombie farm: query cost 14.4-16.8 down to
10.3 ms/tick (34% cut), whole-server mspt 36-40 down to ~32.7,
zero oracle mismatches across 83,000+ sampled queries. Default
off pending broader soak;
-Dferrite.entityquery.oracle=<1-in-N>enables field validation.
-Dferrite.pregen.inflight=<n>** sets the pre-gen inflight cap
command still overrides). While adding it, the 200 default was
re-validated on a constrained 4-core / 2 GB profile: 42.8 chunks/s
vs 34.8 at cap 50, the same relationship as on desktop hardware,
so the default stands on weak CPUs too.
- One-line hardware stamp at boot**:
[hw] arch cores maxHeap jvm
they came from. Field reports from low-end hardware no longer
need follow-up questions about specs.
[entity-tick] misc-topbreakdown.** When the misc entity
top three entity types by time (total, worst single entity,
count). Prompted by a Raspberry Pi field report (PR #8) where
misc spiked to 47.8 ms with no way to tell which entity type was
responsible.
### Fixed
/ferrite biome validatecompared against the wrong dimension.**
which is the nether's, so the in-game command reported a screaming
0/2000 with all-nether answers while the boot-time validator was
fine. It now picks the captured source whose biome set contains
plains. The long-standing 1999/2000 was also diagnosed and closed:
an exact fitness tie between lush_caves and dripstone_caves at a
climate point real terrain never produces; vanilla and Ferrite
break the tie in different but equally valid orders. No in-game
effect.
### Changed
- Fat LTO release profile for the native library.** The workspace
lto = "fat" pluscodegen-units = 1 cuts the Windows dll from 2.1 MB to 794 KB(cross-crate dead code eliminated), with debuginfo stripped but
the symbol table kept so native crash reports stay readable.
Parity re-verified on the LTO build: noise 63/63, density 50/50
bit-exact, biome 1999/2000 (the documented tie). No
panic = "abort": unwinding is what lets a native panic fallback to vanilla instead of killing the server. In-game A/B at a
1022-zombie cramming farm: 38-41 ms/tick LTO vs 37-40 ms/tick old
profile, within noise; perf-neutral, kept for the size win.
- Removed the aarch64 to x86_64 native fallback** in the loader:
replaced an honest "not bundled for this platform" log with a
misleading "failed to load" one.
## [0.7.0-alpha] - 2026-07-27
### Known issues
- Biome parity reads 1999/2000 in the validator: the one miss is an
climate point outside the range real terrain produces (validator
samples wider than the game's climate space). Vanilla and Ferrite
break the tie in different but equally valid orders. No in-game
effect; real climate points match 100%.
- The deep-marker diagnostic walk registers no interior cache
path reads those fingerprints.
### Added
- Linux aarch64 native support** (PR #8, contributed by cwright814,
built by a new CI cross-compile job;
RustBridge picks it byos.arch at load time, and local Gradle builds on ARM hosts targetit automatically. Verified in the field: a full server on a 2GB
Pi 4B under OpenJ9 with memory to spare.
- Building-from-source section in the README**: toolchain
locally built jar bundles versus the CI release jars.
- Pre-gen now skips chunks that are already generated.** Before
live chunk holder, then stream-scans the region file for just the
chunk's Status field (no full deserialize). Re-running pre-gen over
existing terrain completes at thousands of chunks per second instead
of re-loading every chunk from disk; measured 289/289 skipped
sub-second on a fully generated area, with virgin-generation
throughput unchanged. Skip counts show in
/ferrite pregen status.### Fixed
- An unreadable region file no longer makes pre-gen skip chunks.**
region the reader cannot open), the chunk was treated as already
generated and silently left ungenerated. The scan now fails open:
it names the chunk in the log and generates it. Verified by holding
an exclusive lock on a region file during a 121-chunk run, which
produced 121 warnings and 121 generated chunks instead of 121 silent
skips. Chunks whose generation itself fails are logged too, rather
than counted as done.
- Malformed IntervalSelect nodes now stop the walker instead of
child counts disagree used to encode as a degenerate node, which
reads as valid bytecode and produces wrong cave terrain with no
error anywhere. The walker now rejects it and says what it saw.
Every IntervalSelect in the live 26.2 registry passes; density
parity stays 50/50 bit-exact at 2000 samples.
### Fixed
- Five latent redstone defects found in a full review of the AC
every later cascade in that world; removed wires reached the Rust BFS
kernel as phantom power-15 sources through a sign-truncation bug (this
path is default-on); an out-of-range power value could abort the whole
JVM via a release-mode panic across the JNI boundary (now hard-clamped
on both sides); the parity oracle produced guaranteed false positives
on experimental-redstone worlds (now skipped); and a single exception
permanently disabled the oracle and phase counters through stuck
thread-locals (they self-heal every tick now). None of these changed
measured performance; all validators stay bit-exact.
### Changed
- AC redstone lag machine restesting** The
/ferrite redstone ac on mid-choke recovered to a flat 20.00 TPS inabout 40 seconds, ~13x fewer cascades and ~6x cheaper gate updates on
the same build. README benchmark section carries the numbers.
- Chunkforce predicts flight direction.** With
/ferrite chunkforce on, the force-gen ring center now leads a moving player by up to 12
terrain behind them. Flight test on 26.1.2 (view distance 16,
simulation distance 12): 24,581 chunks forced in ~2.5 minutes at
~160 chunks/s with TPS 20.00 held; the generation front stays out
of sight at top creative-fly speed. Stationary players keep the old
radial behavior.
- Pregen inflight cap raised 50 to 200 (+25% throughput).** Four
cap 200 at 114-118 chunks/s; 400 added nothing. Tunable at runtime
with
/ferrite pregen inflight <n>.- Walkability cache: fill strategy fixed, then shelved after A/B.**
~4400 snapshots per 5 s). Session 6 replaced it with a lazy snapshot on
first miss, restricted to sections the PathNavigationRegion actually
backs, and grew the kind cache to 2-way set-associative (2048 sets x 2
ways, LRU). That fixed the cache itself: hit rate 71-94%, snapshots
24-57 per 5 s. An A/B with 294 chasing zombies then hit 84-87% and
still moved nav tick cost at most ~5%, inside noise, because vanilla already
fronts
getPathTypeFromState with a per-position PathTypeCache andour intercept only ever serves vanilla's misses. Shelved: code stays in
tree, default off, post-mortem in JOURNEY. Opt in with
-Dferrite.nav.cache=true, or -Pferrite.navCache=true /-Pferrite.navParity=true on runClient.### Added
[chunk-save]monitor.** TimesSerializableChunkData.copyOf(server
write (background encode) separately, one line per 5 swindow when saves happened. First measurements settled the chunk-save
port candidate: the palette bit-pack already runs on the background
executor in 26.1.x, and the tick thread pays only 0.3-0.5 ms/tick of
copyOf under sustained flight. Candidate closed; monitor stays.
### Fixed
- Workspace is clippy-clean.** All ~120 warnings cleared across the four
bind! codegen now emits From impls without the Copy.clone(), the api layer's Into impls flipped to From, slice paramsreplace
&Vec, and dead verbatim-port items carry explicit allows.Style lints that fight the vanilla ports (index loops, negated float
comparisons that keep vanilla NaN semantics, JNI argument counts) are
allowed at crate level in rust-mod with a justification comment.
Workspace resolver pinned to 2; the macros crate declares
proc-macro = true instead of crate-type.- Unsafe JNI buffer helpers hardened.**
get_i32_slice_mut
get_byte_slice_mut (redstone_queues_jni.rs)returned
&mut [_] from a shared &JByteBuffer, so nothing stopped acaller from holding two aliased mutable slices over one direct buffer.
Never a live bug (each is called once per JNI call), but the invariant
was unwritten. Both are now
unsafe fn with a # Safety contract(no-alias, capacity, alignment) and SAFETY notes at each call site. The
two
clippy::mut_from_ref errors are gone, and build.yml now runsclippy on the linux job so deny-level lints fail CI.
### Changed
- Walkability cache default off.**
-Dferrite.nav.cachenow defaults to
false. Session 5's first live counters showed the pre-fill box thrashingthe 512-slot direct-mapped cache (hit rate 1-17%, ~4400 snapshots per 5 s,
net regression in casual play). The default path stays vanilla until the
session 6 fill-strategy fix lands; set
-Dferrite.nav.cache=true to opt infor measurement.
### Added
- Walkability cache session 4.**
WalkNodeEvaluatorMixinintercepts
WalkNodeEvaluator.getPathTypeFromState(BlockGetter, BlockPos) at HEAD.For sections already snapshotted, returns
PathType directly from aJava-side direct-mapped section cache (512 slots, ~5-10 ns per lookup)
instead of calling
level.getBlockState followed by the 50-lineclassification chain. The Java-side cache is filled in
snapshotSectionalongside the Rust store and evicted on block-kind changes.
kindToPathType maps the unambiguous kinds: AIR/LADDER/SCAFFOLDING/CARPETto OPEN; OPAQUE_FULL/SLAB/STAIRS to BLOCKED; FENCE/WALL to FENCE;
TRAPDOOR to TRAPDOOR; WATER to WATER; LAVA to LAVA; LEAVES to LEAVES.
DOOR, FENCE_GATE, and OTHER fall through to vanilla (state-dependent).
encodeBlockKind now redirects MAGMA_BLOCK, HONEY_BLOCK, POWDER_SNOW,lit campfires, and LAVA_CAULDRON to KIND_OTHER to prevent false BLOCKED
returns for those blocks.
Parity correction:
LiquidBlock.isPathfindable(LAND) is true in 26.1.x(water blocks return
PathType.WATER, not BLOCKED). UpdatedkindToPathType and predictCategory accordingly.- Walkability cache session 5: A/B flags and hit-rate counters.**
-Dferrite.nav.cache (default true) gates the whole cache for cleanvanilla-baseline measurement runs;
-Dferrite.nav.parity (default false)gates the parity validator, which had been running per-node JNI and string
work on every ground-mob findPath after validation was already complete.
Hit/ambiguous/miss/snapshot counters in
NavigationCacheBridge feed a[nav-cache] line in the 5 s monitor report, so measurement runs reporthit rate alongside timing deltas.
### Fixed
- Nav-cache snapshot gate desync.** The section-snapshot gate in
PathFinderMixin checked the Rust store while the hot path reads theJava 512-slot kind cache. A slot collision or door eviction emptied the
Java slot while Rust still reported cached, so the section never refilled
and went permanently cold. The gate now checks the Java cache
(
hasJavaSection), which also drops the per-section JNI call from thepre-fill loop.
- Asymmetric door eviction.** Placing or removing a door evicted the Java
nav_cache.rs). Eviction is now symmetric: any kind-crossing changeevicts both stores; door open/close (DOOR to DOOR) is still filtered.
- Snapshot box could cache wrong-AIR sections.** The pre-fill bounding box
PathNavigationRegion actually backs, where theregion serves
EmptyLevelChunk (all AIR). Those sections were cached aspermanently wrong AIR data invisible to the parity gate, which only checks
in-region path nodes. The box is now clamped to region-backed chunks (new
PathNavigationRegionAccessor) and world build height, and nullgetChunkNow entries are skipped.Next: session 4 of the walkability cache — replace vanilla's per-node
getBlockState calls inside PathFinder with reads from the Rustsection store. The parity gate from 0.6.4-alpha gates that swap.
## [0.6.4-alpha] — 2026-05-17
### Added
- Dispatcher latency probe** (
/ferrite probe dispatcher on, default off).
worldgen and light executors. Measurement on fast-flight load: task body
is 4-10x the dispatcher's worst tail, so the dispatcher is not the
bottleneck. Probe stays in tree for re-measurement under other workloads.
- Walkability cache infrastructure (sessions 1-3).** Block-kind cache for
on block change), JNI surface, Java bridge with 18-category block classifier,
lazy section fill on path requests, and a parity gate that validates cache
predictions against vanilla's node evaluator. Parity result: 6 critical
mismatches out of hundreds of nodes checked per session (all structural
2-block-model limits). The performance swap (session 4) is the follow-on.
### Removed
- Dead
SurfaceValidator.onServerTickmethod and its fields (zero callers).
### Internal
- Threading audit (
docs/COMPATIBILITY.md): three-tier safety classification
Rust-side concurrency posture (zero unsafe statics, zero plain cells).
- Doc index (
docs/DOC_MAP.md) and various doc updates across JOURNEY,
### Added
- AC offer-based Rust kernel (
/ferrite redstone ac-rust on).
powerNetwork() loop in Rust: offer-based propagationwith flow-direction tracking, priority-queue ordered output.
Parity-clean (0 oracle mismatches sustained across Phase 3
validation). **~16% aggregate wire-cost reduction** vs the
existing relaxation kernel on heavy workloads.
### Notes
- Existing relaxation kernel (
RUST_BFS) stays in tree as fallback.
takes over if the AC path bails (overflow / native unavailable).
- Phase 3 depower fix:
runRustAcBatch()now calls
findPower(wire, true) after findExternalPower() to pulloutside-cascade wire contributions before serializing. Fixes
boundary wire power=0 mismatches the oracle surfaced during
initial validation (~5% mismatch rate before fix, 0 after).