RegionIO/internal/worldgen/randomstate.go
Master290 1083e47211 Restore the subsurface layers: real above_preliminary_surface and surface depth
Every land column was one block of grass sitting straight on stone. No dirt
under grass, no sandstone under sand, nothing. Two stubs did it together:

above_preliminary_surface compared blockY against the column's actual top block,
so of every position in the column exactly one passed -- and the entire
biome-specific half of the surface rule tree hangs under that condition.
Vanilla compares against a minimum surface level: the preliminary surface level
sampled at the four corners of the 16-block cell, bilinearly interpolated, plus
the surface depth less 8. That is about twenty blocks of reach on ordinary
terrain, which is what the biome subtree is written against.

Surface depth was hardcoded to 0. Vanilla is surfaceNoise*2.75 + 3 with a
per-column jitter, so it comes out around three; it sets how thick the band is
and feeds every add_surface_depth term in the tree. Zero collapsed them all.

Also samples surface_secondary, so stone_depth's secondary_depth_range widens
its band instead of being parsed and dropped.

Grass columns now read grass, two to four dirt, stone -- the histogram over 256
columns is {2: 223, 3: 33}, against vanilla's 2..4. gendump prints it and fails
if the band collapses again; TestGrassColumnsHaveDirt guards it in the suite.
2026-07-27 02:15:14 +03:00

55 lines
2.4 KiB
Go

package worldgen
// RandomState seeds all worldgen noises from the world seed, mirroring the
// vanilla RandomState: a positional factory derived from XoroshiroRandomSource
// (overworld uses the non-legacy source), with each noise seeded by the MD5
// hash of its resource location.
type RandomState struct {
factory PositionalRandomFactory
noises map[string]*NormalNoise
// aquifer and ore are the positional factories vanilla derives up front for
// the aquifer cell centres and the ore-vein placement, each a forked
// positional factory seeded from a named hash of the root factory.
aquifer PositionalRandomFactory
ore PositionalRandomFactory
}
// NewRandomState builds the seeding context for the given world seed.
func NewRandomState(seed int64) *RandomState {
root := NewXoroshiro(seed).ForkPositional()
return &RandomState{
factory: root,
noises: make(map[string]*NormalNoise),
aquifer: root.FromHashOf("minecraft:aquifer").ForkPositional(),
ore: root.FromHashOf("minecraft:ore").ForkPositional(),
}
}
// AquiferRandom returns the positional factory the aquifer uses to place its
// cell centres (RandomState.aquiferRandom).
func (rs *RandomState) AquiferRandom() PositionalRandomFactory { return rs.aquifer }
// OreRandom returns the positional factory the ore-vein placement uses
// (RandomState.oreRandom).
func (rs *RandomState) OreRandom() PositionalRandomFactory { return rs.ore }
// Positional returns the root positional factory (RandomState.random). It is
// what SurfaceSystem jitters the surface depth with and what the surface rules'
// vertical_gradient and clay bands derive their own factories from.
func (rs *RandomState) Positional() PositionalRandomFactory { return rs.factory }
// Noise returns the NormalNoise for the named noise parameters, seeded as
// NormalNoise.create(factory.fromHashOf(name), params) and cached.
func (rs *RandomState) Noise(name string, firstOctave int, amplitudes []float64) *NormalNoise {
if n, ok := rs.noises[name]; ok {
return n
}
n := NewNormalNoise(rs.factory.FromHashOf(name), firstOctave, amplitudes)
rs.noises[name] = n
return n
}
// BlendedNoise builds the terrain blended noise, seeded from "minecraft:terrain".
func (rs *RandomState) BlendedNoise(xzScale, yScale, xzFactor, yFactor, smear float64) *BlendedNoise {
return NewBlendedNoise(rs.factory.FromHashOf("minecraft:terrain"), xzScale, yScale, xzFactor, yFactor, smear)
}