Bind the surface rule tree to the world seed

The tree was parsed once, globally, and shared by every world -- so every
condition that needs the seed simply did not work. Compiling it per RandomState
fixes four of them at once.

noise_threshold sampled a per-column random draw and pretended it was
"minecraft:surface"; the other six noises it names were unsupported and returned
false. Each condition now holds its own seeded noise, sampled once per column
into a small cache the way vanilla's LazyXZCondition does. Powder snow, packed
ice and ice appear in the dump for the first time; calcite, swamp water windows
and gravel patches have their conditions back too.

vertical_gradient tapered through a per-column RNG shared with the other rules.
Vanilla rolls a positional random at the exact block, from a factory named by
the rule. More importantly the anchor decoder read only above_bottom and
discarded which kind of anchor it was, so the deepslate rule's absolute 0..8
collapsed onto y=-64 and **no deepslate existed anywhere in the world**. Anchors
now carry their kind and resolve against the real height bounds -- which also
retires a hardcoded 384 in y_above.

Two more stubs land with them: hole is surfaceDepth <= 0 rather than a constant
false, and steep reads the neighbouring column heights. steep needs the whole
chunk's heightmap, so the column pass is now two passes -- terrain and fluids
for all 256 columns, then surface rules -- which is the order vanilla uses
anyway (doFill, then buildSurface).

Deepslate was also missing from the block-ID table, and an unknown name resolved
to 0, which the caller read as "no block" and skipped. So even a correct rule
would have placed nothing. Unknown names are now a parse error, deepslate and
mud are in the table, and a rule that resolves to air genuinely places air --
the frozen-ocean surface asks for exactly that.

Below y=0 is now entirely deepslate, y=1..7 a scatter, above y=8 none.
This commit is contained in:
Master290 2026-07-27 02:25:09 +03:00
parent 1083e47211
commit c19e5f0e4f
9 changed files with 478 additions and 255 deletions

View file

@ -56,14 +56,17 @@ type OverworldDensity struct {
// rule tree runs.
Surface *SurfaceSampler
surfaceRule *SurfaceRuleSet
surfaceRuleErr error
prelim *levelCache
}
// SurfaceRule returns the overworld surface rule tree, loading it on first use.
// It does not depend on the seed. A nil rule (on error) is non-fatal: the
// generator falls back to its default surface heuristics.
func (od *OverworldDensity) SurfaceRule() (SurfaceRule, error) {
return LoadOverworldSurfaceRule()
// SurfaceRule returns the overworld surface rule set, compiled against this
// world's seed. A nil rule set (on error) is non-fatal: the generator falls
// back to its biome-blind surface heuristics.
func (od *OverworldDensity) SurfaceRule() (*SurfaceRuleSet, error) {
return od.surfaceRule, od.surfaceRuleErr
}
// LoadOverworldFinalDensity builds the overworld final_density function for the
@ -168,6 +171,12 @@ func LoadOverworldFinalDensity(seed int64) (*OverworldDensity, error) {
secondaryNoise: secondaryNoise,
positionalRand: l.rs.Positional(),
}
// The rule tree is seed-bound: its noise_threshold conditions sample seeded
// noises and its vertical_gradient rolls against a seeded positional
// factory. A failure here is reported but not fatal — the generator keeps
// going on the fallback heuristics rather than refusing to start.
od.surfaceRule, od.surfaceRuleErr = l.loadSurfaceRuleSet(od.MinY, od.Height)
return od, nil
}