Preserve lazy feature placement RNG
This commit is contained in:
parent
aeb7e4d2c2
commit
798016b055
4 changed files with 98 additions and 13 deletions
|
|
@ -49,12 +49,11 @@ func (r *decorationRegion) placeScheduledOresFiltered(seed int64, biomeOrder []s
|
|||
context := r.placementContext(func(position worldgen.FeaturePosition) bool {
|
||||
return r.biomeAllowsFeature(set, scheduled.Name, undergroundOresStage, position)
|
||||
})
|
||||
positions, err := set.PlacementPositions(scheduled.Name, random, origin, context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, position := range positions {
|
||||
if err := set.ForEachPlacementPosition(scheduled.Name, random, origin, context, func(position worldgen.FeaturePosition) error {
|
||||
placeOreEllipsoidRegion(r, random, position.X, position.Y, position.Z, config.Size, config.DiscardAirExposure, targets)
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -540,16 +540,26 @@ func (s *FeatureSet) Placement(name string) (PlacementPlan, error) {
|
|||
// The recursive walk mirrors Stream.flatMap: every repeated position completes
|
||||
// the remaining chain before the next repeated position consumes random draws.
|
||||
func (s *FeatureSet) PlacementPositions(name string, r RandomSource, origin FeaturePosition, context PlacementContext) ([]FeaturePosition, error) {
|
||||
var result []FeaturePosition
|
||||
err := s.ForEachPlacementPosition(name, r, origin, context, func(position FeaturePosition) error {
|
||||
result = append(result, position)
|
||||
return nil
|
||||
})
|
||||
return result, err
|
||||
}
|
||||
|
||||
// ForEachPlacementPosition preserves vanilla's lazy placement stream: the
|
||||
// configured feature consumes random draws for one position before modifiers
|
||||
// produce the next repeated position.
|
||||
func (s *FeatureSet) ForEachPlacementPosition(name string, r RandomSource, origin FeaturePosition, context PlacementContext, visit func(FeaturePosition) error) error {
|
||||
placed, ok := s.Placed[name]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("worldgen: placed feature %s missing", name)
|
||||
return fmt.Errorf("worldgen: placed feature %s missing", name)
|
||||
}
|
||||
var result []FeaturePosition
|
||||
var apply func(int, FeaturePosition) error
|
||||
apply = func(index int, position FeaturePosition) error {
|
||||
if index == len(placed.Placement) {
|
||||
result = append(result, position)
|
||||
return nil
|
||||
return visit(position)
|
||||
}
|
||||
modifier := placed.Placement[index]
|
||||
next := func(value FeaturePosition) error { return apply(index+1, value) }
|
||||
|
|
@ -671,10 +681,7 @@ func (s *FeatureSet) PlacementPositions(name string, r RandomSource, origin Feat
|
|||
return fmt.Errorf("worldgen: %s unsupported executable placement modifier %q", name, modifier.Type)
|
||||
}
|
||||
}
|
||||
if err := apply(0, origin); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
return apply(0, origin)
|
||||
}
|
||||
|
||||
type placementIntProvider struct {
|
||||
|
|
|
|||
|
|
@ -248,6 +248,45 @@ func TestPlacementPositionsPreservesModifierOrder(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestForEachPlacementPositionInterleavesVisitorRandomDraws(t *testing.T) {
|
||||
modifier := func(raw string) PlacementModifier {
|
||||
var value PlacementModifier
|
||||
value.Raw = json.RawMessage(raw)
|
||||
if err := json.Unmarshal(value.Raw, &value); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return value
|
||||
}
|
||||
set := &FeatureSet{Placed: map[string]PlacedFeature{
|
||||
"test": {Placement: []PlacementModifier{
|
||||
modifier(`{"type":"minecraft:count","count":3}`),
|
||||
modifier(`{"type":"minecraft:in_square"}`),
|
||||
}},
|
||||
}}
|
||||
random := NewLegacy(12345)
|
||||
var got []FeaturePosition
|
||||
err := set.ForEachPlacementPosition("test", random, FeaturePosition{}, PlacementContext{}, func(position FeaturePosition) error {
|
||||
got = append(got, position)
|
||||
random.NextLong() // configured feature draw before the next position
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wantRandom := NewLegacy(12345)
|
||||
var want []FeaturePosition
|
||||
for range 3 {
|
||||
want = append(want, FeaturePosition{X: int(wantRandom.NextIntN(16)), Z: int(wantRandom.NextIntN(16))})
|
||||
wantRandom.NextLong()
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("positions = %v, want interleaved %v", got, want)
|
||||
}
|
||||
if gotState, wantState := random.NextLong(), wantRandom.NextLong(); gotState != wantState {
|
||||
t.Fatalf("random state = %d, want %d", gotState, wantState)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPlacementPositionsWorldAwareModifiers(t *testing.T) {
|
||||
modifier := func(raw string) PlacementModifier {
|
||||
var value PlacementModifier
|
||||
|
|
|
|||
|
|
@ -80,3 +80,43 @@ func TestLegacySetSeedResetsGaussianCache(t *testing.T) {
|
|||
t.Fatalf("NextGaussian after SetSeed = %v, want %v", got, first)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecorationAndFeatureSeedVectors(t *testing.T) {
|
||||
tests := []struct {
|
||||
seed int64
|
||||
blockX, blockZ, feature, step int
|
||||
decoration int64
|
||||
int16, int65 int32
|
||||
floatValue float32
|
||||
doubleValue float64
|
||||
long int64
|
||||
}{
|
||||
{12345, 0, 0, 10, 6, 12345, 1, 23, 0.659135699, 0.60100983666890360, 8913713877150976631},
|
||||
{12345, 16, -48, 10, 6, 95234183275347033, 12, 35, 0.821727931, 0.14700435702589810, -8289173915194761532},
|
||||
{12345, -272, 672, 25, 6, -8094914473946183255, 13, 24, 0.743742824, 0.35161152187643063, 6657627925144652261},
|
||||
{-987654321, 48, 80, 3, 4, 7522931011426891727, 14, 63, 0.750663280, 0.34934808320731514, -8531709575983379994},
|
||||
}
|
||||
for _, test := range tests {
|
||||
random := NewLegacy(0)
|
||||
if got := random.SetDecorationSeed(test.seed, test.blockX, test.blockZ); got != test.decoration {
|
||||
t.Errorf("seed %d block (%d,%d): decoration seed = %d, want %d", test.seed, test.blockX, test.blockZ, got, test.decoration)
|
||||
continue
|
||||
}
|
||||
random.SetFeatureSeed(test.decoration, test.feature, test.step)
|
||||
if got := random.NextIntN(16); got != test.int16 {
|
||||
t.Errorf("seed %d block (%d,%d): nextInt(16) = %d, want %d", test.seed, test.blockX, test.blockZ, got, test.int16)
|
||||
}
|
||||
if got := random.NextIntN(65); got != test.int65 {
|
||||
t.Errorf("seed %d block (%d,%d): nextInt(65) = %d, want %d", test.seed, test.blockX, test.blockZ, got, test.int65)
|
||||
}
|
||||
if got := random.NextFloat(); math.Abs(float64(got-test.floatValue)) > 1e-7 {
|
||||
t.Errorf("seed %d block (%d,%d): nextFloat = %.9f, want %.9f", test.seed, test.blockX, test.blockZ, got, test.floatValue)
|
||||
}
|
||||
if got := random.NextDouble(); math.Abs(got-test.doubleValue) > 1e-15 {
|
||||
t.Errorf("seed %d block (%d,%d): nextDouble = %.17f, want %.17f", test.seed, test.blockX, test.blockZ, got, test.doubleValue)
|
||||
}
|
||||
if got := random.NextLong(); got != test.long {
|
||||
t.Errorf("seed %d block (%d,%d): nextLong = %d, want %d", test.seed, test.blockX, test.blockZ, got, test.long)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue