RegionIO/internal/world/feature_scheduler.go
Daniar Mannanov 790d7e3144 world: replay stage-3 monster rooms
Ports MonsterRoomFeature from bytecode with vanilla draw order and loop
structure: shell validation (solid floor and ceiling, one to five open
side columns), interior cave_air carving, cobblestone walls with the
mossy three-in-four floor roll drawn only where vanilla draws it, chest
placement faced away from its single opaque neighbour with the loot-seed
long consumed even though block entities are not modelled yet, and the
spawner's one-draw mob pick. Writes go through Feature.safeSetBlock
semantics against #minecraft:features_cannot_replace, captured verbatim
from the jar into the embedded datapack data.

The pass slots between geodes and the ores so the rooms' cave_air
pockets exist before ore ellipsoids roll their air-exposure discards.
The committed fixture's four chunks happen to contain no dungeons, so
measured parity holds at 98.028%, but the generator's output can differ
elsewhere: generatorVersion bumps to 22.
2026-08-26 02:49:33 +03:00

53 lines
2.3 KiB
Go

package world
import "fmt"
// decorationSource is a source chunk whose feature pass may inspect or write a
// target chunk. Vanilla FEATURES has a one-chunk block-state write radius.
type decorationSource struct {
X, Z int32
}
// decorationSources returns source chunks in deterministic X-major/Z-minor
// order. Replaying all nine against one mutable 3x3 terrain region makes target
// output independent of cache request order while preserving each source's own
// decoration seed and placement origin.
func decorationSources(targetX, targetZ int32) []decorationSource {
sources := make([]decorationSource, 0, 9)
for sourceX := targetX - 1; sourceX <= targetX+1; sourceX++ {
for sourceZ := targetZ - 1; sourceZ <= targetZ+1; sourceZ++ {
sources = append(sources, decorationSource{X: sourceX, Z: sourceZ})
}
}
return sources
}
// replayScheduledOres replays each source center in deterministic order into a
// shared region. The region must contain the target's radius-two base terrain;
// source passes themselves may write only within their own radius-one window.
// This is an explicit canonical order for isolated generation. It must not
// replace the production path until parity evidence confirms that it matches
// vanilla's chunk-status scheduling order.
func (r *decorationRegion) replayScheduledOres(seed int64, targetX, targetZ int32) error {
for _, source := range decorationSources(targetX, targetZ) {
if err := r.setSource(source.X, source.Z); err != nil {
return err
}
if err := r.placeScheduledGeodes(seed); err != nil {
return fmt.Errorf("world: replay source geodes (%d,%d): %w", source.X, source.Z, err)
}
// Stage 3 runs before the ores on purpose: the rooms' cave_air pockets
// are what vanilla's ore ellipsoids roll their air-exposure discards
// against.
if err := r.placeScheduledMonsterRooms(seed); err != nil {
return fmt.Errorf("world: replay source monster rooms (%d,%d): %w", source.X, source.Z, err)
}
if err := r.placeScheduledUndergroundOresStage(seed); err != nil {
return fmt.Errorf("world: replay source underground ores (%d,%d): %w", source.X, source.Z, err)
}
if err := r.placeScheduledVegetationPatches(seed); err != nil {
return fmt.Errorf("world: replay source vegetation patches (%d,%d): %w", source.X, source.Z, err)
}
}
return nil
}