PlaceRuinedPortalPiece runs the template through the decoded processor stack — positional Legacy streams per cell drive the gold-to-air, lava-to-magma, netherrack-to-magma and block-age mossiness rolls, the features-cannot-replace guard skips protected cells, and template lava stays when it lands in existing lava. Drip columns below the portal draw from a documented approximation of the shared decoration stream; spreadNetherrack is not needed for the underground setups. The placement chain is now proven end to end: chunk (1,0) reproduces vanilla's saved start (portal_6, CLOCKWISE_90, NONE, air pocket at (16,12,0)) and the fixture parity ticks up to 98.033% with biomes and heightmaps still exact. generatorVersion bumps to 23.
118 lines
2.7 KiB
Go
118 lines
2.7 KiB
Go
package world
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"regionio/internal/worldgen"
|
|
)
|
|
|
|
func TestScheduledRegionOresAreDeterministic(t *testing.T) {
|
|
makeRegion := func() *decorationRegion {
|
|
var chunks []*Chunk
|
|
for cx := int32(-1); cx <= 1; cx++ {
|
|
for cz := int32(-1); cz <= 1; cz++ {
|
|
chunk := NewChunk(cx, cz, BiomePlains)
|
|
for y := MinY; y < MinY+WorldHeight; y++ {
|
|
for x := 0; x < 16; x++ {
|
|
for z := 0; z < 16; z++ {
|
|
chunk.setBlockRaw(x, y, z, StateStone)
|
|
}
|
|
}
|
|
}
|
|
chunks = append(chunks, chunk)
|
|
}
|
|
}
|
|
region, err := newDecorationRegion(chunks)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := region.setSource(0, 0); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return region
|
|
}
|
|
|
|
a, b := makeRegion(), makeRegion()
|
|
if err := a.placeScheduledOres(12345); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := b.placeScheduledOres(12345); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
changed := 0
|
|
for cx := int32(-1); cx <= 1; cx++ {
|
|
for cz := int32(-1); cz <= 1; cz++ {
|
|
left := a.chunks[[2]int32{cx, cz}]
|
|
right := b.chunks[[2]int32{cx, cz}]
|
|
for y := MinY; y < MinY+WorldHeight; y++ {
|
|
for x := 0; x < 16; x++ {
|
|
for z := 0; z < 16; z++ {
|
|
got := left.GetBlock(x, y, z)
|
|
if got != right.GetBlock(x, y, z) {
|
|
t.Fatalf("regions differ at chunk (%d,%d) block (%d,%d,%d)", cx, cz, x, y, z)
|
|
}
|
|
if got != StateStone {
|
|
changed++
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if changed == 0 {
|
|
t.Fatal("scheduled ore pass changed no blocks")
|
|
}
|
|
}
|
|
|
|
func TestScheduledRegionOreReplayIsDeterministic(t *testing.T) {
|
|
makeRegion := func() *decorationRegion {
|
|
var chunks []*Chunk
|
|
for cx := int32(-2); cx <= 2; cx++ {
|
|
for cz := int32(-2); cz <= 2; cz++ {
|
|
chunk := NewChunk(cx, cz, BiomePlains)
|
|
for y := MinY; y < MinY+WorldHeight; y++ {
|
|
for x := 0; x < 16; x++ {
|
|
for z := 0; z < 16; z++ {
|
|
chunk.setBlockRaw(x, y, z, StateStone)
|
|
}
|
|
}
|
|
}
|
|
chunks = append(chunks, chunk)
|
|
}
|
|
}
|
|
region, err := newDecorationRegion(chunks)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return region
|
|
}
|
|
|
|
a, b := makeRegion(), makeRegion()
|
|
od, err := worldgen.LoadOverworldFinalDensity(12345)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := a.replayScheduledOres(od, 12345, 0, 0); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := b.replayScheduledOres(od, 12345, 0, 0); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for cx := int32(-2); cx <= 2; cx++ {
|
|
for cz := int32(-2); cz <= 2; cz++ {
|
|
left := a.chunks[[2]int32{cx, cz}]
|
|
right := b.chunks[[2]int32{cx, cz}]
|
|
for y := MinY; y < MinY+WorldHeight; y++ {
|
|
for x := 0; x < 16; x++ {
|
|
for z := 0; z < 16; z++ {
|
|
if left.GetBlock(x, y, z) != right.GetBlock(x, y, z) {
|
|
t.Fatalf("replay differs at chunk (%d,%d), block (%d,%d,%d)", cx, cz, x, y, z)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|