aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/server/0761-Add-missing-structure-set-seed-configs.patch
diff options
context:
space:
mode:
authorbooky10 <[email protected]>2023-11-04 20:20:01 +0100
committerGitHub <[email protected]>2023-11-04 20:20:01 +0100
commitf78d7ce8ffb7e4b6fd5ee256f5e3678ea04fd807 (patch)
tree8e7abee5129e15a919630824c4f194a2811f7242 /patches/server/0761-Add-missing-structure-set-seed-configs.patch
parent44057da46727138e19d951b56e98ad8c25c1f869 (diff)
downloadPaper-f78d7ce8ffb7e4b6fd5ee256f5e3678ea04fd807.tar.gz
Paper-f78d7ce8ffb7e4b6fd5ee256f5e3678ea04fd807.zip
Remove "fix-curing-zombie-villager-discount" exploit option (#9895)
Diffstat (limited to 'patches/server/0761-Add-missing-structure-set-seed-configs.patch')
-rw-r--r--patches/server/0761-Add-missing-structure-set-seed-configs.patch349
1 files changed, 349 insertions, 0 deletions
diff --git a/patches/server/0761-Add-missing-structure-set-seed-configs.patch b/patches/server/0761-Add-missing-structure-set-seed-configs.patch
new file mode 100644
index 0000000000..619c499781
--- /dev/null
+++ b/patches/server/0761-Add-missing-structure-set-seed-configs.patch
@@ -0,0 +1,349 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <[email protected]>
+Date: Thu, 13 Jan 2022 23:05:53 -0800
+Subject: [PATCH] Add missing structure set seed configs
+
+The 4 missing structure set seed configs are strongholds, mineshafts,
+buried treasure, and ancient cities.
+
+Strongholds use a ring placement scheme which isn't random so they
+utilize the world seed by default, this adds a config to override it
+for just generating the ring positions.
+
+Mineshafts and Buried Treasure structure sets are special cases
+where the "salt" that can be defined for them via datapacks has 0
+effect because the difference between the spacing and separation is 1
+which is used as the upper bound in the random with salt. So the random
+always returns the same int (0) so the salt has no effect. This adds
+seeds/salts to the frequency reducer which has a similar effect.
+
+Co-authored-by: William Blake Galbreath <[email protected]>
+
+diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java
+index eee2239cd715d01c5adbf1cd79282e115f42cd2e..8bab3fcfc6aa6c0b37621474a69f15e94bda2113 100644
+--- a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java
++++ b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java
+@@ -568,7 +568,7 @@ public abstract class ChunkGenerator {
+ }
+ }
+
+- if (structureplacement.isStructureChunk(placementCalculator, chunkcoordintpair.x, chunkcoordintpair.z)) {
++ if (structureplacement.isStructureChunk(placementCalculator, chunkcoordintpair.x, chunkcoordintpair.z, structureplacement instanceof net.minecraft.world.level.chunk.ChunkGeneratorStructureState.KeyedRandomSpreadStructurePlacement keyed ? keyed.key : null)) { // Paper - add missing structure set configs
+ if (list.size() == 1) {
+ this.tryGenerateStructure((StructureSet.StructureSelectionEntry) list.get(0), structureAccessor, registryManager, randomstate, structureTemplateManager, placementCalculator.getLevelSeed(), chunk, chunkcoordintpair, sectionposition);
+ } else {
+diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java b/src/main/java/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java
+index a310bfbf0d08187375ea17f4b04b276a0b7d0b9f..f8cd23fb6ea7909b8f30bd21d3f2c7bcc483ef21 100644
+--- a/src/main/java/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java
++++ b/src/main/java/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java
+@@ -50,13 +50,14 @@ public class ChunkGeneratorStructureState {
+ private final Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>> ringPositions = new Object2ObjectArrayMap();
+ private boolean hasGeneratedPositions;
+ private final List<Holder<StructureSet>> possibleStructureSets;
++ public final SpigotWorldConfig conf; // Paper
+
+ public static ChunkGeneratorStructureState createForFlat(RandomState randomstate, long i, BiomeSource worldchunkmanager, Stream<Holder<StructureSet>> stream, SpigotWorldConfig conf) { // Spigot
+ List<Holder<StructureSet>> list = stream.filter((holder) -> {
+ return ChunkGeneratorStructureState.hasBiomesForStructureSet((StructureSet) holder.value(), worldchunkmanager);
+ }).toList();
+
+- return new ChunkGeneratorStructureState(randomstate, worldchunkmanager, i, 0L, ChunkGeneratorStructureState.injectSpigot(list, conf)); // Spigot
++ return new ChunkGeneratorStructureState(randomstate, worldchunkmanager, i, 0L, ChunkGeneratorStructureState.injectSpigot(list, conf), conf); // Spigot
+ }
+
+ public static ChunkGeneratorStructureState createForNormal(RandomState randomstate, long i, BiomeSource worldchunkmanager, HolderLookup<StructureSet> holderlookup, SpigotWorldConfig conf) { // Spigot
+@@ -64,14 +65,24 @@ public class ChunkGeneratorStructureState {
+ return ChunkGeneratorStructureState.hasBiomesForStructureSet((StructureSet) holder_c.value(), worldchunkmanager);
+ }).collect(Collectors.toUnmodifiableList());
+
+- return new ChunkGeneratorStructureState(randomstate, worldchunkmanager, i, i, ChunkGeneratorStructureState.injectSpigot(list, conf)); // Spigot
++ return new ChunkGeneratorStructureState(randomstate, worldchunkmanager, i, i, ChunkGeneratorStructureState.injectSpigot(list, conf), conf); // Spigot
+ }
++ // Paper start - horrible hack because spigot creates a ton of direct Holders which lose track of the identifying key
++ public static final class KeyedRandomSpreadStructurePlacement extends RandomSpreadStructurePlacement {
++ public final net.minecraft.resources.ResourceKey<StructureSet> key;
++ public KeyedRandomSpreadStructurePlacement(net.minecraft.resources.ResourceKey<StructureSet> key, net.minecraft.core.Vec3i locateOffset, FrequencyReductionMethod frequencyReductionMethod, float frequency, int salt, java.util.Optional<StructurePlacement.ExclusionZone> exclusionZone, int spacing, int separation, net.minecraft.world.level.levelgen.structure.placement.RandomSpreadType spreadType) {
++ super(locateOffset, frequencyReductionMethod, frequency, salt, exclusionZone, spacing, separation, spreadType);
++ this.key = key;
++ }
++ }
++ // Paper end
+
+ // Spigot start
+ private static List<Holder<StructureSet>> injectSpigot(List<Holder<StructureSet>> list, SpigotWorldConfig conf) {
+ return list.stream().map((holder) -> {
+ StructureSet structureset = holder.value();
+- if (structureset.placement() instanceof RandomSpreadStructurePlacement randomConfig) {
++ final Holder<StructureSet> newHolder; // Paper
++ if (structureset.placement() instanceof RandomSpreadStructurePlacement randomConfig && holder.unwrapKey().orElseThrow().location().getNamespace().equals(net.minecraft.resources.ResourceLocation.DEFAULT_NAMESPACE)) { // Paper - check namespace cause datapacks could add structure sets with the same path
+ String name = holder.unwrapKey().orElseThrow().location().getPath();
+ int seed = randomConfig.salt;
+
+@@ -118,11 +129,24 @@ public class ChunkGeneratorStructureState {
+ case "villages":
+ seed = conf.villageSeed;
+ break;
++ // Paper start
++ case "ancient_cities":
++ seed = conf.ancientCitySeed;
++ break;
++ case "trail_ruins":
++ seed = conf.trailRuinsSeed;
++ break;
++ // Paper end
+ }
+
+- structureset = new StructureSet(structureset.structures(), new RandomSpreadStructurePlacement(randomConfig.locateOffset, randomConfig.frequencyReductionMethod, randomConfig.frequency, seed, randomConfig.exclusionZone, randomConfig.spacing(), randomConfig.separation(), randomConfig.spreadType()));
++ // Paper start
++ structureset = new StructureSet(structureset.structures(), new KeyedRandomSpreadStructurePlacement(holder.unwrapKey().orElseThrow(), randomConfig.locateOffset, randomConfig.frequencyReductionMethod, randomConfig.frequency, seed, randomConfig.exclusionZone, randomConfig.spacing(), randomConfig.separation(), randomConfig.spreadType()));
++ newHolder = Holder.direct(structureset); // I really wish we didn't have to do this here
++ } else {
++ newHolder = holder;
+ }
+- return Holder.direct(structureset);
++ return newHolder;
++ // Paper end
+ }).collect(Collectors.toUnmodifiableList());
+ }
+ // Spigot end
+@@ -139,12 +163,13 @@ public class ChunkGeneratorStructureState {
+ return stream.anyMatch(set::contains);
+ }
+
+- private ChunkGeneratorStructureState(RandomState noiseConfig, BiomeSource biomeSource, long structureSeed, long concentricRingSeed, List<Holder<StructureSet>> structureSets) {
++ private ChunkGeneratorStructureState(RandomState noiseConfig, BiomeSource biomeSource, long structureSeed, long concentricRingSeed, List<Holder<StructureSet>> structureSets, SpigotWorldConfig conf) { // Paper
+ this.randomState = noiseConfig;
+ this.levelSeed = structureSeed;
+ this.biomeSource = biomeSource;
+ this.concentricRingsSeed = concentricRingSeed;
+ this.possibleStructureSets = structureSets;
++ this.conf = conf; // Paper
+ }
+
+ public List<Holder<StructureSet>> possibleStructureSets() {
+@@ -198,7 +223,13 @@ public class ChunkGeneratorStructureState {
+ HolderSet<Biome> holderset = placement.preferredBiomes();
+ RandomSource randomsource = RandomSource.create();
+
++ // Paper start
++ if (this.conf.strongholdSeed != null && structureSetEntry.is(net.minecraft.world.level.levelgen.structure.BuiltinStructureSets.STRONGHOLDS)) {
++ randomsource.setSeed(this.conf.strongholdSeed);
++ } else {
++ // Paper end
+ randomsource.setSeed(this.concentricRingsSeed);
++ } // Paper
+ double d0 = randomsource.nextDouble() * 3.141592653589793D * 2.0D;
+ int l = 0;
+ int i1 = 0;
+@@ -275,7 +306,7 @@ public class ChunkGeneratorStructureState {
+
+ for (int l = centerChunkX - chunkCount; l <= centerChunkX + chunkCount; ++l) {
+ for (int i1 = centerChunkZ - chunkCount; i1 <= centerChunkZ + chunkCount; ++i1) {
+- if (structureplacement.isStructureChunk(this, l, i1)) {
++ if (structureplacement.isStructureChunk(this, l, i1, structureplacement instanceof KeyedRandomSpreadStructurePlacement keyed ? keyed.key : null)) { // Paper - add missing structure set configs
+ return true;
+ }
+ }
+diff --git a/src/main/java/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java b/src/main/java/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java
+index 65dcb14241baadb2c9f8f16919d7b562198ad9c3..594a2dd3b1d4c29c969d1992b8e93795da00e682 100644
+--- a/src/main/java/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java
++++ b/src/main/java/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java
+@@ -59,10 +59,24 @@ public abstract class StructurePlacement {
+ return this.exclusionZone;
+ }
+
++ @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper
+ public boolean isStructureChunk(ChunkGeneratorStructureState calculator, int chunkX, int chunkZ) {
++ // Paper start - add missing structure set configs
++ return this.isStructureChunk(calculator, chunkX, chunkZ, null);
++ }
++ public boolean isStructureChunk(ChunkGeneratorStructureState calculator, int chunkX, int chunkZ, @org.jetbrains.annotations.Nullable net.minecraft.resources.ResourceKey<StructureSet> structureSetKey) {
++ Integer saltOverride = null;
++ if (structureSetKey != null) {
++ if (structureSetKey == net.minecraft.world.level.levelgen.structure.BuiltinStructureSets.MINESHAFTS) {
++ saltOverride = calculator.conf.mineshaftSeed;
++ } else if (structureSetKey == net.minecraft.world.level.levelgen.structure.BuiltinStructureSets.BURIED_TREASURES) {
++ saltOverride = calculator.conf.buriedTreasureSeed;
++ }
++ }
++ // Paper end
+ if (!this.isPlacementChunk(calculator, chunkX, chunkZ)) {
+ return false;
+- } else if (this.frequency < 1.0F && !this.frequencyReductionMethod.shouldGenerate(calculator.getLevelSeed(), this.salt, chunkX, chunkZ, this.frequency)) {
++ } else if (this.frequency < 1.0F && !this.frequencyReductionMethod.shouldGenerate(calculator.getLevelSeed(), this.salt, chunkX, chunkZ, this.frequency, saltOverride)) { // Paper
+ return false;
+ } else {
+ return !this.exclusionZone.isPresent() || !this.exclusionZone.get().isPlacementForbidden(calculator, chunkX, chunkZ);
+@@ -77,25 +91,31 @@ public abstract class StructurePlacement {
+
+ public abstract StructurePlacementType<?> type();
+
+- private static boolean probabilityReducer(long seed, int salt, int chunkX, int chunkZ, float frequency) {
++ private static boolean probabilityReducer(long seed, int salt, int chunkX, int chunkZ, float frequency, @org.jetbrains.annotations.Nullable Integer saltOverride) { // Paper - ignore here
+ WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L));
+ worldgenRandom.setLargeFeatureWithSalt(seed, salt, chunkX, chunkZ);
+ return worldgenRandom.nextFloat() < frequency;
+ }
+
+- private static boolean legacyProbabilityReducerWithDouble(long seed, int salt, int chunkX, int chunkZ, float frequency) {
++ private static boolean legacyProbabilityReducerWithDouble(long seed, int salt, int chunkX, int chunkZ, float frequency, @org.jetbrains.annotations.Nullable Integer saltOverride) { // Paper
+ WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L));
++ if (saltOverride == null) { // Paper
+ worldgenRandom.setLargeFeatureSeed(seed, chunkX, chunkZ);
++ // Paper start
++ } else {
++ worldgenRandom.setLargeFeatureWithSalt(seed, chunkX, chunkZ, saltOverride);
++ }
++ // Paper end
+ return worldgenRandom.nextDouble() < (double)frequency;
+ }
+
+- private static boolean legacyArbitrarySaltProbabilityReducer(long seed, int salt, int chunkX, int chunkZ, float frequency) {
++ private static boolean legacyArbitrarySaltProbabilityReducer(long seed, int salt, int chunkX, int chunkZ, float frequency, @org.jetbrains.annotations.Nullable Integer saltOverride) { // Paper
+ WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L));
+- worldgenRandom.setLargeFeatureWithSalt(seed, chunkX, chunkZ, 10387320);
++ worldgenRandom.setLargeFeatureWithSalt(seed, chunkX, chunkZ, saltOverride != null ? saltOverride : HIGHLY_ARBITRARY_RANDOM_SALT); // Paper
+ return worldgenRandom.nextFloat() < frequency;
+ }
+
+- private static boolean legacyPillagerOutpostReducer(long seed, int salt, int chunkX, int chunkZ, float frequency) {
++ private static boolean legacyPillagerOutpostReducer(long seed, int salt, int chunkX, int chunkZ, float frequency, @org.jetbrains.annotations.Nullable Integer saltOverride) { // Paper - ignore here
+ int i = chunkX >> 4;
+ int j = chunkZ >> 4;
+ WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L));
+@@ -118,7 +138,7 @@ public abstract class StructurePlacement {
+
+ @FunctionalInterface
+ public interface FrequencyReducer {
+- boolean shouldGenerate(long seed, int salt, int chunkX, int chunkZ, float chance);
++ boolean shouldGenerate(long seed, int salt, int chunkX, int chunkZ, float chance, @org.jetbrains.annotations.Nullable Integer saltOverride); // Paper
+ }
+
+ public static enum FrequencyReductionMethod implements StringRepresentable {
+@@ -136,8 +156,8 @@ public abstract class StructurePlacement {
+ this.reducer = generationPredicate;
+ }
+
+- public boolean shouldGenerate(long seed, int salt, int chunkX, int chunkZ, float chance) {
+- return this.reducer.shouldGenerate(seed, salt, chunkX, chunkZ, chance);
++ public boolean shouldGenerate(long seed, int salt, int chunkX, int chunkZ, float chance, @org.jetbrains.annotations.Nullable Integer saltOverride) { // Paper
++ return this.reducer.shouldGenerate(seed, salt, chunkX, chunkZ, chance, saltOverride); // Paper
+ }
+
+ @Override
+diff --git a/src/main/java/org/spigotmc/SpigotWorldConfig.java b/src/main/java/org/spigotmc/SpigotWorldConfig.java
+index 62c1434018be5b5fb70f7019b3c06d4d9200661d..f9b8e2bc039f1a37e47f84909c8785f3ef530284 100644
+--- a/src/main/java/org/spigotmc/SpigotWorldConfig.java
++++ b/src/main/java/org/spigotmc/SpigotWorldConfig.java
+@@ -368,6 +368,17 @@ public class SpigotWorldConfig
+ public int mansionSeed;
+ public int fossilSeed;
+ public int portalSeed;
++ // Paper start - add missing structure set configs
++ public int ancientCitySeed;
++ public int trailRuinsSeed;
++ public int buriedTreasureSeed;
++ public Integer mineshaftSeed;
++ public Long strongholdSeed;
++ private <N extends Number> N getSeed(String path, java.util.function.Function<String, N> toNumberFunc) {
++ final String value = this.getString(path, "default");
++ return org.apache.commons.lang3.math.NumberUtils.isParsable(value) ? toNumberFunc.apply(value) : null;
++ }
++ // Paper end
+ private void initWorldGenSeeds()
+ {
+ this.villageSeed = this.getInt( "seed-village", 10387312 );
+@@ -385,6 +396,13 @@ public class SpigotWorldConfig
+ this.mansionSeed = this.getInt( "seed-mansion", 10387319 );
+ this.fossilSeed = this.getInt( "seed-fossil", 14357921 );
+ this.portalSeed = this.getInt( "seed-portal", 34222645 );
++ // Paper start - add missing structure set configs
++ this.ancientCitySeed = this.getInt("seed-ancientcity", 20083232);
++ this.trailRuinsSeed = this.getInt("seed-trailruins", 83469867);
++ this.buriedTreasureSeed = this.getInt("seed-buriedtreasure", 10387320); // StructurePlacement#HIGHLY_ARBITRARY_RANDOM_SALT
++ this.mineshaftSeed = this.getSeed("seed-mineshaft", Integer::parseInt);
++ this.strongholdSeed = this.getSeed("seed-stronghold", Long::parseLong);
++ // Paper end
+ this.log( "Custom Map Seeds: Village: " + this.villageSeed + " Desert: " + this.desertSeed + " Igloo: " + this.iglooSeed + " Jungle: " + this.jungleSeed + " Swamp: " + this.swampSeed + " Monument: " + this.monumentSeed
+ + " Ocean: " + this.oceanSeed + " Shipwreck: " + this.shipwreckSeed + " End City: " + this.endCitySeed + " Slime: " + this.slimeSeed + " Nether: " + this.netherSeed + " Mansion: " + this.mansionSeed + " Fossil: " + this.fossilSeed + " Portal: " + this.portalSeed );
+ }
+diff --git a/src/test/java/io/papermc/paper/world/structure/StructureSeedConfigTest.java b/src/test/java/io/papermc/paper/world/structure/StructureSeedConfigTest.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..eeb03ecaaa81ef21c15460245398e5246c3ac514
+--- /dev/null
++++ b/src/test/java/io/papermc/paper/world/structure/StructureSeedConfigTest.java
+@@ -0,0 +1,74 @@
++package io.papermc.paper.world.structure;
++
++import io.papermc.paper.configuration.PaperConfigurations;
++import java.io.File;
++import java.lang.reflect.Field;
++import net.minecraft.core.Registry;
++import net.minecraft.core.registries.Registries;
++import net.minecraft.resources.ResourceKey;
++import net.minecraft.resources.ResourceLocation;
++import net.minecraft.world.level.levelgen.structure.BuiltinStructureSets;
++import net.minecraft.world.level.levelgen.structure.StructureSet;
++import net.minecraft.world.level.levelgen.structure.placement.StructurePlacement;
++import org.bukkit.configuration.file.YamlConfiguration;
++import org.bukkit.support.AbstractTestingBase;
++import org.jetbrains.annotations.NotNull;
++import org.junit.jupiter.api.Test;
++import org.spigotmc.SpigotConfig;
++import org.spigotmc.SpigotWorldConfig;
++
++import static org.junit.jupiter.api.Assertions.assertEquals;
++
++public class StructureSeedConfigTest extends AbstractTestingBase {
++
++ @Test
++ public void checkStructureSeedDefaults() throws ReflectiveOperationException {
++ SpigotConfig.config = new YamlConfiguration() {
++ @Override
++ public void save(final @NotNull File file) {
++ // no-op
++ }
++ };
++ final SpigotWorldConfig config = PaperConfigurations.SPIGOT_WORLD_DEFAULTS.get();
++
++
++ final Registry<StructureSet> structureSets = AbstractTestingBase.REGISTRY_CUSTOM.registryOrThrow(Registries.STRUCTURE_SET);
++ for (final ResourceKey<StructureSet> setKey : structureSets.registryKeySet()) {
++ assertEquals(ResourceLocation.DEFAULT_NAMESPACE, setKey.location().getNamespace());
++ final StructureSet set = structureSets.getOrThrow(setKey);
++ if (setKey == BuiltinStructureSets.STRONGHOLDS) { // special case due to seed matching world seed
++ assertEquals(0, set.placement().salt);
++ continue;
++ }
++ int salt = switch (setKey.location().getPath()) {
++ case "villages" -> config.villageSeed;
++ case "desert_pyramids" -> config.desertSeed;
++ case "igloos" -> config.iglooSeed;
++ case "jungle_temples" -> config.jungleSeed;
++ case "swamp_huts" -> config.swampSeed;
++ case "pillager_outposts" -> config.outpostSeed;
++ case "ocean_monuments" -> config.monumentSeed;
++ case "woodland_mansions" -> config.mansionSeed;
++ case "buried_treasures" -> config.buriedTreasureSeed;
++ case "mineshafts" -> config.mineshaftSeed == null ? 0 : config.mineshaftSeed; // mineshaft seed is set differently
++ case "ruined_portals" -> config.portalSeed;
++ case "shipwrecks" -> config.shipwreckSeed;
++ case "ocean_ruins" -> config.oceanSeed;
++ case "nether_complexes" -> config.netherSeed;
++ case "nether_fossils" -> config.fossilSeed;
++ case "end_cities" -> config.endCitySeed;
++ case "ancient_cities" -> config.ancientCitySeed;
++ case "trail_ruins" -> config.trailRuinsSeed;
++ default -> throw new AssertionError("Missing structure set seed in SpigotWorldConfig for " + setKey);
++ };
++ if (setKey == BuiltinStructureSets.BURIED_TREASURES) {
++ final Field field = StructurePlacement.class.getDeclaredField("HIGHLY_ARBITRARY_RANDOM_SALT");
++ field.trySetAccessible();
++ assertEquals(0, set.placement().salt);
++ assertEquals(field.get(null), salt, "Mismatched default seed for " + setKey + ". Should be " + field.get(null));
++ continue;
++ }
++ assertEquals(set.placement().salt, salt, "Mismatched default seed for " + setKey + ". Should be " + set.placement().salt);
++ }
++ }
++}