Commit graph

82 commits

Author SHA1 Message Date
edf624e77b docs: describe region replay parity status 2026-08-25 10:24:20 +03:00
c4be38acdd world: publish decorated region batches 2026-08-25 10:24:19 +03:00
d8b5d0f79b world: replay datapack features across regions 2026-08-25 10:24:19 +03:00
f1d1d109f4 worldgen: parse configured feature definitions 2026-08-25 10:24:19 +03:00
fd32c65da9 worldgen: add vanilla biome search and decoration RNG 2026-08-25 10:24:19 +03:00
736f387b41 chore: ignore local development artifacts 2026-08-25 10:24:19 +03:00
40bdd1b755
Update README.md 2026-08-21 22:21:51 +03:00
b8c57be2f7 Add vanilla base batch generator 2026-08-18 01:43:55 +03:00
9a38622747 Test batch cache persistence 2026-08-18 01:34:36 +03:00
d804dc11f1 Coalesce overlapping chunk batches 2026-08-18 01:24:20 +03:00
3f0f1f70b9 Publish generated chunk batches 2026-08-18 00:58:12 +03:00
cdee55b613 Document current worldgen status 2026-08-18 00:46:09 +03:00
641da0e078 Bound placed ore verification 2026-08-17 22:52:59 +03:00
56a1a5f8d6 Bound tree stage verification 2026-08-17 22:28:55 +03:00
a2ee6f6a8f Measure single-chunk ore transition 2026-08-17 22:12:49 +03:00
798016b055 Preserve lazy feature placement RNG 2026-08-17 18:29:10 +03:00
aeb7e4d2c2 Keep race world tests within timeout 2026-08-17 18:05:13 +03:00
d2e89a03fe Capture feature seed vectors 2026-08-17 14:39:18 +03:00
2cf6ab4775 Preserve trapezoid height plateaus 2026-08-17 14:16:34 +03:00
4fa0a5e554 Match very biased height sampling 2026-08-17 10:56:05 +03:00
8e73993adf Measure per-feature ore parity 2026-08-17 10:18:18 +03:00
a24ffb98f3 Compare ore feature biome orders 2026-08-17 01:47:10 +03:00
51b4891f81 Diagnose ore feature seed indices 2026-08-17 01:37:40 +03:00
cb60050801 Match vanilla ore sphere math 2026-08-17 01:14:24 +03:00
c68c5c2744 Match vanilla ore block traversal 2026-08-17 00:45:32 +03:00
400e76382a Report fixture ore schedules 2026-08-11 14:01:40 +03:00
e2f496fafc Measure cross-chunk ore replay 2026-08-11 13:50:14 +03:00
9ffd2b19ba Place scheduled ores across regions 2026-08-11 13:35:47 +03:00
d01e576dcc Schedule features from mutable regions 2026-08-11 13:23:45 +03:00
4ff62d7c96 Evaluate world-aware block predicates 2026-08-11 13:13:02 +03:00
1a921434d9 Model mutable feature regions 2026-08-11 13:01:52 +03:00
ea66091f85 Schedule source chunk features globally 2026-08-11 12:47:59 +03:00
11440f556c Execute world-aware placement modifiers 2026-08-11 12:33:35 +03:00
053c5bec6a Execute placement modifiers in order 2026-08-11 09:55:50 +03:00
674d0192c9 Match vanilla placement height sampling 2026-08-11 09:42:11 +03:00
e9fe526300 Pin feature ordering to vanilla runtime 2026-08-11 09:29:17 +03:00
a73b689449 Use world coordinates for ore placement 2026-08-11 01:15:34 +03:00
a23650fc0b Match vanilla ore ellipsoid geometry 2026-08-11 00:56:53 +03:00
b11e6f14cb Pin biome fitness against vanilla runtime 2026-08-10 23:59:22 +03:00
be8f030698 Commit exhaustive vanilla worldgen fixture 2026-08-10 23:54:04 +03:00
064f0f1cca Drive basic trees from biome feature stages 2026-08-10 23:36:12 +03:00
2bfa3155f3 Place biome-driven water and lava springs 2026-08-10 23:29:20 +03:00
dd2ea56850 Load vanilla feature stages and place ores 2026-08-10 23:20:43 +03:00
28c11e2fc2 Bound connection outbound queues 2026-08-10 23:00:20 +03:00
ca019756ec Add vanilla parity harness and harden server boundaries 2026-08-10 22:54:16 +03:00
1924cb5591
Add GNU AGPL v3 license
Added the GNU Affero General Public License version 3 to the project.
2026-08-10 22:25:42 +03:00
Master290
c6185d88c8 Configured carvers: caves and canyons
The router's noise caves are one kind of cave. The other kind -- the long
winding tunnels with rooms and side branches, and the ravines that cut down
through the terrain -- is walked, step by step, by a random source, and none of
it existed.

The shape of the work is unusual enough to state plainly. To carve one chunk,
vanilla replays every carver seeded in the 17x17 chunks around it and keeps only
what lands inside, so the same tunnel is walked up to 289 times across a world.
That redundancy is the point: it is what lets a chunk be carved without
generating its neighbours, which is the only way carving fits a generator that
produces one chunk at a time. A carve-once-write-into-neighbours design would be
cheaper and would not reproduce vanilla's mask and ordering.

Two primitives had to be right before any of it could be, and both are pinned
against values captured from the jar:

  * setLargeFeatureSeed, which decides which chunks start a cave. It combines
    its two products with XOR; setDecorationSeed, which it otherwise resembles,
    uses addition and forces the low bit. Getting them the wrong way round moves
    every tunnel in the world and nothing complains.
  * Mth.sin and Mth.cos, which are a 65536-entry lookup table and not libm.
    Mth.sin(-1.0) is -0.8414514 against Math.sin's -0.8414709848078965, and a
    tunnel that walks by adding cos(yaw) a hundred times ends up somewhere else
    entirely if that difference is smoothed away.

Carving lands between the surface pass and decoration, where vanilla puts it,
and both neighbours matter: the surface rules must already have placed grass for
a cave mouth to be retextured, and decoration must come after so nothing is
planted over a hole. The heights decoration plants against are recomputed
afterwards, which is why vanilla re-primes its heightmaps at the start of the
feature step.

The configs are extracted from the jar rather than transcribed, along with the
flattened #minecraft:overworld_carver_replaceables tag, so the probabilities and
Y ranges are data. Open volume below y=60 rises 28% over sixteen sampled chunks,
tunnels cut at or below y=-56 fill with lava rather than air (869 blocks, no
air), and the cost is inside the noise floor of the density pass.
2026-07-27 03:58:56 +03:00
Master290
0f76058db6 Ore veins from the noise router
The router has carried vein_toggle, vein_ridged and vein_gap since the whole of
it was parsed, and nothing read them. So the copper and iron mega-veins -- the
long branching sheets that run through the deepslate and the copper band, not
the small scattered ore blobs -- did not exist.

OreVeinifier is not a decoration feature. It is the second entry of the same
MaterialRuleList the aquifer heads: the aquifer answers first, and only where it
says the position is solid rock does the veinifier get a turn at what would
otherwise be plain stone. That is why a vein never opens into a cave, and it is
why this lands in the density pass rather than in decorate.

Three random draws per position, in a fixed order, from a factory hashed off
"minecraft:ore": solidness, then richness, then the rare raw-ore block.
Reordering them or hoisting the vein_gap compute above the second draw would
change every vein in the world, so the code follows the bytecode's order rather
than the one that reads better.

The Y windows are the sharp edge worth knowing about: a vein's type comes from
the sign of the veininess noise, but its window comes from the type, so
veininess <= 0 anywhere outside -60..-8 is simply not a vein. Over 49 sampled
chunks copper lands in 0..49 and iron in -57..-8, and raw ore comes out at 1.9%
of ore blocks against vanilla's 2%.
2026-07-27 03:48:48 +03:00
Master290
e0fdddd887 Write chunk NBT vanilla reads: flat root, byte section Y, four-bit block palettes
Our region files were not Anvil. Three things stood between them and vanilla's
deserializer, and each is enough on its own:

  * everything was nested under a "Level" compound. Chunk data lived there until
    1.18; SerializableChunkData builds a flat root and never looks for the key.
  * a section's Y was an Int. Vanilla writes putByte and reads getByteOr, so
    every section of ours decodes as index 0 and overwrites the one before it.
  * block palettes were packed at ceil(log2(size)) bits. Strategy's tableswitch
    routes bit counts 1 through 4 to the same four-bit configuration, so a
    palette of 2..16 states is four bits wide on disk. Ours were one to three,
    which makes the long array a quarter of the length vanilla computes, and
    SimpleBitStorage rejects the section outright rather than misreading it.

Biome containers were already right: Strategy has no such floor for them, and a
Global configuration above three bits still stores palette indices, just at its
own width. The suspicion that biomes collapsed on save/load was unfounded --
what let it stand is that every round-trip test in this package set blocks and
asserted blocks, so nothing proved biomes survived. They do now, per cell, for
every palette width a section can hold.

Verified against vanilla rather than against our reading of it:
tools/VanillaChunkFormatCheck.java opens a region file we wrote using vanilla's
RegionFile, NbtIo, Strategy and SimpleBitStorage. Sixteen generated chunks pass.
Reverting either the Y type or the palette floor makes it fail with vanilla's
own message -- "Invalid length given for storage, got: 64 but expected: 256" --
so the check can fail, which is the only reason to trust it passing.

Reading a world the official server generated is what the surface-height parity
capture in CLAUDE.md has always needed, and this is half of it.
2026-07-27 03:44:48 +03:00
Master290
113a59e365 Resolve block names to the default state, not the first one
blocks.json lists a block's states in getPossibleStates() order -- the property
cartesian product -- and separately marks which one is the default. The parser
declared only id and properties, so the default flag was dropped on the floor
and nameToStateID returned states[0]. Those differ for 642 of the 1168 blocks.

What that produced, measured rather than guessed: every redstone vein in the
world was lit=true and glowing, every sunflower was placed as its own top half
with nothing under it, and oak stairs came out upside down and waterlogged. It
also quietly disagreed with the StateGrass, StateOakLog and StateOakLeaf
constants next door in encode.go, which are the real defaults.

Now it starts from the default state and applies the properties it recognises,
keeping the default's value for an unknown key or an illegal value -- which is
what vanilla does when it reads a palette entry. That matters on the disk path:
a chunk written with a property we no longer know used to decode to a random
corner state instead of something sane.

The signature grows an ok result, because air was doing double duty as both a
real block and "no such name".

Separately, blockPaletteEntry filled the Properties compound by ranging a Go
map. nbt.Compound preserves insertion order precisely so encoding is
deterministic, so saving one chunk twice produced different region-file bytes
for every multi-property block. Keys are sorted now.
2026-07-27 03:35:39 +03:00