world: port ruined portal generation point and expose pre-carve terrain

generateBaseTerrain now factors the noise+surface stage out of chunk
generation so structure placement can read pre-carve heights exactly
like vanilla's STRUCTURE_STARTS stage. RuinedPortalGenerationPoint
replays the ruined_portals set: the weighted pick across all seven
variants on its own stream, each attempt's setup/template/rotation/
mirror draws on a fresh GenerationContext stream (makeRandom reseeds
per attempt, confirmed from bytecode), findSuitableY with corner-column
settle scan against those heights, and the post-draw 3D-biome filter at
quart-snapped coordinates.

Known gap, documented in STRUCTURE_NOTES.md: no variant is accepted in
the window around the fixture's portal yet while vanilla placed one;
the biome/surface inputs need a vanilla-side probe to bisect. Not wired
into the production replay until that closes.
This commit is contained in:
Daniar Mannanov 2026-08-26 13:56:51 +03:00
parent 6e04bd8bbc
commit 798a3ec7b4
6 changed files with 454 additions and 31 deletions

View file

@ -24,10 +24,28 @@ type TemplateBlockInfo struct {
HasNBT bool
}
// ResolvedTemplate is a parsed .nbt structure with every palette entry mapped
// to concrete state IDs through resolve.
// LoadResolvedTemplate reads a template file from disk.
func LoadResolvedTemplate(path string, resolve func(name string, props map[string]string) (uint16, bool)) ([]TemplateBlockInfo, [3]int, error) {
raw, err := readMaybeGzip(path)
raw, err := os.ReadFile(path)
if err != nil {
return nil, [3]int{}, err
}
raw, err = readMaybeGzipBytes(raw)
if err != nil {
return nil, [3]int{}, err
}
return LoadResolvedTemplateBytes(raw, resolve)
}
// EmbeddedStructureTemplate reads a template from the embedded datapack data
// by its resource-path name, e.g. "ruined_portal/portal_1".
func EmbeddedStructureTemplate(name string) ([]byte, error) {
return dataFS.ReadFile("data/structure_template/" + name + ".nbt")
}
// LoadResolvedTemplateBytes parses template NBT already in memory.
func LoadResolvedTemplateBytes(raw []byte, resolve func(name string, props map[string]string) (uint16, bool)) ([]TemplateBlockInfo, [3]int, error) {
raw, err := readMaybeGzipBytes(raw)
if err != nil {
return nil, [3]int{}, err
}
@ -121,11 +139,7 @@ func LoadResolvedTemplate(path string, resolve func(name string, props map[strin
return blocks, size, nil
}
func readMaybeGzip(path string) ([]byte, error) {
b, err := os.ReadFile(path)
if err != nil {
return nil, err
}
func readMaybeGzipBytes(b []byte) ([]byte, error) {
if len(b) >= 2 && b[0] == 0x1f && b[1] == 0x8b {
gr, err := gzip.NewReader(bytes.NewReader(b))
if err != nil {
@ -174,3 +188,4 @@ func MthGetSeed(x, y, z int) int64 {
}