Initial commit: RegionIO Minecraft server core (26.1.2/protocol 775)

Vanilla-faithful overworld generator (final_density + multi-noise biomes),
full connection lifecycle (status/login/configuration/play), chunk streaming,
creative block editing, and the protocol/nbt/registry infrastructure.
This commit is contained in:
Master290 2026-06-24 00:32:51 +03:00
commit a7bb9496ae
146 changed files with 217621 additions and 0 deletions

158
internal/worldgen/biome.go Normal file
View file

@ -0,0 +1,158 @@
package worldgen
import "math"
// This file reproduces net.minecraft.world.level.biome.Climate, the multi-noise
// biome selector. A point in climate space is six quantized coordinates
// (temperature, humidity, continentalness, erosion, weirdness, depth); the
// finder returns the biome whose parameter range is closest to the point by the
// vanilla fitDistance metric.
//
// Coordinates are quantized to long via Math.round(v * 10000.0) exactly as the
// vanilla Climate.quantizeCoord does, and fitDistance is the sum of squared
// coordinate differences (no per-axis weighting) — matching the vanilla
// TargetPoint/ParameterPoint fitness. Range membership uses the inclusive-lower
// / exclusive-upper half-open convention vanilla applies to each axis band.
// quantize converts a climate coordinate to its long representation. Vanilla's
// Climate.quantizeCoord is Math.round(v * 10000.0); Go's math.Round halves
// away from zero, matching Java for these inputs.
func quantize(v float64) int64 {
return int64(math.Round(v * 10000.0))
}
// Quantize is the exported form of quantize, for the biome table builder in the
// world package.
func Quantize(v float64) int64 { return quantize(v) }
// AxisCount is the number of climate coordinates (temperature, humidity,
// continentalness, erosion, weirdness, depth).
const AxisCount = 6
// TargetPoint is a fully-specified climate point: the value the biome finder
// tries to match against parameter ranges. Fields are pre-quantized longs.
type TargetPoint struct {
Temperature, Humidity, Continentalness, Erosion, Weirdness, Depth int64
}
// NewTargetPoint quantizes six float climate coordinates into a TargetPoint.
func NewTargetPoint(temp, humid, cont, ero, weird, depth float64) TargetPoint {
return TargetPoint{
Temperature: quantize(temp),
Humidity: quantize(humid),
Continentalness: quantize(cont),
Erosion: quantize(ero),
Weirdness: quantize(weird),
Depth: quantize(depth),
}
}
// fitDistance is the vanilla Climate.fitness metric: the sum of squared
// differences between two points across all six axes. The squared sum is the
// comparison key; smaller is a better match.
func fitDistance(a, b TargetPoint) int64 {
dx := a.Temperature - b.Temperature
dh := a.Humidity - b.Humidity
dc := a.Continentalness - b.Continentalness
de := a.Erosion - b.Erosion
dw := a.Weirdness - b.Weirdness
dd := a.Depth - b.Depth
return dx*dx + dh*dh + dc*dc + de*de + dw*dw + dd*dd
}
// ClimateRange is one axis's [min, max] half-open band on a biome parameter.
type ClimateRange struct {
Min, Max int64
}
// contains reports whether the quantized coordinate v falls in [min, max).
func (r ClimateRange) contains(v int64) bool { return v >= r.Min && v < r.Max }
// BiomeParameter is one biome entry's full climate signature plus its name.
// Each axis is a half-open range; offset is the extra depth offset (always 0 in
// the overworld surface table, but kept for parity/future cave biomes).
type BiomeParameter struct {
Name string
// ranges[0..5] = temperature, humidity, continentalness, erosion, weirdness, depth.
Ranges [AxisCount]ClimateRange
Offset int64
}
// paramCentre returns the centre of the entry's climate ranges as a TargetPoint
// (depth centre folded in). Pre-computing this once lets the finder compare by
// distance to the centre, then verify range membership — mirroring how the
// vanilla finder prunes by fitness then tests the band.
func (p *BiomeParameter) centre() TargetPoint {
mid := func(r ClimateRange) int64 { return (r.Min + r.Max) / 2 }
return TargetPoint{
Temperature: mid(p.Ranges[0]),
Humidity: mid(p.Ranges[1]),
Continentalness: mid(p.Ranges[2]),
Erosion: mid(p.Ranges[3]),
Weirdness: mid(p.Ranges[4]),
Depth: mid(p.Ranges[5]),
}
}
// ParameterTable is the set of biome parameters the finder searches.
type ParameterTable struct {
entries []tableEntry
}
// tableEntry pairs a parameter with its precomputed centre for fast pruning.
type tableEntry struct {
param BiomeParameter
centre TargetPoint
}
// NewParameterTable builds a searchable table from raw biome parameters.
func NewParameterTable(params []BiomeParameter) *ParameterTable {
t := &ParameterTable{entries: make([]tableEntry, len(params))}
for i, p := range params {
t.entries[i] = tableEntry{param: p, centre: p.centre()}
}
return t
}
// FindBiome returns the name of the biome whose range best matches point, by
// the vanilla fitDistance metric among entries whose ranges all contain point.
// If no entry's ranges contain point (should not happen for the overworld table,
// which tiles climate space), it falls back to the nearest centre.
func (t *ParameterTable) FindBiome(point TargetPoint) string {
var best string
bestDist := int64(math.MaxInt64)
var fallback string
fallbackDist := int64(math.MaxInt64)
for _, e := range t.entries {
// Distance to centre is the pruning key (precomputed). Track it always
// so we have a fallback if no range contains the point.
d := fitDistance(point, e.centre)
if d < fallbackDist {
fallbackDist = d
fallback = e.param.Name
}
// Only consider entries whose ranges actually contain the point.
if !containsAll(e.param.Ranges, point) {
continue
}
if d < bestDist {
bestDist = d
best = e.param.Name
}
}
if best != "" {
return best
}
return fallback
}
// containsAll reports whether every range contains its corresponding coordinate.
func containsAll(ranges [AxisCount]ClimateRange, p TargetPoint) bool {
return ranges[0].contains(p.Temperature) &&
ranges[1].contains(p.Humidity) &&
ranges[2].contains(p.Continentalness) &&
ranges[3].contains(p.Erosion) &&
ranges[4].contains(p.Weirdness) &&
ranges[5].contains(p.Depth)
}

View file

@ -0,0 +1,96 @@
package worldgen
import (
"testing"
)
func TestQuantize(t *testing.T) {
cases := []struct {
v float64
want int64
}{
{0.0, 0},
{0.5, 5000},
{-1.0, -10000},
{1.0, 10000},
{-0.15, -1500},
{0.55, 5500},
}
for _, c := range cases {
if got := quantize(c.v); got != c.want {
t.Errorf("quantize(%v) = %d, want %d", c.v, got, c.want)
}
}
}
// TestFitDistanceZero confirms identical points are zero-distance and distinct
// points are positive; the exact value is not asserted to stay robust to
// representation choices.
func TestFitDistance(t *testing.T) {
a := NewTargetPoint(0, 0, 0, 0, 0, 0)
if got := fitDistance(a, a); got != 0 {
t.Errorf("fitDistance(a,a) = %d, want 0", got)
}
b := NewTargetPoint(1, 0, 0, 0, 0, 0)
// 10000^2 per axis of difference.
if got := fitDistance(a, b); got != 10000*10000 {
t.Errorf("fitDistance for 1.0 temp diff = %d, want %d", got, int64(10000*10000))
}
}
// TestRangeContains checks the half-open [min, max) band used by the finder.
func TestRangeContains(t *testing.T) {
r := ClimateRange{Min: 0, Max: 100}
if !r.contains(0) {
t.Error("min should be inclusive")
}
if r.contains(100) {
t.Error("max should be exclusive")
}
if !r.contains(50) {
t.Error("interior should contain")
}
}
// TestSampleColumnDeterministic verifies the same seed/coords give the same
// biome and a different seed gives (almost certainly) a different one.
func TestSampleColumnDeterministic(t *testing.T) {
od1, err := LoadOverworldFinalDensity(1)
if err != nil {
t.Fatalf("load seed 1: %v", err)
}
od2, err := LoadOverworldFinalDensity(99999)
if err != nil {
t.Fatalf("load seed 99999: %v", err)
}
p1a := SampleColumn(od1, 63, 100, 200)
p1b := SampleColumn(od1, 63, 100, 200)
if p1a != p1b {
t.Error("same seed/coords should produce identical TargetPoint")
}
p2 := SampleColumn(od2, 63, 100, 200)
if p1a == p2 {
// Not a hard failure (collisions exist), but flag it for inspection.
t.Log("note: different seed produced identical climate point at (100,200)")
}
}
// TestClimateFieldsLoaded confirms the loader populates all six climate axes
// from the noise_router (regression guard for the loader change).
func TestClimateFieldsLoaded(t *testing.T) {
od, err := LoadOverworldFinalDensity(42)
if err != nil {
t.Fatalf("load: %v", err)
}
if od.Final == nil {
t.Fatal("Final density not loaded")
}
dfs := []DensityFunction{od.Temperature, od.Humidity, od.Continentalness, od.Erosion, od.Weirdness, od.Depth}
for i, df := range dfs {
if df == nil {
t.Errorf("climate axis %d not loaded", i)
}
}
}

View file

@ -0,0 +1,102 @@
package worldgen
// BlendedNoise is the old_blended_noise density function: the legacy 3D
// terrain noise built from min/max limit noises and a main noise. Transcribed
// from the official BlendedNoise; the building-block noises are validated
// bit-for-bit against captured reference values.
type BlendedNoise struct {
minLimit, maxLimit, main *PerlinNoise
xzScale, yScale, xzFactor, yFactor float64
smearScaleMultiplier float64
xzMultiplier, yMultiplier float64
maxValue float64
}
// NewBlendedNoise builds a BlendedNoise from r (legacy seeding: three Perlin
// stacks drawn sequentially) and the scale parameters.
func NewBlendedNoise(r RandomSource, xzScale, yScale, xzFactor, yFactor, smearScaleMultiplier float64) *BlendedNoise {
b := &BlendedNoise{
minLimit: legacyOctaves(r, -15, 0),
maxLimit: legacyOctaves(r, -15, 0),
main: legacyOctaves(r, -7, 0),
xzScale: xzScale,
yScale: yScale,
xzFactor: xzFactor,
yFactor: yFactor,
smearScaleMultiplier: smearScaleMultiplier,
}
b.xzMultiplier = 684.412 * xzScale
b.yMultiplier = 684.412 * yScale
b.maxValue = b.minLimit.MaxBrokenValue(b.yMultiplier)
return b
}
// legacyOctaves creates a legacy PerlinNoise over the inclusive octave range
// [firstOctave, lastOctave], all amplitudes 1 (PerlinNoise.makeAmplitudes).
func legacyOctaves(r RandomSource, firstOctave, lastOctave int) *PerlinNoise {
count := lastOctave - firstOctave + 1
amps := make([]float64, count)
for i := range amps {
amps[i] = 1.0
}
return NewLegacyPerlinNoise(r, firstOctave, amps)
}
// Compute samples the blended noise at (x, y, z).
func (b *BlendedNoise) Compute(c FunctionContext) float64 {
limitX := c.X * b.xzMultiplier
limitY := c.Y * b.yMultiplier
limitZ := c.Z * b.xzMultiplier
mainX := limitX / b.xzFactor
mainY := limitY / b.yFactor
mainZ := limitZ / b.xzFactor
limitSmear := b.yMultiplier * b.smearScaleMultiplier
mainSmear := limitSmear / b.yFactor
mainNoiseValue := 0.0
pow := 1.0
for i := 0; i < 8; i++ {
if oct := b.main.GetOctaveNoise(i); oct != nil {
mainNoiseValue += oct.NoiseY(wrap(mainX*pow), wrap(mainY*pow), wrap(mainZ*pow), mainSmear*pow, mainY*pow) / pow
}
pow /= 2.0
}
factor := (mainNoiseValue/10.0 + 1.0) / 2.0
isMax := factor >= 1.0
isMin := factor <= 0.0
blendMin, blendMax := 0.0, 0.0
pow = 1.0
for i := 0; i < 16; i++ {
wx := wrap(limitX * pow)
wy := wrap(limitY * pow)
wz := wrap(limitZ * pow)
yScalePow := limitSmear * pow
if !isMax {
if oct := b.minLimit.GetOctaveNoise(i); oct != nil {
blendMin += oct.NoiseY(wx, wy, wz, yScalePow, limitY*pow) / pow
}
}
if !isMin {
if oct := b.maxLimit.GetOctaveNoise(i); oct != nil {
blendMax += oct.NoiseY(wx, wy, wz, yScalePow, limitY*pow) / pow
}
}
pow /= 2.0
}
return clampedLerp(factor, blendMin/512.0, blendMax/512.0) / 128.0
}
// clampedLerp is Mth.clampedLerp(factor, min, max): min if factor<0, max if
// factor>1, otherwise linear interpolation.
func clampedLerp(factor, min, max float64) float64 {
if factor < 0 {
return min
}
if factor > 1 {
return max
}
return min + factor*(max-min)
}

View file

@ -0,0 +1,29 @@
package worldgen
import (
"math"
"testing"
)
func approx(t *testing.T, name string, got, want float64) {
t.Helper()
if math.Abs(got-want) > 1e-12 {
t.Fatalf("%s = %v, want %v", name, got, want)
}
}
func TestImprovedNoise5Arg(t *testing.T) {
n := NewImprovedNoise(NewXoroshiro(42))
approx(t, "imp5(1.5,2.5,3.5,0.1,0.2)", n.NoiseY(1.5, 2.5, 3.5, 0.1, 0.2), 0.33416541490816576)
approx(t, "imp5(100.1,64,-200.7,0.5,1.3)", n.NoiseY(100.1, 64.0, -200.7, 0.5, 1.3), -0.31688479572345046)
}
func TestLegacyPerlinNoise(t *testing.T) {
pn := legacyOctaves(NewXoroshiro(42), -15, 0) // PerlinNoise.createLegacyForBlendedNoise(-15..0)
approx(t, "octave0.xo", pn.GetOctaveNoise(0).Xo, 190.83062484342904)
approx(t, "octave15.xo", pn.GetOctaveNoise(15).Xo, 128.19773398126475)
approx(t, "maxBrokenValue(85.5515)", pn.MaxBrokenValue(85.5515), 87.55150000000002)
approx(t, "pn5(0.5,0.5,0.5,0.3,1.1)", pn.GetValueY(0.5, 0.5, 0.5, 0.3, 1.1), 0.03454101275150972)
approx(t, "pn5(12.3,45.6,-78.9,0.3,1.1)", pn.GetValueY(12.3, 45.6, -78.9, 0.3, 1.1), 0.03853671559715216)
}

View file

@ -0,0 +1,16 @@
package worldgen
import ("math";"testing")
func TestBlendedCompute(t *testing.T){
bn:=NewBlendedNoise(NewXoroshiro(42),0.25,0.125,80.0,160.0,8.0)
cases:=[]struct{x,y,z float64;want float64}{
{0,64,0,-0.012282880040235755},
{100,40,-200,0.007126388725845459},
{1234,80,-5678,-0.13408933571823986},
{-37,128,99,-0.0932958728408956},
{8,200,8,0.0075622598474688885},
}
for _,c:=range cases{
got:=bn.Compute(FunctionContext{X:c.x,Y:c.y,Z:c.z})
if math.Abs(got-c.want)>1e-12 { t.Fatalf("bn(%v,%v,%v)=%v want %v (diff %v)",c.x,c.y,c.z,got,c.want,got-c.want) }
}
}

View file

@ -0,0 +1,37 @@
package worldgen
// This file samples the climate density functions into a TargetPoint for the
// biome finder. The climate router keys are 2D (flat_cache + y_scale=0) except
// depth, which is 3D. For surface biome selection we fix depth to 0.0, matching
// the depth=0 (surface) entries of the biome parameter table; underground and
// cave biomes use depth=1.0 / non-zero offset and are a later milestone.
// SampleColumn evaluates the six climate parameters at block (wx, wz) using od
// and returns the TargetPoint for surface biome lookup. seaLevelY is the Y at
// which to sample the 2D climate noises (callers pass the world sea level).
func SampleColumn(od *OverworldDensity, seaLevelY int, wx, wz int) TargetPoint {
ctx := FunctionContext{X: float64(wx), Y: float64(seaLevelY), Z: float64(wz)}
temp := computeOrZero(od.Temperature, ctx)
humid := computeOrZero(od.Humidity, ctx)
cont := computeOrZero(od.Continentalness, ctx)
ero := computeOrZero(od.Erosion, ctx)
weird := computeOrZero(od.Weirdness, ctx)
// Surface layer: depth axis is fixed at 0.0 so only the depth=0 (surface)
// biome parameter entries match. The real 3D depth is consulted in the
// per-cell milestone.
const surfaceDepth = 0.0
return NewTargetPoint(temp, humid, cont, ero, weird, surfaceDepth)
}
// computeOrZero evaluates df at ctx, returning 0 when df is nil (a climate key
// absent from the router). This keeps sampling robust without special-casing
// each axis at the call site.
func computeOrZero(df DensityFunction, ctx FunctionContext) float64 {
if df == nil {
return 0
}
return df.Compute(ctx)
}

View file

@ -0,0 +1,8 @@
{
"type": "minecraft:old_blended_noise",
"smear_scale_multiplier": 8.0,
"xz_factor": 80.0,
"xz_scale": 0.25,
"y_factor": 160.0,
"y_scale": 0.125
}

View file

@ -0,0 +1,83 @@
{
"type": "minecraft:cache_once",
"argument": {
"type": "minecraft:min",
"argument1": {
"type": "minecraft:add",
"argument1": {
"type": "minecraft:add",
"argument1": 0.37,
"argument2": {
"type": "minecraft:noise",
"noise": "minecraft:cave_entrance",
"xz_scale": 0.75,
"y_scale": 0.5
}
},
"argument2": {
"type": "minecraft:y_clamped_gradient",
"from_value": 0.3,
"from_y": -10,
"to_value": 0.0,
"to_y": 30
}
},
"argument2": {
"type": "minecraft:add",
"argument1": "minecraft:overworld/caves/spaghetti_roughness_function",
"argument2": {
"type": "minecraft:clamp",
"input": {
"type": "minecraft:add",
"argument1": {
"type": "minecraft:max",
"argument1": {
"type": "minecraft:weird_scaled_sampler",
"input": {
"type": "minecraft:cache_once",
"argument": {
"type": "minecraft:noise",
"noise": "minecraft:spaghetti_3d_rarity",
"xz_scale": 2.0,
"y_scale": 1.0
}
},
"noise": "minecraft:spaghetti_3d_1",
"rarity_value_mapper": "type_1"
},
"argument2": {
"type": "minecraft:weird_scaled_sampler",
"input": {
"type": "minecraft:cache_once",
"argument": {
"type": "minecraft:noise",
"noise": "minecraft:spaghetti_3d_rarity",
"xz_scale": 2.0,
"y_scale": 1.0
}
},
"noise": "minecraft:spaghetti_3d_2",
"rarity_value_mapper": "type_1"
}
},
"argument2": {
"type": "minecraft:add",
"argument1": -0.0765,
"argument2": {
"type": "minecraft:mul",
"argument1": -0.011499999999999996,
"argument2": {
"type": "minecraft:noise",
"noise": "minecraft:spaghetti_3d_thickness",
"xz_scale": 1.0,
"y_scale": 1.0
}
}
}
},
"max": 1.0,
"min": -1.0
}
}
}
}

View file

@ -0,0 +1,94 @@
{
"type": "minecraft:range_choice",
"input": {
"type": "minecraft:interpolated",
"argument": {
"type": "minecraft:range_choice",
"input": "minecraft:y",
"max_exclusive": 321.0,
"min_inclusive": -60.0,
"when_in_range": {
"type": "minecraft:noise",
"noise": "minecraft:noodle",
"xz_scale": 1.0,
"y_scale": 1.0
},
"when_out_of_range": -1.0
}
},
"max_exclusive": 0.0,
"min_inclusive": -1000000.0,
"when_in_range": 64.0,
"when_out_of_range": {
"type": "minecraft:add",
"argument1": {
"type": "minecraft:interpolated",
"argument": {
"type": "minecraft:range_choice",
"input": "minecraft:y",
"max_exclusive": 321.0,
"min_inclusive": -60.0,
"when_in_range": {
"type": "minecraft:add",
"argument1": -0.07500000000000001,
"argument2": {
"type": "minecraft:mul",
"argument1": -0.025,
"argument2": {
"type": "minecraft:noise",
"noise": "minecraft:noodle_thickness",
"xz_scale": 1.0,
"y_scale": 1.0
}
}
},
"when_out_of_range": 0.0
}
},
"argument2": {
"type": "minecraft:mul",
"argument1": 1.5,
"argument2": {
"type": "minecraft:max",
"argument1": {
"type": "minecraft:abs",
"argument": {
"type": "minecraft:interpolated",
"argument": {
"type": "minecraft:range_choice",
"input": "minecraft:y",
"max_exclusive": 321.0,
"min_inclusive": -60.0,
"when_in_range": {
"type": "minecraft:noise",
"noise": "minecraft:noodle_ridge_a",
"xz_scale": 2.6666666666666665,
"y_scale": 2.6666666666666665
},
"when_out_of_range": 0.0
}
}
},
"argument2": {
"type": "minecraft:abs",
"argument": {
"type": "minecraft:interpolated",
"argument": {
"type": "minecraft:range_choice",
"input": "minecraft:y",
"max_exclusive": 321.0,
"min_inclusive": -60.0,
"when_in_range": {
"type": "minecraft:noise",
"noise": "minecraft:noodle_ridge_b",
"xz_scale": 2.6666666666666665,
"y_scale": 2.6666666666666665
},
"when_out_of_range": 0.0
}
}
}
}
}
}
}

View file

@ -0,0 +1,50 @@
{
"type": "minecraft:cache_once",
"argument": {
"type": "minecraft:mul",
"argument1": {
"type": "minecraft:add",
"argument1": {
"type": "minecraft:mul",
"argument1": 2.0,
"argument2": {
"type": "minecraft:noise",
"noise": "minecraft:pillar",
"xz_scale": 25.0,
"y_scale": 0.3
}
},
"argument2": {
"type": "minecraft:add",
"argument1": -1.0,
"argument2": {
"type": "minecraft:mul",
"argument1": -1.0,
"argument2": {
"type": "minecraft:noise",
"noise": "minecraft:pillar_rareness",
"xz_scale": 1.0,
"y_scale": 1.0
}
}
}
},
"argument2": {
"type": "minecraft:cube",
"argument": {
"type": "minecraft:add",
"argument1": 0.55,
"argument2": {
"type": "minecraft:mul",
"argument1": 0.55,
"argument2": {
"type": "minecraft:noise",
"noise": "minecraft:pillar_thickness",
"xz_scale": 1.0,
"y_scale": 1.0
}
}
}
}
}
}

View file

@ -0,0 +1,61 @@
{
"type": "minecraft:clamp",
"input": {
"type": "minecraft:max",
"argument1": {
"type": "minecraft:add",
"argument1": {
"type": "minecraft:weird_scaled_sampler",
"input": {
"type": "minecraft:noise",
"noise": "minecraft:spaghetti_2d_modulator",
"xz_scale": 2.0,
"y_scale": 1.0
},
"noise": "minecraft:spaghetti_2d",
"rarity_value_mapper": "type_2"
},
"argument2": {
"type": "minecraft:mul",
"argument1": 0.083,
"argument2": "minecraft:overworld/caves/spaghetti_2d_thickness_modulator"
}
},
"argument2": {
"type": "minecraft:cube",
"argument": {
"type": "minecraft:add",
"argument1": {
"type": "minecraft:abs",
"argument": {
"type": "minecraft:add",
"argument1": {
"type": "minecraft:add",
"argument1": 0.0,
"argument2": {
"type": "minecraft:mul",
"argument1": 8.0,
"argument2": {
"type": "minecraft:noise",
"noise": "minecraft:spaghetti_2d_elevation",
"xz_scale": 1.0,
"y_scale": 0.0
}
}
},
"argument2": {
"type": "minecraft:y_clamped_gradient",
"from_value": 8.0,
"from_y": -64,
"to_value": -40.0,
"to_y": 320
}
}
},
"argument2": "minecraft:overworld/caves/spaghetti_2d_thickness_modulator"
}
}
},
"max": 1.0,
"min": -1.0
}

View file

@ -0,0 +1,17 @@
{
"type": "minecraft:cache_once",
"argument": {
"type": "minecraft:add",
"argument1": -0.95,
"argument2": {
"type": "minecraft:mul",
"argument1": -0.35000000000000003,
"argument2": {
"type": "minecraft:noise",
"noise": "minecraft:spaghetti_2d_thickness",
"xz_scale": 2.0,
"y_scale": 1.0
}
}
}
}

View file

@ -0,0 +1,33 @@
{
"type": "minecraft:cache_once",
"argument": {
"type": "minecraft:mul",
"argument1": {
"type": "minecraft:add",
"argument1": -0.05,
"argument2": {
"type": "minecraft:mul",
"argument1": -0.05,
"argument2": {
"type": "minecraft:noise",
"noise": "minecraft:spaghetti_roughness_modulator",
"xz_scale": 1.0,
"y_scale": 1.0
}
}
},
"argument2": {
"type": "minecraft:add",
"argument1": -0.4,
"argument2": {
"type": "minecraft:abs",
"argument": {
"type": "minecraft:noise",
"noise": "minecraft:spaghetti_roughness",
"xz_scale": 1.0,
"y_scale": 1.0
}
}
}
}
}

View file

@ -0,0 +1,12 @@
{
"type": "minecraft:flat_cache",
"argument": {
"type": "minecraft:shifted_noise",
"noise": "minecraft:continentalness",
"shift_x": "minecraft:shift_x",
"shift_y": 0.0,
"shift_z": "minecraft:shift_z",
"xz_scale": 0.25,
"y_scale": 0.0
}
}

View file

@ -0,0 +1,11 @@
{
"type": "minecraft:add",
"argument1": {
"type": "minecraft:y_clamped_gradient",
"from_value": 1.5,
"from_y": -64,
"to_value": -1.5,
"to_y": 320
},
"argument2": "minecraft:overworld/offset"
}

View file

@ -0,0 +1,12 @@
{
"type": "minecraft:flat_cache",
"argument": {
"type": "minecraft:shifted_noise",
"noise": "minecraft:erosion",
"shift_x": "minecraft:shift_x",
"shift_y": 0.0,
"shift_z": "minecraft:shift_z",
"xz_scale": 0.25,
"y_scale": 0.0
}
}

View file

@ -0,0 +1,890 @@
{
"type": "minecraft:flat_cache",
"argument": {
"type": "minecraft:cache_2d",
"argument": {
"type": "minecraft:add",
"argument1": 10.0,
"argument2": {
"type": "minecraft:mul",
"argument1": {
"type": "minecraft:blend_alpha"
},
"argument2": {
"type": "minecraft:add",
"argument1": -10.0,
"argument2": {
"type": "minecraft:spline",
"spline": {
"coordinate": "minecraft:overworld/continents",
"points": [
{
"derivative": 0.0,
"location": -0.19,
"value": 3.95
},
{
"derivative": 0.0,
"location": -0.15,
"value": {
"coordinate": "minecraft:overworld/erosion",
"points": [
{
"derivative": 0.0,
"location": -0.6,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": -0.2,
"value": 6.3
},
{
"derivative": 0.0,
"location": 0.2,
"value": 6.25
}
]
}
},
{
"derivative": 0.0,
"location": -0.5,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": -0.05,
"value": 6.3
},
{
"derivative": 0.0,
"location": 0.05,
"value": 2.67
}
]
}
},
{
"derivative": 0.0,
"location": -0.35,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": -0.2,
"value": 6.3
},
{
"derivative": 0.0,
"location": 0.2,
"value": 6.25
}
]
}
},
{
"derivative": 0.0,
"location": -0.25,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": -0.2,
"value": 6.3
},
{
"derivative": 0.0,
"location": 0.2,
"value": 6.25
}
]
}
},
{
"derivative": 0.0,
"location": -0.1,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": -0.05,
"value": 2.67
},
{
"derivative": 0.0,
"location": 0.05,
"value": 6.3
}
]
}
},
{
"derivative": 0.0,
"location": 0.03,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": -0.2,
"value": 6.3
},
{
"derivative": 0.0,
"location": 0.2,
"value": 6.25
}
]
}
},
{
"derivative": 0.0,
"location": 0.35,
"value": 6.25
},
{
"derivative": 0.0,
"location": 0.45,
"value": {
"coordinate": "minecraft:overworld/ridges_folded",
"points": [
{
"derivative": 0.0,
"location": -0.9,
"value": 6.25
},
{
"derivative": 0.0,
"location": -0.69,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": 0.0,
"value": 6.25
},
{
"derivative": 0.0,
"location": 0.1,
"value": 0.625
}
]
}
}
]
}
},
{
"derivative": 0.0,
"location": 0.55,
"value": {
"coordinate": "minecraft:overworld/ridges_folded",
"points": [
{
"derivative": 0.0,
"location": -0.9,
"value": 6.25
},
{
"derivative": 0.0,
"location": -0.69,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": 0.0,
"value": 6.25
},
{
"derivative": 0.0,
"location": 0.1,
"value": 0.625
}
]
}
}
]
}
},
{
"derivative": 0.0,
"location": 0.62,
"value": 6.25
}
]
}
},
{
"derivative": 0.0,
"location": -0.1,
"value": {
"coordinate": "minecraft:overworld/erosion",
"points": [
{
"derivative": 0.0,
"location": -0.6,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": -0.2,
"value": 6.3
},
{
"derivative": 0.0,
"location": 0.2,
"value": 5.47
}
]
}
},
{
"derivative": 0.0,
"location": -0.5,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": -0.05,
"value": 6.3
},
{
"derivative": 0.0,
"location": 0.05,
"value": 2.67
}
]
}
},
{
"derivative": 0.0,
"location": -0.35,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": -0.2,
"value": 6.3
},
{
"derivative": 0.0,
"location": 0.2,
"value": 5.47
}
]
}
},
{
"derivative": 0.0,
"location": -0.25,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": -0.2,
"value": 6.3
},
{
"derivative": 0.0,
"location": 0.2,
"value": 5.47
}
]
}
},
{
"derivative": 0.0,
"location": -0.1,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": -0.05,
"value": 2.67
},
{
"derivative": 0.0,
"location": 0.05,
"value": 6.3
}
]
}
},
{
"derivative": 0.0,
"location": 0.03,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": -0.2,
"value": 6.3
},
{
"derivative": 0.0,
"location": 0.2,
"value": 5.47
}
]
}
},
{
"derivative": 0.0,
"location": 0.35,
"value": 5.47
},
{
"derivative": 0.0,
"location": 0.45,
"value": {
"coordinate": "minecraft:overworld/ridges_folded",
"points": [
{
"derivative": 0.0,
"location": -0.9,
"value": 5.47
},
{
"derivative": 0.0,
"location": -0.69,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": 0.0,
"value": 5.47
},
{
"derivative": 0.0,
"location": 0.1,
"value": 0.625
}
]
}
}
]
}
},
{
"derivative": 0.0,
"location": 0.55,
"value": {
"coordinate": "minecraft:overworld/ridges_folded",
"points": [
{
"derivative": 0.0,
"location": -0.9,
"value": 5.47
},
{
"derivative": 0.0,
"location": -0.69,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": 0.0,
"value": 5.47
},
{
"derivative": 0.0,
"location": 0.1,
"value": 0.625
}
]
}
}
]
}
},
{
"derivative": 0.0,
"location": 0.62,
"value": 5.47
}
]
}
},
{
"derivative": 0.0,
"location": 0.03,
"value": {
"coordinate": "minecraft:overworld/erosion",
"points": [
{
"derivative": 0.0,
"location": -0.6,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": -0.2,
"value": 6.3
},
{
"derivative": 0.0,
"location": 0.2,
"value": 5.08
}
]
}
},
{
"derivative": 0.0,
"location": -0.5,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": -0.05,
"value": 6.3
},
{
"derivative": 0.0,
"location": 0.05,
"value": 2.67
}
]
}
},
{
"derivative": 0.0,
"location": -0.35,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": -0.2,
"value": 6.3
},
{
"derivative": 0.0,
"location": 0.2,
"value": 5.08
}
]
}
},
{
"derivative": 0.0,
"location": -0.25,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": -0.2,
"value": 6.3
},
{
"derivative": 0.0,
"location": 0.2,
"value": 5.08
}
]
}
},
{
"derivative": 0.0,
"location": -0.1,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": -0.05,
"value": 2.67
},
{
"derivative": 0.0,
"location": 0.05,
"value": 6.3
}
]
}
},
{
"derivative": 0.0,
"location": 0.03,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": -0.2,
"value": 6.3
},
{
"derivative": 0.0,
"location": 0.2,
"value": 5.08
}
]
}
},
{
"derivative": 0.0,
"location": 0.35,
"value": 5.08
},
{
"derivative": 0.0,
"location": 0.45,
"value": {
"coordinate": "minecraft:overworld/ridges_folded",
"points": [
{
"derivative": 0.0,
"location": -0.9,
"value": 5.08
},
{
"derivative": 0.0,
"location": -0.69,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": 0.0,
"value": 5.08
},
{
"derivative": 0.0,
"location": 0.1,
"value": 0.625
}
]
}
}
]
}
},
{
"derivative": 0.0,
"location": 0.55,
"value": {
"coordinate": "minecraft:overworld/ridges_folded",
"points": [
{
"derivative": 0.0,
"location": -0.9,
"value": 5.08
},
{
"derivative": 0.0,
"location": -0.69,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": 0.0,
"value": 5.08
},
{
"derivative": 0.0,
"location": 0.1,
"value": 0.625
}
]
}
}
]
}
},
{
"derivative": 0.0,
"location": 0.62,
"value": 5.08
}
]
}
},
{
"derivative": 0.0,
"location": 0.06,
"value": {
"coordinate": "minecraft:overworld/erosion",
"points": [
{
"derivative": 0.0,
"location": -0.6,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": -0.2,
"value": 6.3
},
{
"derivative": 0.0,
"location": 0.2,
"value": 4.69
}
]
}
},
{
"derivative": 0.0,
"location": -0.5,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": -0.05,
"value": 6.3
},
{
"derivative": 0.0,
"location": 0.05,
"value": 2.67
}
]
}
},
{
"derivative": 0.0,
"location": -0.35,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": -0.2,
"value": 6.3
},
{
"derivative": 0.0,
"location": 0.2,
"value": 4.69
}
]
}
},
{
"derivative": 0.0,
"location": -0.25,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": -0.2,
"value": 6.3
},
{
"derivative": 0.0,
"location": 0.2,
"value": 4.69
}
]
}
},
{
"derivative": 0.0,
"location": -0.1,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": -0.05,
"value": 2.67
},
{
"derivative": 0.0,
"location": 0.05,
"value": 6.3
}
]
}
},
{
"derivative": 0.0,
"location": 0.03,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": -0.2,
"value": 6.3
},
{
"derivative": 0.0,
"location": 0.2,
"value": 4.69
}
]
}
},
{
"derivative": 0.0,
"location": 0.05,
"value": {
"coordinate": "minecraft:overworld/ridges_folded",
"points": [
{
"derivative": 0.0,
"location": 0.45,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": -0.2,
"value": 6.3
},
{
"derivative": 0.0,
"location": 0.2,
"value": 4.69
}
]
}
},
{
"derivative": 0.0,
"location": 0.7,
"value": 1.56
}
]
}
},
{
"derivative": 0.0,
"location": 0.4,
"value": {
"coordinate": "minecraft:overworld/ridges_folded",
"points": [
{
"derivative": 0.0,
"location": 0.45,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": -0.2,
"value": 6.3
},
{
"derivative": 0.0,
"location": 0.2,
"value": 4.69
}
]
}
},
{
"derivative": 0.0,
"location": 0.7,
"value": 1.56
}
]
}
},
{
"derivative": 0.0,
"location": 0.45,
"value": {
"coordinate": "minecraft:overworld/ridges_folded",
"points": [
{
"derivative": 0.0,
"location": -0.7,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": -0.2,
"value": 6.3
},
{
"derivative": 0.0,
"location": 0.2,
"value": 4.69
}
]
}
},
{
"derivative": 0.0,
"location": -0.15,
"value": 1.37
}
]
}
},
{
"derivative": 0.0,
"location": 0.55,
"value": {
"coordinate": "minecraft:overworld/ridges_folded",
"points": [
{
"derivative": 0.0,
"location": -0.7,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": -0.2,
"value": 6.3
},
{
"derivative": 0.0,
"location": 0.2,
"value": 4.69
}
]
}
},
{
"derivative": 0.0,
"location": -0.15,
"value": 1.37
}
]
}
},
{
"derivative": 0.0,
"location": 0.58,
"value": 4.69
}
]
}
}
]
}
}
}
}
}
}
}

View file

@ -0,0 +1,303 @@
{
"type": "minecraft:flat_cache",
"argument": {
"type": "minecraft:cache_2d",
"argument": {
"type": "minecraft:add",
"argument1": 0.0,
"argument2": {
"type": "minecraft:mul",
"argument1": {
"type": "minecraft:blend_alpha"
},
"argument2": {
"type": "minecraft:add",
"argument1": -0.0,
"argument2": {
"type": "minecraft:spline",
"spline": {
"coordinate": "minecraft:overworld/continents",
"points": [
{
"derivative": 0.0,
"location": -0.11,
"value": 0.0
},
{
"derivative": 0.0,
"location": 0.03,
"value": {
"coordinate": "minecraft:overworld/erosion",
"points": [
{
"derivative": 0.0,
"location": -1.0,
"value": {
"coordinate": "minecraft:overworld/ridges_folded",
"points": [
{
"derivative": 0.0,
"location": 0.19999999,
"value": 0.0
},
{
"derivative": 0.0,
"location": 0.44999996,
"value": 0.0
},
{
"derivative": 0.0,
"location": 1.0,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": -0.01,
"value": 0.63
},
{
"derivative": 0.0,
"location": 0.01,
"value": 0.3
}
]
}
}
]
}
},
{
"derivative": 0.0,
"location": -0.78,
"value": {
"coordinate": "minecraft:overworld/ridges_folded",
"points": [
{
"derivative": 0.0,
"location": 0.19999999,
"value": 0.0
},
{
"derivative": 0.0,
"location": 0.44999996,
"value": 0.0
},
{
"derivative": 0.0,
"location": 1.0,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": -0.01,
"value": 0.315
},
{
"derivative": 0.0,
"location": 0.01,
"value": 0.15
}
]
}
}
]
}
},
{
"derivative": 0.0,
"location": -0.5775,
"value": {
"coordinate": "minecraft:overworld/ridges_folded",
"points": [
{
"derivative": 0.0,
"location": 0.19999999,
"value": 0.0
},
{
"derivative": 0.0,
"location": 0.44999996,
"value": 0.0
},
{
"derivative": 0.0,
"location": 1.0,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": -0.01,
"value": 0.315
},
{
"derivative": 0.0,
"location": 0.01,
"value": 0.15
}
]
}
}
]
}
},
{
"derivative": 0.0,
"location": -0.375,
"value": 0.0
}
]
}
},
{
"derivative": 0.0,
"location": 0.65,
"value": {
"coordinate": "minecraft:overworld/erosion",
"points": [
{
"derivative": 0.0,
"location": -1.0,
"value": {
"coordinate": "minecraft:overworld/ridges_folded",
"points": [
{
"derivative": 0.0,
"location": 0.19999999,
"value": 0.0
},
{
"derivative": 0.0,
"location": 0.44999996,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": -0.01,
"value": 0.63
},
{
"derivative": 0.0,
"location": 0.01,
"value": 0.3
}
]
}
},
{
"derivative": 0.0,
"location": 1.0,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": -0.01,
"value": 0.63
},
{
"derivative": 0.0,
"location": 0.01,
"value": 0.3
}
]
}
}
]
}
},
{
"derivative": 0.0,
"location": -0.78,
"value": {
"coordinate": "minecraft:overworld/ridges_folded",
"points": [
{
"derivative": 0.0,
"location": 0.19999999,
"value": 0.0
},
{
"derivative": 0.0,
"location": 0.44999996,
"value": 0.0
},
{
"derivative": 0.0,
"location": 1.0,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": -0.01,
"value": 0.63
},
{
"derivative": 0.0,
"location": 0.01,
"value": 0.3
}
]
}
}
]
}
},
{
"derivative": 0.0,
"location": -0.5775,
"value": {
"coordinate": "minecraft:overworld/ridges_folded",
"points": [
{
"derivative": 0.0,
"location": 0.19999999,
"value": 0.0
},
{
"derivative": 0.0,
"location": 0.44999996,
"value": 0.0
},
{
"derivative": 0.0,
"location": 1.0,
"value": {
"coordinate": "minecraft:overworld/ridges",
"points": [
{
"derivative": 0.0,
"location": -0.01,
"value": 0.63
},
{
"derivative": 0.0,
"location": 0.01,
"value": 0.3
}
]
}
}
]
}
},
{
"derivative": 0.0,
"location": -0.375,
"value": 0.0
}
]
}
}
]
}
}
}
}
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,12 @@
{
"type": "minecraft:flat_cache",
"argument": {
"type": "minecraft:shifted_noise",
"noise": "minecraft:ridge",
"shift_x": "minecraft:shift_x",
"shift_y": 0.0,
"shift_z": "minecraft:shift_z",
"xz_scale": 0.25,
"y_scale": 0.0
}
}

View file

@ -0,0 +1,19 @@
{
"type": "minecraft:mul",
"argument1": -3.0,
"argument2": {
"type": "minecraft:add",
"argument1": -0.3333333333333333,
"argument2": {
"type": "minecraft:abs",
"argument": {
"type": "minecraft:add",
"argument1": -0.6666666666666666,
"argument2": {
"type": "minecraft:abs",
"argument": "minecraft:overworld/ridges"
}
}
}
}
}

View file

@ -0,0 +1,32 @@
{
"type": "minecraft:add",
"argument1": {
"type": "minecraft:mul",
"argument1": 4.0,
"argument2": {
"type": "minecraft:quarter_negative",
"argument": {
"type": "minecraft:mul",
"argument1": {
"type": "minecraft:add",
"argument1": "minecraft:overworld/depth",
"argument2": {
"type": "minecraft:mul",
"argument1": "minecraft:overworld/jaggedness",
"argument2": {
"type": "minecraft:half_negative",
"argument": {
"type": "minecraft:noise",
"noise": "minecraft:jagged",
"xz_scale": 1500.0,
"y_scale": 0.0
}
}
}
},
"argument2": "minecraft:overworld/factor"
}
}
},
"argument2": "minecraft:overworld/base_3d_noise"
}

View file

@ -0,0 +1,10 @@
{
"type": "minecraft:flat_cache",
"argument": {
"type": "minecraft:cache_2d",
"argument": {
"type": "minecraft:shift_a",
"argument": "minecraft:offset"
}
}
}

View file

@ -0,0 +1,10 @@
{
"type": "minecraft:flat_cache",
"argument": {
"type": "minecraft:cache_2d",
"argument": {
"type": "minecraft:shift_b",
"argument": "minecraft:offset"
}
}
}

View file

@ -0,0 +1,7 @@
{
"type": "minecraft:y_clamped_gradient",
"from_value": -4064.0,
"from_y": -4064,
"to_value": 4062.0,
"to_y": 4062
}

View file

@ -0,0 +1 @@
0.0

View file

@ -0,0 +1,6 @@
{
"amplitudes": [
1.0
],
"firstOctave": -3
}

View file

@ -0,0 +1,6 @@
{
"amplitudes": [
1.0
],
"firstOctave": -7
}

View file

@ -0,0 +1,6 @@
{
"amplitudes": [
1.0
],
"firstOctave": -5
}

View file

@ -0,0 +1,6 @@
{
"amplitudes": [
1.0
],
"firstOctave": -1
}

View file

@ -0,0 +1,9 @@
{
"amplitudes": [
1.0,
1.0,
1.0,
1.0
],
"firstOctave": -2
}

View file

@ -0,0 +1,6 @@
{
"amplitudes": [
1.0
],
"firstOctave": -8
}

View file

@ -0,0 +1,8 @@
{
"amplitudes": [
1.0,
1.0,
1.0
],
"firstOctave": -6
}

View file

@ -0,0 +1,9 @@
{
"amplitudes": [
1.0,
1.0,
1.0,
1.0
],
"firstOctave": -9
}

View file

@ -0,0 +1,14 @@
{
"amplitudes": [
0.5,
1.0,
2.0,
1.0,
2.0,
1.0,
0.0,
2.0,
0.0
],
"firstOctave": -8
}

View file

@ -0,0 +1,8 @@
{
"amplitudes": [
0.4,
0.5,
1.0
],
"firstOctave": -7
}

View file

@ -0,0 +1,6 @@
{
"amplitudes": [
1.0
],
"firstOctave": -8
}

View file

@ -0,0 +1,6 @@
{
"amplitudes": [
1.0
],
"firstOctave": -8
}

View file

@ -0,0 +1,14 @@
{
"amplitudes": [
1.0,
1.0,
2.0,
2.0,
2.0,
1.0,
1.0,
1.0,
1.0
],
"firstOctave": -9
}

View file

@ -0,0 +1,14 @@
{
"amplitudes": [
1.0,
1.0,
2.0,
2.0,
2.0,
1.0,
1.0,
1.0,
1.0
],
"firstOctave": -11
}

View file

@ -0,0 +1,10 @@
{
"amplitudes": [
1.0,
1.0,
0.0,
1.0,
1.0
],
"firstOctave": -9
}

View file

@ -0,0 +1,10 @@
{
"amplitudes": [
1.0,
1.0,
0.0,
1.0,
1.0
],
"firstOctave": -11
}

View file

@ -0,0 +1,9 @@
{
"amplitudes": [
1.0,
1.0,
1.0,
1.0
],
"firstOctave": -8
}

View file

@ -0,0 +1,14 @@
{
"amplitudes": [
1.0,
1.0,
1.0,
1.0,
0.0,
0.0,
0.0,
0.0,
0.013333333333333334
],
"firstOctave": -8
}

View file

@ -0,0 +1,9 @@
{
"amplitudes": [
1.0,
1.0,
1.0,
1.0
],
"firstOctave": -4
}

View file

@ -0,0 +1,9 @@
{
"amplitudes": [
1.0,
1.0,
1.0,
1.0
],
"firstOctave": -6
}

View file

@ -0,0 +1,6 @@
{
"amplitudes": [
1.0
],
"firstOctave": -3
}

View file

@ -0,0 +1,8 @@
{
"amplitudes": [
1.0,
1.0,
1.0
],
"firstOctave": -6
}

View file

@ -0,0 +1,21 @@
{
"amplitudes": [
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0
],
"firstOctave": -16
}

View file

@ -0,0 +1,7 @@
{
"amplitudes": [
1.0,
1.0
],
"firstOctave": -7
}

View file

@ -0,0 +1,7 @@
{
"amplitudes": [
1.0,
1.0
],
"firstOctave": -7
}

View file

@ -0,0 +1,6 @@
{
"amplitudes": [
1.0
],
"firstOctave": -4
}

View file

@ -0,0 +1,9 @@
{
"amplitudes": [
1.0,
0.0,
0.0,
0.9
],
"firstOctave": -3
}

View file

@ -0,0 +1,9 @@
{
"amplitudes": [
1.0,
0.0,
0.0,
0.35
],
"firstOctave": -3
}

View file

@ -0,0 +1,6 @@
{
"amplitudes": [
1.0
],
"firstOctave": -8
}

View file

@ -0,0 +1,6 @@
{
"amplitudes": [
1.0
],
"firstOctave": -7
}

View file

@ -0,0 +1,6 @@
{
"amplitudes": [
1.0
],
"firstOctave": -7
}

View file

@ -0,0 +1,6 @@
{
"amplitudes": [
1.0
],
"firstOctave": -8
}

View file

@ -0,0 +1,9 @@
{
"amplitudes": [
1.0,
1.0,
1.0,
0.0
],
"firstOctave": -3
}

View file

@ -0,0 +1,6 @@
{
"amplitudes": [
1.0
],
"firstOctave": -5
}

View file

@ -0,0 +1,6 @@
{
"amplitudes": [
1.0
],
"firstOctave": -7
}

View file

@ -0,0 +1,6 @@
{
"amplitudes": [
1.0
],
"firstOctave": -7
}

View file

@ -0,0 +1,6 @@
{
"amplitudes": [
1.0
],
"firstOctave": -8
}

View file

@ -0,0 +1,9 @@
{
"amplitudes": [
1.0,
1.0,
1.0,
1.0
],
"firstOctave": -7
}

View file

@ -0,0 +1,11 @@
{
"amplitudes": [
1.0,
0.0,
0.0,
0.0,
0.0,
0.013333333333333334
],
"firstOctave": -5
}

View file

@ -0,0 +1,7 @@
{
"amplitudes": [
1.0,
1.0
],
"firstOctave": -7
}

View file

@ -0,0 +1,6 @@
{
"amplitudes": [
1.0
],
"firstOctave": -8
}

View file

@ -0,0 +1,6 @@
{
"amplitudes": [
1.0
],
"firstOctave": -8
}

View file

@ -0,0 +1,9 @@
{
"amplitudes": [
1.0,
1.0,
1.0,
1.0
],
"firstOctave": -6
}

View file

@ -0,0 +1,11 @@
{
"amplitudes": [
1.0,
2.0,
1.0,
0.0,
0.0,
0.0
],
"firstOctave": -7
}

View file

@ -0,0 +1,14 @@
{
"amplitudes": [
1.0,
1.0,
1.0,
1.0,
0.0,
0.0,
0.0,
0.0,
0.013333333333333334
],
"firstOctave": -8
}

View file

@ -0,0 +1,6 @@
{
"amplitudes": [
1.0
],
"firstOctave": -7
}

View file

@ -0,0 +1,6 @@
{
"amplitudes": [
1.0
],
"firstOctave": -8
}

View file

@ -0,0 +1,6 @@
{
"amplitudes": [
1.0
],
"firstOctave": -11
}

View file

@ -0,0 +1,6 @@
{
"amplitudes": [
1.0
],
"firstOctave": -11
}

View file

@ -0,0 +1,6 @@
{
"amplitudes": [
1.0
],
"firstOctave": -7
}

View file

@ -0,0 +1,6 @@
{
"amplitudes": [
1.0
],
"firstOctave": -7
}

View file

@ -0,0 +1,6 @@
{
"amplitudes": [
1.0
],
"firstOctave": -11
}

View file

@ -0,0 +1,6 @@
{
"amplitudes": [
1.0
],
"firstOctave": -8
}

View file

@ -0,0 +1,6 @@
{
"amplitudes": [
1.0
],
"firstOctave": -5
}

View file

@ -0,0 +1,6 @@
{
"amplitudes": [
1.0
],
"firstOctave": -8
}

View file

@ -0,0 +1,8 @@
{
"amplitudes": [
1.0,
1.0,
1.0
],
"firstOctave": -6
}

View file

@ -0,0 +1,9 @@
{
"amplitudes": [
1.0,
1.0,
0.0,
1.0
],
"firstOctave": -6
}

View file

@ -0,0 +1,6 @@
{
"amplitudes": [
1.0
],
"firstOctave": -2
}

View file

@ -0,0 +1,11 @@
{
"amplitudes": [
1.5,
0.0,
1.0,
0.0,
0.0,
0.0
],
"firstOctave": -10
}

View file

@ -0,0 +1,11 @@
{
"amplitudes": [
1.5,
0.0,
1.0,
0.0,
0.0,
0.0
],
"firstOctave": -12
}

View file

@ -0,0 +1,11 @@
{
"amplitudes": [
1.0,
1.0,
0.0,
0.0,
0.0,
0.0
],
"firstOctave": -8
}

View file

@ -0,0 +1,11 @@
{
"amplitudes": [
1.0,
1.0,
0.0,
0.0,
0.0,
0.0
],
"firstOctave": -10
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,250 @@
package worldgen
import "math"
// FunctionContext is the sample point for a density function (block coords).
// During chunk generation, interp holds the precomputed cell-interpolated value
// for each Interpolated node (indexed by node); it is nil for plain evaluation.
type FunctionContext struct {
X, Y, Z float64
interp []float64
}
// WithInterp returns a copy of c carrying the given per-node interpolated values.
func (c FunctionContext) WithInterp(v []float64) FunctionContext {
c.interp = v
return c
}
// Interpolated marks a sub-function that vanilla samples on the cell-corner grid
// and trilinearly interpolates (the heavy 3D terrain noise). During generation
// the value is looked up by Index; otherwise the inner function is evaluated.
type Interpolated struct {
Inner DensityFunction
Index int
}
func (n *Interpolated) Compute(c FunctionContext) float64 {
if c.interp != nil {
return c.interp[n.Index]
}
return n.Inner.Compute(c)
}
// DensityFunction is a node in the density-function tree. Compute returns the
// density at the given point; positive conventionally means "solid".
//
// This is the interpreter engine; only the node types we currently need are
// implemented. The full vanilla set (splines, blend_density, caches, etc.) can
// be added incrementally without changing this interface.
type DensityFunction interface {
Compute(c FunctionContext) float64
}
// Constant is a fixed value.
type Constant float64
func (c Constant) Compute(FunctionContext) float64 { return float64(c) }
type binaryOp struct {
a, b DensityFunction
op func(x, y float64) float64
}
func (n binaryOp) Compute(c FunctionContext) float64 { return n.op(n.a.Compute(c), n.b.Compute(c)) }
// Add, Mul, Min, Max combine two density functions pointwise.
func Add(a, b DensityFunction) DensityFunction {
return binaryOp{a, b, func(x, y float64) float64 { return x + y }}
}
func Mul(a, b DensityFunction) DensityFunction {
return binaryOp{a, b, func(x, y float64) float64 { return x * y }}
}
func Min(a, b DensityFunction) DensityFunction {
return binaryOp{a, b, func(x, y float64) float64 {
if x < y {
return x
}
return y
}}
}
func Max(a, b DensityFunction) DensityFunction {
return binaryOp{a, b, func(x, y float64) float64 {
if x > y {
return x
}
return y
}}
}
// YClampedGradient is the y_clamped_gradient node: a linear map of Y from
// [fromY, toY] onto [fromV, toV], clamped outside that range.
type YClampedGradient struct {
FromY, ToY, FromV, ToV float64
}
func (g YClampedGradient) Compute(c FunctionContext) float64 {
return clampedMap(c.Y, g.FromY, g.ToY, g.FromV, g.ToV)
}
// NoiseDF samples a NormalNoise, scaling the input coordinates (the "noise" /
// "shifted_noise" family, without the shift inputs).
type NoiseDF struct {
Noise *NormalNoise
XZScale, YScale float64
}
func (n NoiseDF) Compute(c FunctionContext) float64 {
return n.Noise.GetValue(c.X*n.XZScale, c.Y*n.YScale, c.Z*n.XZScale)
}
type unaryOp struct {
a DensityFunction
op func(float64) float64
}
func (n unaryOp) Compute(c FunctionContext) float64 { return n.op(n.a.Compute(c)) }
// Abs, Square, Cube, HalfNegative, QuarterNegative, Squeeze are the unary
// transforms used by the vanilla density tree.
func Abs(a DensityFunction) DensityFunction { return unaryOp{a, math.Abs} }
func Square(a DensityFunction) DensityFunction { return unaryOp{a, func(x float64) float64 { return x * x }} }
func Cube(a DensityFunction) DensityFunction { return unaryOp{a, func(x float64) float64 { return x * x * x }} }
func HalfNegative(a DensityFunction) DensityFunction {
return unaryOp{a, func(x float64) float64 {
if x > 0 {
return x
}
return x * 0.5
}}
}
func QuarterNegative(a DensityFunction) DensityFunction {
return unaryOp{a, func(x float64) float64 {
if x > 0 {
return x
}
return x * 0.25
}}
}
func Squeeze(a DensityFunction) DensityFunction {
return unaryOp{a, func(x float64) float64 {
d := clamp(x, -1, 1)
return d/2.0 - d*d*d/24.0
}}
}
// Clamp constrains a density function to [min, max].
func Clamp(a DensityFunction, min, max float64) DensityFunction {
return unaryOp{a, func(x float64) float64 { return clamp(x, min, max) }}
}
// RangeChoice picks whenInRange if input is within [min, max), else whenOut.
type RangeChoice struct {
Input DensityFunction
Min, Max float64
WhenInRange DensityFunction
WhenOutOfRange DensityFunction
}
func (r RangeChoice) Compute(c FunctionContext) float64 {
d := r.Input.Compute(c)
if d >= r.Min && d < r.Max {
return r.WhenInRange.Compute(c)
}
return r.WhenOutOfRange.Compute(c)
}
// ShiftedNoise samples a NormalNoise at coordinates scaled and offset by shift
// density functions (the workhorse of climate/terrain inputs).
type ShiftedNoise struct {
ShiftX, ShiftY, ShiftZ DensityFunction
XZScale, YScale float64
Noise *NormalNoise
}
func (s ShiftedNoise) Compute(c FunctionContext) float64 {
x := c.X*s.XZScale + s.ShiftX.Compute(c)
y := c.Y*s.YScale + s.ShiftY.Compute(c)
z := c.Z*s.XZScale + s.ShiftZ.Compute(c)
return s.Noise.GetValue(x, y, z)
}
// shiftNoise samples the offset noise at quarter scale, times four.
func shiftNoise(noise *NormalNoise, x, y, z float64) float64 {
return noise.GetValue(x*0.25, y*0.25, z*0.25) * 4.0
}
// ShiftA shifts along X/Z (used by shift_x): noise(x, 0, z).
type ShiftA struct{ Noise *NormalNoise }
func (s ShiftA) Compute(c FunctionContext) float64 { return shiftNoise(s.Noise, c.X, 0, c.Z) }
// ShiftB shifts with swapped axes (used by shift_z): noise(z, x, 0).
type ShiftB struct{ Noise *NormalNoise }
func (s ShiftB) Compute(c FunctionContext) float64 { return shiftNoise(s.Noise, c.Z, c.X, 0) }
// WeirdScaledSampler scales a noise sample by a rarity derived from an input
// density function (used by the spaghetti caves).
type WeirdScaledSampler struct {
Input DensityFunction
Noise *NormalNoise
Rarity func(float64) float64
}
func (w WeirdScaledSampler) Compute(c FunctionContext) float64 {
rarity := w.Rarity(w.Input.Compute(c))
return rarity * math.Abs(w.Noise.GetValue(c.X/rarity, c.Y/rarity, c.Z/rarity))
}
// SpaghettiRarity2D is the type_2 rarity mapping.
func SpaghettiRarity2D(v float64) float64 {
switch {
case v < -0.75:
return 0.5
case v < -0.5:
return 0.75
case v < 0.5:
return 1.0
case v < 0.75:
return 2.0
default:
return 3.0
}
}
// SpaghettiRarity3D is the type_1 rarity mapping.
func SpaghettiRarity3D(v float64) float64 {
switch {
case v < -0.5:
return 0.75
case v < 0.0:
return 1.0
case v < 0.5:
return 1.5
default:
return 2.0
}
}
func clamp(v, lo, hi float64) float64 {
if v < lo {
return lo
}
if v > hi {
return hi
}
return v
}
// clampedMap linearly maps v from [inMin,inMax] to [outMin,outMax], clamped.
func clampedMap(v, inMin, inMax, outMin, outMax float64) float64 {
if v <= inMin {
return outMin
}
if v >= inMax {
return outMax
}
t := (v - inMin) / (inMax - inMin)
return outMin + t*(outMax-outMin)
}

View file

@ -0,0 +1,7 @@
package worldgen
import "testing"
func BenchmarkFinalDensity(b *testing.B) {
od,_ := LoadOverworldFinalDensity(0)
b.ReportAllocs()
for i:=0;i<b.N;i++ { _ = od.Final.Compute(FunctionContext{X:float64(i&255),Y:64,Z:float64(i>>8)}) }
}

View file

@ -0,0 +1,117 @@
package worldgen
import "math"
// gradient is SimplexNoise.GRADIENT: the 16 (with repeats) 3D gradient vectors
// used by Perlin gradient hashing.
var gradient = [16][3]float64{
{1, 1, 0}, {-1, 1, 0}, {1, -1, 0}, {-1, -1, 0},
{1, 0, 1}, {-1, 0, 1}, {1, 0, -1}, {-1, 0, -1},
{0, 1, 1}, {0, -1, 1}, {0, 1, -1}, {0, -1, -1},
{1, 1, 0}, {0, -1, 1}, {-1, 1, 0}, {0, -1, -1},
}
// ImprovedNoise is a single Perlin noise octave (ImprovedNoise), with random
// offsets and a 256-entry permutation table.
type ImprovedNoise struct {
Xo, Yo, Zo float64
p [256]int
}
// NewImprovedNoise constructs an ImprovedNoise, consuming three doubles for the
// offsets and 256 bounded ints for the FisherYates permutation shuffle.
func NewImprovedNoise(r RandomSource) *ImprovedNoise {
n := &ImprovedNoise{
Xo: r.NextDouble() * 256.0,
Yo: r.NextDouble() * 256.0,
Zo: r.NextDouble() * 256.0,
}
for i := 0; i < 256; i++ {
n.p[i] = i
}
for i := 0; i < 256; i++ {
j := int(r.NextIntN(int32(256 - i)))
n.p[i], n.p[i+j] = n.p[i+j], n.p[i]
}
return n
}
func (n *ImprovedNoise) perm(i int) int { return n.p[i&255] & 255 }
// Noise samples 3D Perlin noise at (x, y, z).
func (n *ImprovedNoise) Noise(x, y, z float64) float64 {
return n.NoiseY(x, y, z, 0, 0)
}
// NoiseY is the 5-argument variant used by BlendedNoise: yScale/yFudge "smear"
// the Y gradient sampling while the smoothstep still uses the true Y fraction.
func (n *ImprovedNoise) NoiseY(x, y, z, yScale, yFudge float64) float64 {
d := x + n.Xo
e := y + n.Yo
f := z + n.Zo
i := int(math.Floor(d))
j := int(math.Floor(e))
k := int(math.Floor(f))
xr := d - float64(i)
yr := e - float64(j)
zr := f - float64(k)
var yrFudge float64
if yScale != 0.0 {
fudgeLimit := yr
if yFudge >= 0.0 && yFudge < yr {
fudgeLimit = yFudge
}
yrFudge = math.Floor(fudgeLimit/yScale+1.0e-7) * yScale
}
return n.sampleAndLerp(i, j, k, xr, yr-yrFudge, zr, yr)
}
// sampleAndLerp uses dyGrad for gradient hashing and dySmooth for the Y
// smoothstep (they differ only in the 5-arg "smear" path).
func (n *ImprovedNoise) sampleAndLerp(gx, gy, gz int, dx, dyGrad, dz, dySmooth float64) float64 {
dy := dyGrad
a := n.perm(gx)
b := n.perm(gx + 1)
aa := n.perm(a + gy)
ab := n.perm(a + gy + 1)
ba := n.perm(b + gy)
bb := n.perm(b + gy + 1)
d000 := grad(n.perm(aa+gz), dx, dy, dz)
d100 := grad(n.perm(ba+gz), dx-1, dy, dz)
d010 := grad(n.perm(ab+gz), dx, dy-1, dz)
d110 := grad(n.perm(bb+gz), dx-1, dy-1, dz)
d001 := grad(n.perm(aa+gz+1), dx, dy, dz-1)
d101 := grad(n.perm(ba+gz+1), dx-1, dy, dz-1)
d011 := grad(n.perm(ab+gz+1), dx, dy-1, dz-1)
d111 := grad(n.perm(bb+gz+1), dx-1, dy-1, dz-1)
r := smoothstep(dx)
s := smoothstep(dySmooth)
t := smoothstep(dz)
return lerp3(r, s, t, d000, d100, d010, d110, d001, d101, d011, d111)
}
// grad is GradientNoise: dot of the hashed gradient vector with (x, y, z).
func grad(hash int, x, y, z float64) float64 {
g := gradient[hash&15]
return g[0]*x + g[1]*y + g[2]*z
}
// smoothstep is Mth.smoothstep: 6t^5 - 15t^4 + 10t^3.
func smoothstep(t float64) float64 {
return t * t * t * (t*(t*6-15) + 10)
}
func lerp(t, a, b float64) float64 { return a + t*(b-a) }
func lerp2(tx, ty, v00, v10, v01, v11 float64) float64 {
return lerp(ty, lerp(tx, v00, v10), lerp(tx, v01, v11))
}
func lerp3(tx, ty, tz, v000, v100, v010, v110, v001, v101, v011, v111 float64) float64 {
return lerp(tz,
lerp2(tx, ty, v000, v100, v010, v110),
lerp2(tx, ty, v001, v101, v011, v111))
}

View file

@ -0,0 +1,32 @@
package worldgen
import (
"math"
"testing"
)
func TestImprovedNoiseVectors(t *testing.T) {
n := NewImprovedNoise(NewXoroshiro(42))
for _, c := range []struct {
name string
got, want float64
}{
{"xo", n.Xo, 190.83062484342904},
{"yo", n.Yo, 101.88674612737026},
{"zo", n.Zo, 151.323544791807},
} {
if math.Abs(c.got-c.want) > 1e-9 {
t.Fatalf("%s = %v, want %v", c.name, c.got, c.want)
}
}
pts := [][3]float64{{0.5, 0.5, 0.5}, {1.5, 2.5, 3.5}, {100.1, 64.0, -200.7}, {-12.3, 5.0, 7.7}}
want := []float64{0.078420838879807, 0.42903440359153633, 0.041997176611984766, -0.07019565638436798}
for i, p := range pts {
got := n.Noise(p[0], p[1], p[2])
if math.Abs(got-want[i]) > 1e-12 {
t.Fatalf("Noise%v = %v, want %v", p, got, want[i])
}
}
}

328
internal/worldgen/loader.go Normal file
View file

@ -0,0 +1,328 @@
package worldgen
import (
"embed"
"encoding/json"
"fmt"
"strings"
)
//go:embed data
var dataFS embed.FS
// Loader parses the embedded datapack density-function tree into evaluatable
// nodes, seeding noises through a RandomState. Shared sub-functions are cached
// by name so the DAG is built once.
type Loader struct {
rs *RandomState
dfCache map[string]DensityFunction
interpolated []*Interpolated
}
// OverworldDensity is the parsed final_density plus the set of Interpolated
// nodes that the generator samples on the cell grid.
type OverworldDensity struct {
Final DensityFunction
Interpolated []*Interpolated
// Climate parameters sampled by the biome finder. Read from the same
// noise_router as final_density. The router keys map to climate axes:
// temperature→Temperature, vegetation→Humidity, continents→Continentalness,
// erosion→Erosion, ridges→Weirdness, depth→Depth.
Temperature, Humidity, Continentalness, Erosion, Weirdness, Depth DensityFunction
}
// LoadOverworldFinalDensity builds the overworld final_density function for the
// given world seed.
func LoadOverworldFinalDensity(seed int64) (*OverworldDensity, error) {
l := &Loader{rs: NewRandomState(seed), dfCache: make(map[string]DensityFunction)}
var settings struct {
NoiseRouter map[string]json.RawMessage `json:"noise_router"`
}
if err := l.readJSON("data/overworld.json", &settings); err != nil {
return nil, err
}
var node any
if err := json.Unmarshal(settings.NoiseRouter["final_density"], &node); err != nil {
return nil, err
}
final, err := l.parseNode(node)
if err != nil {
return nil, err
}
od := &OverworldDensity{Final: final, Interpolated: l.interpolated}
// Parse the climate router keys used by the biome finder. Each key resolves
// to a density function via the same parseNode/loadRef machinery as
// final_density. A missing key is not fatal — the climate axis stays nil and
// the sampler treats it as a constant zero — but a parse error is.
climateKeys := map[string]*DensityFunction{
"temperature": &od.Temperature,
"vegetation": &od.Humidity,
"continents": &od.Continentalness,
"erosion": &od.Erosion,
"ridges": &od.Weirdness,
"depth": &od.Depth,
}
for key, dst := range climateKeys {
raw, ok := settings.NoiseRouter[key]
if !ok {
continue
}
var cn any
if err := json.Unmarshal(raw, &cn); err != nil {
return nil, fmt.Errorf("parse climate key %q: %w", key, err)
}
df, err := l.parseNode(cn)
if err != nil {
return nil, fmt.Errorf("climate key %q: %w", key, err)
}
*dst = df
}
return od, nil
}
func (l *Loader) readJSON(path string, v any) error {
b, err := dataFS.ReadFile(path)
if err != nil {
return fmt.Errorf("read %s: %w", path, err)
}
return json.Unmarshal(b, v)
}
// parseNode builds a density function from a decoded JSON value: a number is a
// constant, a string is a reference to another density-function file, and an
// object is a typed node.
func (l *Loader) parseNode(v any) (DensityFunction, error) {
switch t := v.(type) {
case float64:
return Constant(t), nil
case string:
return l.loadRef(t)
case map[string]any:
return l.parseObject(t)
default:
return nil, fmt.Errorf("unexpected density-function node %T", v)
}
}
// loadRef loads and caches a density function referenced by resource location.
func (l *Loader) loadRef(name string) (DensityFunction, error) {
if df, ok := l.dfCache[name]; ok {
return df, nil
}
path := "data/density_function/" + strings.TrimPrefix(name, "minecraft:") + ".json"
var node any
if err := l.readJSON(path, &node); err != nil {
return nil, err
}
df, err := l.parseNode(node)
if err != nil {
return nil, fmt.Errorf("in %s: %w", name, err)
}
l.dfCache[name] = df
return df, nil
}
func (l *Loader) parseObject(m map[string]any) (DensityFunction, error) {
typ, _ := m["type"].(string)
arg := func(k string) (DensityFunction, error) { return l.parseNode(m[k]) }
num := func(k string) float64 { f, _ := m[k].(float64); return f }
switch strings.TrimPrefix(typ, "minecraft:") {
case "add", "mul", "min", "max":
a, err := arg("argument1")
if err != nil {
return nil, err
}
b, err := arg("argument2")
if err != nil {
return nil, err
}
switch typ[10:] {
case "add":
return Add(a, b), nil
case "mul":
return Mul(a, b), nil
case "min":
return Min(a, b), nil
default:
return Max(a, b), nil
}
case "abs", "square", "cube", "half_negative", "quarter_negative", "squeeze":
a, err := arg("argument")
if err != nil {
return nil, err
}
return unaryByName(typ[10:], a), nil
case "clamp":
a, err := arg("input")
if err != nil {
return nil, err
}
return Clamp(a, num("min"), num("max")), nil
case "range_choice":
in, err := arg("input")
if err != nil {
return nil, err
}
whenIn, err := arg("when_in_range")
if err != nil {
return nil, err
}
whenOut, err := arg("when_out_of_range")
if err != nil {
return nil, err
}
return RangeChoice{in, num("min_inclusive"), num("max_exclusive"), whenIn, whenOut}, nil
case "y_clamped_gradient":
return YClampedGradient{num("from_y"), num("to_y"), num("from_value"), num("to_value")}, nil
case "noise":
n, err := l.noiseField(m["noise"])
if err != nil {
return nil, err
}
return NoiseDF{Noise: n, XZScale: num("xz_scale"), YScale: num("y_scale")}, nil
case "shifted_noise":
sx, err := arg("shift_x")
if err != nil {
return nil, err
}
sy, err := arg("shift_y")
if err != nil {
return nil, err
}
sz, err := arg("shift_z")
if err != nil {
return nil, err
}
n, err := l.noiseField(m["noise"])
if err != nil {
return nil, err
}
return ShiftedNoise{sx, sy, sz, num("xz_scale"), num("y_scale"), n}, nil
case "shift_a", "shift_b":
n, err := l.noiseField(m["argument"])
if err != nil {
return nil, err
}
if typ[10:] == "shift_a" {
return ShiftA{n}, nil
}
return ShiftB{n}, nil
case "old_blended_noise":
return l.rs.BlendedNoise(num("xz_scale"), num("y_scale"), num("xz_factor"), num("y_factor"), num("smear_scale_multiplier")), nil
case "weird_scaled_sampler":
in, err := arg("input")
if err != nil {
return nil, err
}
n, err := l.noiseField(m["noise"])
if err != nil {
return nil, err
}
rarity := SpaghettiRarity3D
if s, _ := m["rarity_value_mapper"].(string); s == "type_2" {
rarity = SpaghettiRarity2D
}
return WeirdScaledSampler{in, n, rarity}, nil
case "spline":
return l.parseSpline(m["spline"])
case "blend_alpha":
return Constant(1.0), nil // no blending: alpha = 1
case "blend_offset":
return Constant(0.0), nil // no blending: offset = 0
case "interpolated":
inner, err := arg("argument")
if err != nil {
return nil, err
}
n := &Interpolated{Inner: inner, Index: len(l.interpolated)}
l.interpolated = append(l.interpolated, n)
return n, nil
case "blend_density", "flat_cache", "cache_2d", "cache_once", "cache_all_in_cell":
// 2D caches and blend wrappers are value-preserving for per-point
// evaluation (recomputed rather than cached); only the 3D interpolated
// marker changes the result and is handled above.
return arg("argument")
default:
return nil, fmt.Errorf("unsupported density-function type %q", typ)
}
}
func unaryByName(name string, a DensityFunction) DensityFunction {
switch name {
case "abs":
return Abs(a)
case "square":
return Square(a)
case "cube":
return Cube(a)
case "half_negative":
return HalfNegative(a)
case "quarter_negative":
return QuarterNegative(a)
default: // squeeze
return Squeeze(a)
}
}
// noiseField resolves a noise reference (a "minecraft:<name>" key, or an object
// with a "noise" key) to a seeded NormalNoise.
func (l *Loader) noiseField(v any) (*NormalNoise, error) {
var key string
switch t := v.(type) {
case string:
key = t
case map[string]any:
key, _ = t["noise"].(string)
}
if key == "" {
return nil, fmt.Errorf("missing noise reference")
}
var params struct {
FirstOctave int `json:"firstOctave"`
Amplitudes []float64 `json:"amplitudes"`
}
path := "data/noise/" + strings.TrimPrefix(key, "minecraft:") + ".json"
if err := l.readJSON(path, &params); err != nil {
return nil, err
}
return l.rs.Noise(key, params.FirstOctave, params.Amplitudes), nil
}
func (l *Loader) parseSpline(v any) (DensityFunction, error) {
m, ok := v.(map[string]any)
if !ok {
return nil, fmt.Errorf("spline is not an object")
}
coord, err := l.parseNode(m["coordinate"])
if err != nil {
return nil, err
}
pts, _ := m["points"].([]any)
s := &CubicSpline{coordinate: coord}
for _, p := range pts {
pm := p.(map[string]any)
loc, _ := pm["location"].(float64)
der, _ := pm["derivative"].(float64)
val, err := l.parseSplineValue(pm["value"])
if err != nil {
return nil, err
}
s.locations = append(s.locations, float32(loc))
s.derivatives = append(s.derivatives, float32(der))
s.values = append(s.values, val)
}
return s, nil
}
// parseSplineValue handles a spline point's value: a number (constant), a raw
// nested spline (object with "coordinate"), or a density-function node.
func (l *Loader) parseSplineValue(v any) (DensityFunction, error) {
if m, ok := v.(map[string]any); ok {
if _, hasCoord := m["coordinate"]; hasCoord {
return l.parseSpline(m)
}
}
return l.parseNode(v)
}

View file

@ -0,0 +1,27 @@
package worldgen
import (
"math"
"testing"
)
// TestLoadOverworldDensity loads the full overworld final_density tree and
// checks it evaluates to finite values with the expected vertical sign trend
// (solid deep down, air high up).
func TestLoadOverworldDensity(t *testing.T) {
od, err := LoadOverworldFinalDensity(0)
if err != nil {
t.Fatal(err)
}
deep := od.Final.Compute(FunctionContext{X: 0, Y: -40, Z: 0})
high := od.Final.Compute(FunctionContext{X: 0, Y: 200, Z: 0})
if math.IsNaN(deep) || math.IsNaN(high) {
t.Fatal("final_density produced NaN")
}
if !(deep > 0) {
t.Fatalf("expected solid (positive) deep underground, got %v", deep)
}
if !(high < 0) {
t.Fatalf("expected air (negative) high up, got %v", high)
}
}

View file

@ -0,0 +1,45 @@
package worldgen
import (
"math"
"testing"
)
var noisePts = [][3]float64{{0.5, 0.5, 0.5}, {1.5, 2.5, 3.5}, {100.1, 64.0, -200.7}, {-12.3, 5.0, 7.7}}
func TestPerlinNoiseVectors(t *testing.T) {
p := NewPerlinNoise(NewXoroshiro(42), -3, []float64{1, 1, 1})
// Octave offsets validate the positional-factory MD5 seeding chain.
wantOff := [3][3]float64{
{77.66507715247522, 242.19573546755112, 173.13896594232995},
{217.18954032212207, 38.031116641324985, 29.079876730588552},
{245.20475284681797, 184.39003372698303, 174.76798991121467},
}
for i, w := range wantOff {
o := p.octaves[len(p.octaves)-1-i]
if math.Abs(o.Xo-w[0]) > 1e-9 || math.Abs(o.Yo-w[1]) > 1e-9 || math.Abs(o.Zo-w[2]) > 1e-9 {
t.Fatalf("octave[%d] offsets = %v,%v,%v want %v", i, o.Xo, o.Yo, o.Zo, w)
}
}
want := []float64{0.14203479195685254, -0.2004829169283356, 0.10959099511010406, 0.02936359094335893}
for i, pt := range noisePts {
if got := p.GetValue(pt[0], pt[1], pt[2]); math.Abs(got-want[i]) > 1e-12 {
t.Fatalf("PerlinNoise%v = %v, want %v", pt, got, want[i])
}
}
}
func TestNormalNoiseVectors(t *testing.T) {
n := NewNormalNoise(NewXoroshiro(42), -3, []float64{1, 1, 1})
if math.Abs(n.MaxValue()-5.0) > 1e-12 {
t.Fatalf("maxValue = %v, want 5.0", n.MaxValue())
}
want := []float64{0.08875533507209354, -0.1338868205633287, -0.18990226335882565, 0.008404386832678992}
for i, pt := range noisePts {
if got := n.GetValue(pt[0], pt[1], pt[2]); math.Abs(got-want[i]) > 1e-12 {
t.Fatalf("NormalNoise%v = %v, want %v", pt, got, want[i])
}
}
}

View file

@ -0,0 +1,49 @@
package worldgen
// normalInputFactor is NormalNoise.INPUT_FACTOR, the frequency offset applied
// to the second Perlin field so the two octave stacks don't align.
const normalInputFactor = 1.0181268882175227
// NormalNoise combines two PerlinNoise fields, scaled so the result has a
// normalized deviation. This is the noise type referenced by density functions.
type NormalNoise struct {
first *PerlinNoise
second *PerlinNoise
valueFactor float64
maxValue float64
}
// NewNormalNoise builds a NormalNoise from the same parameters vanilla uses:
// two PerlinNoise stacks drawn sequentially from r, plus a value factor derived
// from the span of non-zero amplitudes.
func NewNormalNoise(r RandomSource, firstOctave int, amplitudes []float64) *NormalNoise {
n := &NormalNoise{
first: NewPerlinNoise(r, firstOctave, amplitudes),
second: NewPerlinNoise(r, firstOctave, amplitudes),
}
min, max := len(amplitudes), 0
for i, a := range amplitudes {
if a != 0 {
if i < min {
min = i
}
if i > max {
max = i
}
}
}
expectedDeviation := 0.1 * (1.0 + 1.0/float64(max-min+1))
n.valueFactor = (1.0 / 6.0) / expectedDeviation
n.maxValue = (n.first.MaxValue() + n.second.MaxValue()) * n.valueFactor
return n
}
// GetValue samples the combined noise at (x, y, z).
func (n *NormalNoise) GetValue(x, y, z float64) float64 {
return (n.first.GetValue(x, y, z) +
n.second.GetValue(x*normalInputFactor, y*normalInputFactor, z*normalInputFactor)) * n.valueFactor
}
// MaxValue returns the theoretical maximum magnitude.
func (n *NormalNoise) MaxValue() float64 { return n.maxValue }

View file

@ -0,0 +1,122 @@
package worldgen
import (
"math"
"strconv"
)
// PerlinNoise is an octave sum of ImprovedNoise layers, matching the official
// PerlinNoise (non-legacy factory path).
type PerlinNoise struct {
octaves []*ImprovedNoise // entries may be nil for zero amplitudes
amplitudes []float64
firstOctave int
lowestFreqInputFactor float64
lowestFreqValueFactor float64
maxValue float64
}
// NewPerlinNoise builds a PerlinNoise over the given amplitudes starting at
// firstOctave. Each octave is seeded by the positional factory's hash of
// "octave_<n>", exactly as vanilla does.
func NewPerlinNoise(r RandomSource, firstOctave int, amplitudes []float64) *PerlinNoise {
count := len(amplitudes)
p := &PerlinNoise{
octaves: make([]*ImprovedNoise, count),
amplitudes: amplitudes,
firstOctave: firstOctave,
}
factory := r.ForkPositional()
for k := 0; k < count; k++ {
if amplitudes[k] != 0 {
octave := firstOctave + k
p.octaves[k] = NewImprovedNoise(factory.FromHashOf("octave_" + strconv.Itoa(octave)))
}
}
p.lowestFreqInputFactor = math.Pow(2, float64(firstOctave))
p.lowestFreqValueFactor = math.Pow(2, float64(count-1)) / (math.Pow(2, float64(count)) - 1)
p.maxValue = p.edgeValue(2.0)
return p
}
// NewLegacyPerlinNoise builds a PerlinNoise with the legacy (non-positional)
// octave seeding used by BlendedNoise: octaves are drawn sequentially from r,
// starting with the zero octave, then descending. Skipped (zero-amplitude)
// octaves consume a fixed number of draws.
func NewLegacyPerlinNoise(r RandomSource, firstOctave int, amplitudes []float64) *PerlinNoise {
octaves := len(amplitudes)
zeroIdx := -firstOctave
p := &PerlinNoise{
octaves: make([]*ImprovedNoise, octaves),
amplitudes: amplitudes,
firstOctave: firstOctave,
}
zeroOctave := NewImprovedNoise(r) // always drawn
if zeroIdx >= 0 && zeroIdx < octaves && amplitudes[zeroIdx] != 0 {
p.octaves[zeroIdx] = zeroOctave
}
for i := zeroIdx - 1; i >= 0; i-- {
if i < octaves && amplitudes[i] != 0 {
p.octaves[i] = NewImprovedNoise(r)
} else {
r.ConsumeCount(262) // skipOctave
}
}
p.lowestFreqInputFactor = math.Pow(2, float64(-zeroIdx))
p.lowestFreqValueFactor = math.Pow(2, float64(octaves-1)) / (math.Pow(2, float64(octaves)) - 1)
p.maxValue = p.edgeValue(2.0)
return p
}
// GetOctaveNoise returns the i-th octave from the high-frequency end (vanilla's
// reverse indexing), or nil if that octave's amplitude is zero.
func (p *PerlinNoise) GetOctaveNoise(i int) *ImprovedNoise {
return p.octaves[len(p.octaves)-1-i]
}
// MaxBrokenValue is PerlinNoise.maxBrokenValue: edgeValue(yScale + 2).
func (p *PerlinNoise) MaxBrokenValue(yScale float64) float64 { return p.edgeValue(yScale + 2.0) }
// GetValue samples the octave sum at (x, y, z).
func (p *PerlinNoise) GetValue(x, y, z float64) float64 { return p.GetValueY(x, y, z, 0, 0) }
// GetValueY is the 5-argument octave sum used with Y-smearing.
func (p *PerlinNoise) GetValueY(x, y, z, yScale, yFudge float64) float64 {
d := 0.0
inputFactor := p.lowestFreqInputFactor
valueFactor := p.lowestFreqValueFactor
for i, oct := range p.octaves {
if oct != nil {
g := oct.NoiseY(wrap(x*inputFactor), wrap(y*inputFactor), wrap(z*inputFactor),
yScale*inputFactor, yFudge*inputFactor)
d += p.amplitudes[i] * g * valueFactor
}
inputFactor *= 2.0
valueFactor /= 2.0
}
return d
}
// MaxValue returns the theoretical maximum magnitude.
func (p *PerlinNoise) MaxValue() float64 { return p.maxValue }
func (p *PerlinNoise) edgeValue(x float64) float64 {
e := 0.0
valueFactor := p.lowestFreqValueFactor
for i, oct := range p.octaves {
if oct != nil {
e += p.amplitudes[i] * x * valueFactor
}
valueFactor /= 2.0
}
return e
}
// wrap is PerlinNoise.wrap: folds large coordinates back near the origin to
// preserve floating-point precision. The constant is 2^25.
func wrap(value float64) float64 {
const period = 3.3554432e7
return value - float64(int64(math.Floor(value/period+0.5)))*period
}

220
internal/worldgen/random.go Normal file
View file

@ -0,0 +1,220 @@
// Package worldgen ports Minecraft's noise-based terrain generation: the random
// sources, Perlin/normal noise, and (later) the density-function interpreter.
//
// Implementations mirror the official 26.1.2 server bit-for-bit; values are
// verified against vectors captured from the real classes (see random_test.go).
package worldgen
import (
"crypto/md5"
"encoding/binary"
"math/bits"
)
// md5Seed mirrors RandomSupport.seedFromHashOf: the MD5 digest of name split
// into two big-endian 64-bit halves.
func md5Seed(name string) (lo, hi uint64) {
sum := md5.Sum([]byte(name))
return binary.BigEndian.Uint64(sum[0:8]), binary.BigEndian.Uint64(sum[8:16])
}
// Mixing constants from RandomSupport.
const (
goldenRatio64 = 0x9E3779B97F4A7C15
silverRatio64 = 0x6A09E667F3BCC909
)
// mixStafford13 is RandomSupport.mixStafford13, a 64-bit avalanche mix.
func mixStafford13(z uint64) uint64 {
z = (z ^ (z >> 30)) * 0xBF58476D1CE4E5B9
z = (z ^ (z >> 27)) * 0x94D049BB133111EB
return z ^ (z >> 31)
}
// seed128 is RandomSupport.Seed128bit.
type seed128 struct{ lo, hi uint64 }
// upgradeSeedTo128bit mirrors RandomSupport.upgradeSeedTo128bit: derive a
// 128-bit seed from a 64-bit one, then avalanche-mix both halves.
func upgradeSeedTo128bit(seed uint64) seed128 {
lo := seed ^ silverRatio64
hi := lo + goldenRatio64
return seed128{mixStafford13(lo), mixStafford13(hi)}
}
// RandomSource is the subset of Minecraft's RandomSource we use.
type RandomSource interface {
NextLong() int64
NextInt() int32
NextIntN(bound int32) int32
NextDouble() float64
NextFloat() float32
NextBoolean() bool
// ForkPositional returns a factory for deriving deterministic child sources
// (used to seed noise octaves by name).
ForkPositional() PositionalRandomFactory
// ConsumeCount advances the generator by n draws (used to skip noise octaves).
ConsumeCount(n int)
}
// PositionalRandomFactory derives child RandomSources deterministically.
type PositionalRandomFactory interface {
// FromHashOf seeds a child source from the MD5 hash of name.
FromHashOf(name string) RandomSource
}
// --- Xoroshiro128++ ---
// Xoroshiro is XoroshiroRandomSource backed by Xoroshiro128PlusPlus.
type Xoroshiro struct{ lo, hi uint64 }
// NewXoroshiro seeds a Xoroshiro source from a 64-bit seed.
func NewXoroshiro(seed int64) *Xoroshiro {
s := upgradeSeedTo128bit(uint64(seed))
return newXoroshiroFrom(s.lo, s.hi)
}
func newXoroshiroFrom(lo, hi uint64) *Xoroshiro {
if lo == 0 && hi == 0 {
lo, hi = goldenRatio64, silverRatio64
}
return &Xoroshiro{lo: lo, hi: hi}
}
// nextBits advances the Xoroshiro128++ state and returns the raw 64-bit output.
func (x *Xoroshiro) nextBits() uint64 {
l, m := x.lo, x.hi
n := bits.RotateLeft64(l+m, 17) + l
m ^= l
x.lo = bits.RotateLeft64(l, 49) ^ m ^ (m << 21)
x.hi = bits.RotateLeft64(m, 28)
return n
}
func (x *Xoroshiro) NextLong() int64 { return int64(x.nextBits()) }
func (x *Xoroshiro) NextInt() int32 { return int32(x.nextBits()) }
// NextIntN mirrors XoroshiroRandomSource.nextInt(bound): Lemire's multiply-shift
// with rejection for an unbiased result.
func (x *Xoroshiro) NextIntN(bound int32) int32 {
l := uint64(uint32(x.NextInt()))
m := l * uint64(bound)
low := uint32(m)
if low < uint32(bound) {
threshold := uint32(-bound) % uint32(bound)
for low < threshold {
l = uint64(uint32(x.NextInt()))
m = l * uint64(bound)
low = uint32(m)
}
}
return int32(m >> 32)
}
func (x *Xoroshiro) NextDouble() float64 {
return float64(x.nextBits()>>11) * 0x1.0p-53
}
func (x *Xoroshiro) NextFloat() float32 {
return float32(x.nextBits()>>40) * 0x1.0p-24
}
func (x *Xoroshiro) NextBoolean() bool { return x.nextBits()&1 != 0 }
// ConsumeCount advances the underlying generator n times.
func (x *Xoroshiro) ConsumeCount(n int) {
for i := 0; i < n; i++ {
x.nextBits()
}
}
// ForkPositional consumes two outputs to seed a positional factory.
func (x *Xoroshiro) ForkPositional() PositionalRandomFactory {
return &xoroshiroPositional{seedLo: x.nextBits(), seedHi: x.nextBits()}
}
type xoroshiroPositional struct{ seedLo, seedHi uint64 }
// FromHashOf mirrors XoroshiroPositionalRandomFactory.fromHashOf: MD5 the name
// into a 128-bit seed, XOR with the factory seed, no avalanche mixing.
func (f *xoroshiroPositional) FromHashOf(name string) RandomSource {
lo, hi := md5Seed(name)
return newXoroshiroFrom(lo^f.seedLo, hi^f.seedHi)
}
// --- Legacy LCG (java.util.Random) ---
const (
lcgMultiplier = 0x5DEECE66D
lcgAddend = 0xB
lcgMask = (1 << 48) - 1
)
// Legacy is LegacyRandomSource: java.util.Random's 48-bit LCG.
type Legacy struct{ seed uint64 }
// NewLegacy seeds a Legacy source, applying Java's seed scramble.
func NewLegacy(seed int64) *Legacy {
return &Legacy{seed: (uint64(seed) ^ lcgMultiplier) & lcgMask}
}
// next returns the top `b` bits of the next LCG state.
func (r *Legacy) next(b uint) int32 {
r.seed = (r.seed*lcgMultiplier + lcgAddend) & lcgMask
return int32(r.seed >> (48 - b))
}
func (r *Legacy) NextInt() int32 { return r.next(32) }
func (r *Legacy) NextLong() int64 { return int64(r.next(32))<<32 + int64(r.next(32)) }
// NextIntN mirrors BitRandomSource.nextInt(bound): power-of-two fast path,
// otherwise modulo with rejection to avoid bias.
func (r *Legacy) NextIntN(bound int32) int32 {
if bound&-bound == bound { // power of two
return int32((int64(bound) * int64(r.next(31))) >> 31)
}
for {
j := r.next(31)
k := j % bound
if j-k+(bound-1) >= 0 {
return k
}
}
}
func (r *Legacy) NextDouble() float64 {
hi := int64(r.next(26))
lo := int64(r.next(27))
return float64(hi<<27+lo) * 0x1.0p-53
}
func (r *Legacy) NextFloat() float32 { return float32(r.next(24)) * 0x1.0p-24 }
func (r *Legacy) NextBoolean() bool { return r.next(1) != 0 }
// ConsumeCount advances the LCG n times.
func (r *Legacy) ConsumeCount(n int) {
for i := 0; i < n; i++ {
r.next(32)
}
}
// ForkPositional mirrors LegacyRandomSource.forkPositional.
func (r *Legacy) ForkPositional() PositionalRandomFactory {
return &legacyPositional{seed: uint64(r.NextLong())}
}
type legacyPositional struct{ seed uint64 }
// FromHashOf mirrors LegacyPositionalRandomFactory.fromHashOf: seed from the
// Java String.hashCode of name XORed with the factory seed.
func (f *legacyPositional) FromHashOf(name string) RandomSource {
return NewLegacy(int64(int32(javaStringHashCode(name))) ^ int64(f.seed))
}
func javaStringHashCode(s string) int32 {
var h int32
for i := 0; i < len(s); i++ {
h = 31*h + int32(s[i])
}
return h
}

View file

@ -0,0 +1,73 @@
package worldgen
import (
"math"
"testing"
)
// Reference vectors captured from the official 26.1.2 server classes
// (XoroshiroRandomSource / LegacyRandomSource seeded with 42), consumed in
// order: 5x NextLong, 5x NextIntN(100), then doubles/floats.
func TestXoroshiroVectors(t *testing.T) {
x := NewXoroshiro(42)
wantLong := []int64{
-4695948378737616609, 7341713790291473579, -7542733514721318211,
4888889476139319686, 8419651034331256779,
}
for i, w := range wantLong {
if got := x.NextLong(); got != w {
t.Fatalf("NextLong[%d] = %d, want %d", i, got, w)
}
}
wantInt := []int32{28, 93, 40, 73, 75}
for i, w := range wantInt {
if got := x.NextIntN(100); got != w {
t.Fatalf("NextIntN[%d] = %d, want %d", i, got, w)
}
}
wantDouble := []float64{0.4990607418038817, 0.4922907789978952, 0.09296765457327383}
for i, w := range wantDouble {
if got := x.NextDouble(); math.Abs(got-w) > 1e-15 {
t.Fatalf("NextDouble[%d] = %v, want %v", i, got, w)
}
}
wantFloat := []float32{0.1058414, 0.583224, 0.34108514}
for i, w := range wantFloat {
if got := x.NextFloat(); math.Abs(float64(got-w)) > 1e-6 {
t.Fatalf("NextFloat[%d] = %v, want %v", i, got, w)
}
}
}
func TestLegacyVectors(t *testing.T) {
r := NewLegacy(42)
wantLong := []int64{
-5025562857975149833, -5843495416241995736, 5694868678511409995,
5111195811822994797, -6169532649852302182,
}
for i, w := range wantLong {
if got := r.NextLong(); got != w {
t.Fatalf("NextLong[%d] = %d, want %d", i, got, w)
}
}
wantInt := []int32{82, 2, 76, 92, 76}
for i, w := range wantInt {
if got := r.NextIntN(100); got != w {
t.Fatalf("NextIntN[%d] = %d, want %d", i, got, w)
}
}
wantDouble := []float64{0.6904257605024213, 0.762090173108902, 0.998178600062844}
for i, w := range wantDouble {
if got := r.NextDouble(); math.Abs(got-w) > 1e-15 {
t.Fatalf("NextDouble[%d] = %v, want %v", i, got, w)
}
}
}

Some files were not shown because too many files have changed in this diff Show more