RegionIO/internal/world/terrain.go
Master290 a7bb9496ae 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.
2026-06-24 00:32:51 +03:00

88 lines
2.2 KiB
Go

package world
import (
"sync"
"regionio/internal/worldgen"
)
// SeaLevel is the water surface height for generated terrain.
const SeaLevel = 63
// NewTerrainGenerator returns a chunk generator backed by a density function.
// The density tree is built once (shared, read-only noise state) and sampled
// per block.
func NewTerrainGenerator(seed int64) Generator {
density := worldgen.SimpleTerrain(seed)
return func(cx, cz int32) *Chunk {
return generateFromDensity(density, cx, cz)
}
}
// generateFromDensity fills a chunk by sampling the density function (>0 is
// solid). The per-column sampling is the expensive part and is run in parallel
// across the 16 x-rows; chunk assembly is sequential to avoid racing on lazy
// section allocation. Sampling the density only reads shared noise state, so it
// is safe to run concurrently.
func generateFromDensity(d worldgen.DensityFunction, cx, cz int32) *Chunk {
c := NewChunk(cx, cz, BiomePlains)
var columns [16][16][WorldHeight]uint16
var wg sync.WaitGroup
for lx := 0; lx < 16; lx++ {
wg.Add(1)
go func(lx int) {
defer wg.Done()
for lz := 0; lz < 16; lz++ {
wx := float64(int(cx)*16 + lx)
wz := float64(int(cz)*16 + lz)
computeColumn(d, wx, wz, &columns[lx][lz])
}
}(lx)
}
wg.Wait()
for lx := 0; lx < 16; lx++ {
for lz := 0; lz < 16; lz++ {
col := &columns[lx][lz]
for i := 0; i < WorldHeight; i++ {
if s := col[i]; s != StateAir {
c.SetBlock(lx, MinY+i, lz, s)
}
}
}
}
return c
}
// computeColumn fills out with the block state for each Y in one column.
func computeColumn(d worldgen.DensityFunction, wx, wz float64, out *[WorldHeight]uint16) {
var solid [WorldHeight]bool
top := -1
for i := 0; i < WorldHeight; i++ {
y := MinY + i
if d.Compute(worldgen.FunctionContext{X: wx, Y: float64(y), Z: wz}) > 0 {
solid[i] = true
top = i
}
}
for i := 0; i < WorldHeight; i++ {
y := MinY + i
switch {
case y == MinY:
out[i] = StateBedrock
case solid[i]:
switch {
case i == top && y >= SeaLevel:
out[i] = StateGrass
case i > top-4:
out[i] = StateDirt
default:
out[i] = StateStone
}
case y < SeaLevel:
out[i] = StateWater
}
}
}