Compatibility
Minecraft: Java Edition
26.1.x
Platforms
Supported environments
Server-side
Client and server
Tags
Creators
Details
Licensed MIT
Published 3 weeks ago
Changelog
StateStream Changelog
1.0.1 — Multi-loader support, critical fixes, major performance pass
Loader support
- NEW: Native NeoForge build (
statestream-neoforge-1.0.1.jar) targeting NeoForge26.1.2.65-beta. - NEW: Native Quilt support via
quilt.mod.json(also runs under Quilt's Fabric compatibility layer). - Refactored project into a loader-agnostic core (
com.statestream.*) with separate entrypoints per loader (StateStreamFabric,StateStreamNeoForge). - Three distinct jars now shipped: Fabric, Quilt, NeoForge.
Critical bug fixes
- Fixed silent data corruption when Y > 255. The previous position encoding masked Y to 8 bits, which collided for any block above Y=255 (Overworld extends to Y=320). The registry is now split per-section using section-local Y (4 bits) — collisions are mathematically impossible. New test
allEncodingsUniqueWithinSectionverifies every (x, z, ly) tuple produces a unique encoding. - Fixed registry corruption on every block placement and break.
onBlockChangedwas always receivingnullfor the old BlockState, which caused:- Duplicate entries when replacing a tickable block with another tickable block
- Phantom entries (never removed) when a tickable block was destroyed
- Tick loop wasting cycles on positions that no longer held tickable blocks
- Old state is now captured at HEAD via a ThreadLocal and consumed at RETURN.
- Added the
/statestream enable <module>and/statestream disable <module>commands that the Modrinth description promised but didn't actually exist. Includes tab-completion for module names.
New feature: per-section tickable index with bitmap skip
The biggest performance win in this release. Each loaded chunk now keeps:
- One sorted
short[]per section containing only tickable positions - A 64-bit
nonEmptyMaskwhere bit s is set iff section s has at least one tickable block - The total tickable count for stats
The random-tick loop walks the mask using Long.numberOfTrailingZeros + mask & (mask-1) — empty sections are skipped at the cost of a single CPU instruction. On a typical Overworld chunk with tickables in only 3 of 24 sections, this is roughly an 8× reduction in section iterations per tick.
Probability parity with vanilla is preserved exactly: each section still rolls randomTickSpeed picks of nextInt(4096) and a tickable block still has P=1/4096 per roll.
Performance fixes
AtomicLongcache counters replaced withLongAdder— eliminatesLOCK CMPXCHGcontention on the per-call hot path.- Block state IDs are now cached on the BlockState itself via a
@Uniquemixin field onBlockBehaviour$BlockStateBase. First read pays aReference2IntOpenHashMap.getInt(); every subsequent read is a single field load. Without this, the cache was doing a hashmap lookup to save a field lookup — a net loss. onBlockChangedno longer allocates aHashSetper mutation. It now does CAS-based sorted-array binary insertion/removal. Lock-free, single allocation for the new array only.onChunkLoaditeratesLevelChunkSectionarrays directly instead of callingchunk.getBlockState()~98 000 times viaBlockPos.betweenClosed. Empty sections are skipped at O(1).UpdateCuller.shouldSuppressnow has an O(1) empty-set early exit and an O(1)Blockreference check (couldSuppressSource) that skips Identifier allocation entirely when the source block isn't involved in any pair. Internal storage switched fromSet<String>toMap<Identifier, Set<Identifier>>— no moretoString()+ concatenation per call.- Tick loop now uses a single reusable
BlockPos.MutableBlockPosinstead of allocating oneBlockPosper ticked block per tick. - Cache verification moved from per-call to one-shot at
populate(). The hot path no longer pays for verification overhead. Long2ObjectOpenHashMapcandidacy preserved viaConcurrentHashMap<Long, ChunkEntry>— Long boxes are short-lived and escape-analysis-friendly.
Code quality
- JSON parsing in
UpdateCullernow uses Gson (with the hand-rolled string parser kept as a fallback for hostile input). - Config paths resolved via
FabricLoader.getConfigDir()/FMLPaths.CONFIGDIR.get()on the respective loader, with astatestream.config.dirsystem property as the loader-agnostic bridge. - Mod version now read via Fabric's / NeoForge's
ModContainerAPI instead of a hand-rolled JSON parser. fabric.mod.jsonenvironment changed from"*"to"server"— the mod is no longer required on the client.
Tests
- Updated for the new section-local encoding API (
encodePos(x, z, ly)+ matchingdecodeX/Z/LocalY). - New test
allEncodingsUniqueWithinSectionproving the Y-collision bug cannot recur. - New test
nonEmptyMaskMatchesSectionContentscovering the bitmap-walk semantics. - Counter assertions updated from
AtomicLong.get()toLongAdder.sum().
Internal
- New
BlockStateIdHolderinterface implemented on every BlockState viaBlockStateBaseMixin. - New
ReactiveTickRegistry.ChunkEntryimmutable struct (per-section arrays + bitmap + count + minY). - New
com.statestream.fabricpackage with the Fabric-specific entrypoint. - New
neoforge/Gradle subproject usingnet.neoforged.moddev2.0.141.
1.0.0 — Initial release
- JIT BlockState Inlining (
BlockStateCache) - Update Culling (
UpdateCuller) - Reactive Block Ticking (
ReactiveTickRegistry) - Per-module fallback handler — any unexpected error disables the offending module and restores vanilla behavior
- TOML config with independent toggles for each module
/statestream stats,status,reload,benchmarkcommands- Fabric loader for MC 26.1.2
Dependencies
Files
statestream-fabric-1.0.1.jar(41.12 KiB) Primary
Metadata
Release channel
ReleaseVersion number
1.0.1Loaders
Fabric Forge Quilt
Game versions
26.1–26.1.2Environment
Client and server, optional on client


