- Concurrent replacement requests report busy before waiting for the manager lock
- Replacement staging reports localized phases and elapsed time
- Player progress messages use entity scheduling
- Pack copies flush completed files after traversal
- Replacement admission and progress tests cover scheduling and cleanup
- Language editor tests use a local catalog response
- Dune and Strata add dunes and layered bands
- Wood and Marble add warped grain and mineral veins
- Gabor and Scales add oriented waves and overlapping scallops
- Chladni and Kaleidoscope add nodal lines and mirrored motifs
- Menger Sponge and Circuit add recursive voids and connected traces
- New styles use the shared scale and full seeds
- Tests cover tile seams, volume variation, bounds, and octaves
- Existing worlds retain generated terrain across generator updates
- New terrain blends at generation boundaries
- Cold generation and Studio startup improvements are integrated
- Terrain sampling validates active generator bounds and noise
- Cold startup and hydrology changes integrate with terrain history
- Flat authoring worlds skip native features under the history write guards
- Native decoration checks retain recorded structure and terrain ownership
- Automatic build revisions replace manual source seals
- Flat authoring studios skip terrain and native structure preparation
- Normal Studio entry prepares the exact hydrology column asynchronously
- Hydrology prefetch admits the complete frontier before stale queued work
- Concurrent hydrology loads avoid cache monitor contention
- Cave density tasks drain failures before releasing chunk state
- Terrain and interpolation queries reuse exact bounded samples
- Pregeneration counts requested chunks directly from its bounds
- World-local pack epochs preserve generated chunks across restart-only updates
- Kernel catalogs and source seals retain executable generation behavior across jar changes
- Crash-safe ownership, boundary, registry, semantic, and mantle records survive activation changes
- New terrain blends lower surfaces and inverted ceilings against frozen historical boundaries
- Cave, object, structure, biome, and hydrology work respects activation ownership
- Find commands merge recorded facts with eligible active predictions for biomes, regions, structures, objects, and rivers
- Bukkit, Fabric, Forge, and NeoForge route generation through retained runtime scopes
- Shutdown ordering preserves class-loader resources until world and service cleanup finishes
- Tests cover activation chains, failure recovery, retained registries, bounded lookup, and transition geometry
- Seekable ZIP output removes redundant data descriptors without changing entry contents
- Compaction validates entry size and CRC before replacing the artifact
- Tests cover metadata preservation, deterministic output, corrupt payloads, and duplicate entries
- Every registrant is evaluated on load (IrisData.preprocessObject ->
IrisRegistrant.evaluateCompat) by a cached per-class reflective walker over the
annotated key fields; the verdict is stored on the registrant and pool sites filter
with isCompatExcluded(). Block keys resolve through the live registry, the legacy
rename table, IrisDimension.blockFallbacks and the entry backup, in that order.
- Unit of exclusion is the compat unit that composes the missing content (registrant
or object placement). A referencing unit drops the reference; an emptied required
pool excludes the container (CompatPools cascade through regions, biomes, dimension,
spawners, loot tables, jigsaw pools and structures). A cascade that reaches the
dimension is a blocking validation error and world creation is refused.
- Object placements are evaluated per object from the .iob palette header: an object
is excluded from a placement's pool unless a chance-1 type-replace (edit rule
matching the key) or a fallback covers the key. edit find lists and marker mark
lists are match-only. Missing palette keys stay MissingBlockState placeholders
(air everywhere, real air nativeHandle) so a chance-1 edit rule can still rewrite
them at placement time.
- Approved decisions: exclude the composing unit; console-only reporting; no version
fields on packs and no cross-version pre-check (authors build on their version,
older servers gate at load).
- IrisDimension.blockFallbacks (base key -> full block state) applies to every
palette, decorator, deposit, replace rule and object in the pack.
- IrisBlockData.backup is revived on every platform (it was dead on Bukkit because
B.getState returned air).
- The legacy rename table now lives in LegacyBlockRenames and is applied identically
on Bukkit and the mod loaders.
Operator surface
- Boot listing per pack after the validation lines (IrisWorldGeneratorResolver on
Bukkit, ModdedStartup on modded), grouped by key, capped at three subjects per key.
- Validation summary line on /iris pack validate and /iris pack status, findings
persisted in PackValidationResult through PackValidationCache.
- One summary line per world engine when its pack has findings.
- /iris pack compat [pack] (alias cp) on Bukkit and modded, uncapped, from the
persisted validation so no pack is reloaded; catalog keys in all 17 locales.
Verification on the branch
- Full build green in the .compatwt worktree; modded adapters compile.
- Golden hash unchanged on Paper 26.2, seed 1337, radius 8.
- Live boots on Paper 26.1.2 and 26.2: the stock Overworld lists 6 unavailable keys
on 26.1.2 and none on 26.2; the world creates and generates without the excluded
content on 26.1.2.
Integration on top of master (df34f328e..fc3a26dd9)
- Conflicts: PackValidator keeps both import sets (JSONObject for static object
validation plus the compat and loader types); RiverPolicyResolver.State.apply takes
the confinement scope and the pack data (confined rivers and gate-filtered river
biome selections); IrisRiverPolicy keeps shoreBiomeWidth and confined next to
compatBiomes.
- Static objects honour the gate. The per-object palette evaluation moved out of
IrisObjectPlacement into the shared IrisObjectCompat helper; IrisStaticObject
.evaluateCompat mirrors a one-object placement, records findings with subject type
"static object" and the object key (DROPPED for uncovered palette keys, EXCLUDED
for a missing edit replace palette), IrisStaticObjectLayer.compile skips gated
entries instead of failing, and PackValidator.gateContent asks every dimension for
its static objects (IrisDimension.evaluateStaticObjectsCompat) so the boot listing
is complete. CompatWalker treats IrisStaticObject like IrisObjectPlacement: never
descended, so a static object's edit palette excludes only that entry, never the
dimension. Covered by IrisStaticObjectCompatTest.
- The modded compat and cp subcommands run inside ModdedCommandTree.localized like
the rest of the pack tree and are documented in the modded help; the parity test
asserts both.
- Locale keys stay in core/src/main/resources/languages: master's on-demand download
reads those files from the repository and the build only keeps them out of the jar,
so the compat keys sit next to the debugdump keys in the same form.
- Placeholder audit of master's new code: the only native casts added are the
isFaceSturdy fallbacks in IrisSpeleothems and ModdedDecoratorHooks, both behind
B.isAir guards, and MissingBlockState.nativeHandle is the real air anyway;
IrisObjectTransforms only asks B.isAir; decorators resolve palettes through
IrisBlockData.getBlockData, which never yields a placeholder. No new guards needed.
- core/purity-allowlist.txt gains IrisSpeleothems: 5053a644c split the Bukkit
isFaceSturdy fallback out of the already allowlisted DecoratorCore without moving
the allowlist entry, so master's own build failed the Bukkit purity ratchet.
- CompatTestPlatform states now answer isAir, isSolid, isFluid and materialKey like a
real adapter: B caches the first registries it sees per JVM, so an interned mock
air that never said isAir leaked into the core.compat tests once the static layer
ran under that platform.
Gate commands on the merged tree
- ./gradlew build (default VolmLib composite, ../VolmLib at 7b8029e): BUILD SUCCESSFUL;
core 3499 (8 skipped) tests, bukkit plugin 343 tests, nms v26_2_R1 255 (1 skipped) tests,
probe 78 tests, buildSrc 10 tests, 0 failures.
- cd adapters/fabric && ../../gradlew compileJava: BUILD SUCCESSFUL (:compileJava executed)
- cd adapters/forge && ../../gradlew compileJava: BUILD SUCCESSFUL (:compileJava executed)
- cd adapters/neoforge && ../../gradlew compileJava: BUILD SUCCESSFUL (:compileJava executed)
- cd adapters/forge && ../../gradlew test --tests IrisModdedCommandParityTest --tests
ModdedLanguageCommandsTest: BUILD SUCCESSFUL, 11 tests, 0 failures.
/iris language opens a locale menu and sets the language for the caller
or, for a gamemaster, the server default; /iris debugdump writes a
diagnostic report and optionally uploads it. Neither needs iris.all, so
a plain player can reach them. Every modded command now executes inside
a language audience scope, and the Bukkit dispatcher opens one around
execution, tab completion and help. Permissions, message keys and the
shipped locales follow.
Locales are fetched from the Iris repository at runtime into
languages/downloaded and cached against the revision the jar was built
from; the artifact ships only a manifest of the available locales.
IrisLanguage gains a player language service, per-audience snapshots and
an in-game message editor that writes locale overrides. The Bukkit
artifact verifier now fails when any locale file is embedded, and the
modded shadow jars drop the Bukkit-only switcher and editor classes.
Dripstone and sulfur spikes are placed as oriented, thickness-graded
columns by the surface and ceiling decorators through a shared
IrisSpeleothems helper, replacing the ad hoc dripstone fixups, and the
stacking path is unified for both directions. The perfection modifier
renormalizes a spike whose support a pool replaced, dropping the
unsupported run and re-tipping what remains. Surface sturdiness is asked
per face now, so a downward spike tests the ceiling it hangs from.
A height or biome query from the tick thread no longer blocks on a cold
hydrology plan: it answers from the natural streams and asks the
planning pool for the tiles meanwhile. A new world prefetches the tiles
around spawn when the generator is injected, and the hydrology pool gets
full parallelism instead of a third. Also stops the shared corner-bounds
cache from pinning a non-finite sample and fences its two stamp reads.
Surface rivers gain a drowned inlet at the mouth, a spring pond and an
inland terminal pond, a configurable erosion profile (thalweg fraction,
blend curve, bed noise, smoothing) and a per-area shore biome width;
banks lose freeboard and inset for a single sink. Underground courses
accept tributaries, coastal grottos can be admitted as standalone sea
caves, and a region or biome can confine the rivers born in it. Tile
planning forks its ring work through HydrologyForkJoin. Pack validation,
the schema, the probes and the tests follow.
Vanilla structure and feature placement now runs through the bounded
worldgen access with a protected-position predicate, so a jigsaw piece
or an imported feature can read a static object's blocks but never
overwrite them, and never schedules a tick or block entity there.
Dimensions without static objects keep the plain level as before.
A dimension can now list staticObjects: an object key, a world position, a
fixed rotation and scale, block edits and an optional bore or smart bore.
The layer compiles once per dimension and stamps its blocks into every
newly generated chunk it covers, later entries winning overlaps. Pack
validation, the schema, the export closure and the object transforms
(origin-anchored scaling, tile rotation) cover the new fields.
- Force-load every registrant and placement during pack validation and persist the findings with the Minecraft version in the validation cache
- Turn an excluded dimension into a blocking validation error and suppress content-key warnings the gate already reported
- Print the capped per-key listing after the validation lines on Bukkit and modded, ordered excluded, dropped, substituted
- Log one summary line per world engine from the published validation result
- Add /iris pack compat on Bukkit and modded with help and locale keys for every shipped language
- Filter excluded biomes, regions, spawners, loot tables, jigsaw pieces and structures out of every pool through CompatPools and cascade empty pools to the container
- Evaluate each object placement per object from the .iob palette header, honouring chance-1 edit rules and reporting every missing palette key
- Treat edit find lists and marker mark lists as match-only
- Exclude jigsaw pieces on missing palette keys, drop them from pools and exclude structures whose start pool is gone
- Drop custom biome spawns whose entity is missing before the datapack JSON is written
- Skip excluded placements in the object, floating object, tree and locator paths while keeping the per-chunk RNG draws unchanged
- Keep the engine focus biome and region null when the gate excluded them
- Add the ContentGate, CompatWalker, PackCompatReport and finding types under core.compat
- Resolve block keys through the live registry, the legacy rename table, dimension blockFallbacks and the entry backup on every platform
- Move the legacy rename table into LegacyBlockRenames so Bukkit and the mod loaders apply it identically
- Evaluate every registrant on load from IrisData.preprocessObject and store the verdict on IrisRegistrant
- Add IrisDimension.blockFallbacks as a plain object property
- Keep missing palette keys as MissingBlockState placeholders so a chance-1 edit rule can still rewrite them
- Guard the modded rotator, merger and face fixer against non-modded states
- Source the legacy potion effect aliases from the gate so the Bukkit-typed table stays unloaded on modded
A pregeneration now generates in multicore mode: the matter stage runs
on the calling thread so its mantle window fans out across the burst
pool while biome and terrain run alongside, components are claimed
through the in-flight map so overlapping windows join instead of
blocking, and each region's chunks are ordered in stride-4 lattices so
consecutive chunks never share a window. Hydrology tiles for the whole
area are planned ahead nearest the centre first on a dedicated planning
pool, and the ring around every needed tile stays planned as the front
moves. Height bounds at grid corners are shared between threads and
from planning into generation, the sole engine of a pack is answered
from a snapshot instead of the registry lock, composed chunk keys are
mixed, cached tiles are answered in place, the burst pool grows to two
workers per core for the run, and large chunk-worker pools admit up to
256 requests.
Planning failures used to escape into the chunk system and crash the
server. The tile cache now catches them, logs the failure in full and
publishes an empty tile so terrain keeps generating. A natural height
that is not finite is no longer memoized, and its error names the
region, biome, fluid height, overlay, every interpolator's bounds and
generator heights, and the cached and fresh values for the column.
VolmLib master added remote language catalogs and TOML language tooling.
The TOML language editor and parser depend on the excluded config
package and nothing in Iris uses them, so they are excluded too, and the
artifact size budget rises to 7.5 MB for the shipped localization
classes.
Brings master's Sentry removal, bound platform classification, Vision
entity toggle, release version and generation context binding onto the
rivers rework while keeping the hydrology system master had removed.
Hydrology files master deleted are restored from the branch, master's
non-hydrology edits are kept, and the modded adapter builds drop the
Sentry dependency with master.
Also removes the ridge_tunnel find selector: no course publishes ridge
bores any more, so it could never return a result.
- routing.tributaries lets an outlet keep extra surface courses; each is cut where its centerline first meets the stem and joined with a short connector
- A tributary must keep half the minimum course length and arrive at or above the stem's water
- Surface shape rejections carry the failing rule as their detail
- hydrology.surfacePools defines bowls filled with their own fluid; riverPolicy.surfacePools opts areas in
- Pools are independent SURFACE_POOL courses with one STANDING_POOL segment, shaped by the erosion field, kept clear of rivers and off slopes
- Locators accept the pool selector and pool ids; validator, renderer, probes and docs follow
Valley solver rejections carry the failing measure (required cut, exposed stations, missing head) through the planner diagnostics; the transect probe prints them.
- Lattice edges that rise more than the cut the valley solver accepts are not surface drainage
- Ocean reachability uses the same rule, so ground behind a ridge gets inland grotto outlets instead of an impossible mouth
- Transect probe renders a tile diagnostics map with the drainage graph and rejected candidates
Refined routes now pay for ground that falls away beside them, keeping channels on valley floors instead of hillside traverses the incision cap rejects.
- Only the centerline cell of the first station carries the source feature
- Headwater and styled geometry tests expect the spring pool
- Transect table starts with the first 24 stations
- channel.springWidthRatio / springLength open every surface river in a pool that shrinks where the ground falls away
- Banks settings keep structural invariants only; authoring bounds stay in the validator
- Feature searches walk tiles outward and stop early; find and goto report progress per ring
- surface.bed: falling blocks under and beside river water are replaced by a padding palette unless allowed
- Ring order helper for bounded feature searches
- Geometry pins became relational properties; tests for deleted mechanisms removed
- Every owned channel water column is published to the mantle so steps get fluid updates
- Upper beds at head steps reach the lower head so the water stays one body
- Ocean aprons are measured from the first ocean station and bounded laterally
- Dry cave headroom uses the flooded cave biome
- Ocean integrity fires on land columns near a mouth; shallow incision and bank continuity are gates
- Probe-local centerline and turn helpers replaced by production geometry
- v1 checks for ridge bores, endpoint ramps, terminal caps, transition recovery and pool lengths removed
- Head ring now covers every cell that can sit beside water
- Blend widths are smoothed along the course; the bed is a broad bowl
- Diagonal shore cells keep the lip; cross-section rows belong to their own station
- Transect probe prints coordinates and numeric section tables
- PackHydrologyValidator rewritten for surface.channel/banks/flow/mouths, routing weights and bankBiomes
- 76 unreferenced private members deleted from HydrologyPlanner and HydrologyFootprintCompiler
- riverTransectProbe renders each surface course as a plan and five cross-sections
- Summary gates ocean writes and spilling channel cells
- Grading columns now report the bank biome
- Skip nib, pothole, wall and slab post passes on river footprints and their collar
- Keep automatic object scatter off the channel and shore band
- Expose deeper biome layers on eroded banks and use the bank biome on the blend band
- Give dry cave headroom the flooded cave biome
- Build surface courses through the valley solver and segment labeler
- Compile surface footprints through the erosion field and index them for cave containment
- Drop the contained-surface fallback and the post-hoc surface rejection passes
- Choose the river profile at the source
- Add the surface package with centerline densification, channel profile, valley head solver and erosion field
- Derive the head from the bank ring outside the channel so every wet cell is contained by construction
- Stamp one continuous cross-section with a cut-scaled blend and a freeboard lip, filtered at the ocean
- Replace hydraulics, ridge tunnel, branching and inset ranges with channel, banks, flow and mouth configs
- Add routing cost weights, underground mouth leveling, bank biomes and bank multiplier
- Map the new schema into planner settings and derive refinement spacing
- Replace the river package with the hydrology planner, footprint compiler, cave containment and runtime
- Add the hydrology pack schema, validator, probes and tests
- Wire hydrology into IrisComplex, actuators, mantle, Vision and commands
- Hide living-entity markers by default while retaining player markers.
- Add an Entities control beside Grid and stop polling hidden entity snapshots.
- Localize the new control and status messages across all bundled languages.
- God i gate these kinds of commit messages
- Resolve first-generation engine binding from authoritative published level context.
- Validate generator identity and recover canonical Overworld binding from stale snapshots.
- Add shared loader regression tests and their Mockito test dependency.
- Publish and remove dynamic levels through host lifecycle methods when available.
- Attach hybrid host level data before engine binding and publication.
- Preserve native loader behavior when host extensions are absent.
- Select Bukkit scheduling only when the bound platform identifies as Bukkit.
- Route hybrid Youer hosts through the modded scheduler despite Bukkit classes on the classpath.
- Add regression coverage for Bukkit and NeoForge platform identities.
- Remove automatic error reporting and server-identifying attachments.
- Remove Sentry dependencies, release tooling, settings, and test commands.
- Keep failures operator-visible through local stack traces and logging.
Iris 4.0 unification: pure-JVM core + SPI with Bukkit/Fabric/Forge/NeoForge
adapters, art.arcane package layout, and the Gradle Groovy build.
Conflicts resolved in favour of unification for every path. The six commits
master gained after the fork point are all superseded there:
- CraftEngine data provider (#1227): present, ported to CraftEngine 26.7.3
- BlockProperty ofDouble/ofLong: present
- 1.21.11 ambient-particle and ultrawarm dripstone datapack fixes: present
- libs.versions.toml craftengine entries and the momirealms repo: present
- build.gradle.kts / core/build.gradle.kts: replaced by the .gradle build
Merged tree is identical to origin/unification.