diff --git a/patches/server/Add-config-for-stronghold-seed.patch b/patches/server/Add-config-for-stronghold-seed.patch
deleted file mode 100644
index fe245a5ff3..0000000000
--- a/patches/server/Add-config-for-stronghold-seed.patch
+++ /dev/null
@@ -1,47 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Jake Potrebic <jake.m.potrebic@gmail.com>
-Date: Thu, 13 Jan 2022 23:05:53 -0800
-Subject: [PATCH] Add config for stronghold seed
-
-
-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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java
-+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java
-@@ -0,0 +0,0 @@ public abstract class ChunkGenerator {
-             HolderSet<Biome> holderset = concentricringsstructureplacement.preferredBiomes();
-             RandomSource randomsource = RandomSource.create();
- 
-+            // Paper start
-+            if (this.conf.strongholdSeed != null && this.structureSets.getResourceKey(holder).orElse(null) == net.minecraft.world.level.levelgen.structure.BuiltinStructureSets.STRONGHOLDS) {
-+                randomsource.setSeed(this.conf.strongholdSeed);
-+            } else {
-             randomsource.setSeed(this instanceof FlatLevelSource ? 0L : randomstate.legacyLevelSeed());
-+            }
-+            // Paper end
-             double d0 = randomsource.nextDouble() * 3.141592653589793D * 2.0D;
-             int l = 0;
-             int i1 = 0;
-diff --git a/src/main/java/org/spigotmc/SpigotWorldConfig.java b/src/main/java/org/spigotmc/SpigotWorldConfig.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/org/spigotmc/SpigotWorldConfig.java
-+++ b/src/main/java/org/spigotmc/SpigotWorldConfig.java
-@@ -0,0 +0,0 @@ public class SpigotWorldConfig
-     public int mansionSeed;
-     public int fossilSeed;
-     public int portalSeed;
-+    public Long strongholdSeed; // Paper
-     private void initWorldGenSeeds()
-     {
-         this.villageSeed = this.getInt( "seed-village", 10387312 );
-@@ -0,0 +0,0 @@ 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
-+        final String strongholdSeedString = this.getString("seed-stronghold", "default");
-+        this.strongholdSeed = org.apache.commons.lang3.math.NumberUtils.isParsable(strongholdSeedString) ? Long.parseLong(strongholdSeedString) : null;
-+        // 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/patches/server/Add-missing-structure-set-seed-configs.patch b/patches/server/Add-missing-structure-set-seed-configs.patch
new file mode 100644
index 0000000000..85e707b3c1
--- /dev/null
+++ b/patches/server/Add-missing-structure-set-seed-configs.patch
@@ -0,0 +1,197 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <jake.m.potrebic@gmail.com>
+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 <blake.galbreath@gmail.com>
+
+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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
+--- a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java
++++ b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java
+@@ -0,0 +0,0 @@ public abstract class ChunkGenerator {
+     // Spigot start
+     private Stream<StructureSet> possibleStructureSetsSpigot() {
+         return this.possibleStructureSets().map(Holder::value).map((structureset) -> {
+-            if (structureset.placement() instanceof RandomSpreadStructurePlacement randomConfig) {
++            if (structureset.placement() instanceof RandomSpreadStructurePlacement randomConfig && this.structureSets.getKey(structureset).getNamespace().equals(net.minecraft.resources.ResourceLocation.DEFAULT_NAMESPACE)) { // Paper - check namespace cause datapacks could add structure sets with the same path
+                 String name = this.structureSets.getKey(structureset).getPath();
+                 int seed = randomConfig.salt;
+ 
+@@ -0,0 +0,0 @@ public abstract class ChunkGenerator {
+                     case "villages":
+                         seed = conf.villageSeed;
+                         break;
++                        // Paper start
++                    case "ancient_cities":
++                        seed = conf.ancientCitySeed;
++                        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()));
+@@ -0,0 +0,0 @@ public abstract class ChunkGenerator {
+             HolderSet<Biome> holderset = concentricringsstructureplacement.preferredBiomes();
+             RandomSource randomsource = RandomSource.create();
+ 
++            // Paper start
++            if (this.conf.strongholdSeed != null && this.structureSets.getResourceKey(holder).orElse(null) == net.minecraft.world.level.levelgen.structure.BuiltinStructureSets.STRONGHOLDS) {
++                randomsource.setSeed(this.conf.strongholdSeed);
++            } else {
+             randomsource.setSeed(this instanceof FlatLevelSource ? 0L : randomstate.legacyLevelSeed());
++            }
++            // Paper end
+             double d0 = randomsource.nextDouble() * 3.141592653589793D * 2.0D;
+             int l = 0;
+             int i1 = 0;
+@@ -0,0 +0,0 @@ public abstract class ChunkGenerator {
+ 
+             for (int i1 = chunkX - chunkRange; i1 <= chunkX + chunkRange; ++i1) {
+                 for (int j1 = chunkZ - chunkRange; j1 <= chunkZ + chunkRange; ++j1) {
+-                    if (structureplacement.isStructureChunk(this, noiseConfig, seed, i1, j1)) {
++                    if (structureplacement.isStructureChunk(this, noiseConfig, seed, i1, j1, structureSet.unwrapKey().map(ResourceKey::location).orElse(null))) { // Paper
+                         return true;
+                     }
+                 }
+@@ -0,0 +0,0 @@ public abstract class ChunkGenerator {
+                 }
+             }
+ 
+-            if (structureplacement.isStructureChunk(this, noiseConfig, seed, chunkcoordintpair.x, chunkcoordintpair.z)) {
++            if (structureplacement.isStructureChunk(this, noiseConfig, seed, chunkcoordintpair.x, chunkcoordintpair.z, this.structureSets.getResourceKey(holder).map(ResourceKey::location).orElse(null))) { // Paper
+                 if (list.size() == 1) {
+                     this.tryGenerateStructure((StructureSet.StructureSelectionEntry) list.get(0), structureAccessor, registryManager, noiseConfig, structureTemplateManager, seed, chunk, chunkcoordintpair, sectionposition);
+                 } else {
+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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
+@@ -0,0 +0,0 @@ public abstract class StructurePlacement {
+         return this.exclusionZone;
+     }
+ 
++    @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper
+     public boolean isStructureChunk(ChunkGenerator chunkGenerator, RandomState noiseConfig, long seed, int chunkX, int chunkZ) {
++        // Paper start - add missing structure set configs
++        return this.isStructureChunk(chunkGenerator, noiseConfig, seed, chunkX, chunkZ, null);
++    }
++    public boolean isStructureChunk(ChunkGenerator chunkGenerator, RandomState noiseConfig, long seed, int chunkX, int chunkZ, @org.jetbrains.annotations.Nullable net.minecraft.resources.ResourceLocation structureSetKey) {
++        Integer saltOverride = null;
++        if (net.minecraft.world.level.levelgen.structure.BuiltinStructureSets.MINESHAFTS.location().equals(structureSetKey)) {
++            saltOverride = chunkGenerator.conf.mineshaftSeed;
++        } else if (net.minecraft.world.level.levelgen.structure.BuiltinStructureSets.BURIED_TREASURES.location().equals(structureSetKey)) {
++            saltOverride = chunkGenerator.conf.buriedTreasureSeed;
++        }
++        // Paper end
+         if (!this.isPlacementChunk(chunkGenerator, noiseConfig, seed, chunkX, chunkZ)) {
+             return false;
+-        } else if (this.frequency < 1.0F && !this.frequencyReductionMethod.shouldGenerate(seed, this.salt, chunkX, chunkZ, this.frequency)) {
++        } else if (this.frequency < 1.0F && !this.frequencyReductionMethod.shouldGenerate(seed, this.salt, chunkX, chunkZ, this.frequency, saltOverride)) { // Paper
+             return false;
+         } else {
+             return !this.exclusionZone.isPresent() || !this.exclusionZone.get().isPlacementForbidden(chunkGenerator, noiseConfig, seed, chunkX, chunkZ);
+@@ -0,0 +0,0 @@ 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 : 10387320); // 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));
+@@ -0,0 +0,0 @@ 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 {
+@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
+--- a/src/main/java/org/spigotmc/SpigotWorldConfig.java
++++ b/src/main/java/org/spigotmc/SpigotWorldConfig.java
+@@ -0,0 +0,0 @@ public class SpigotWorldConfig
+     public int mansionSeed;
+     public int fossilSeed;
+     public int portalSeed;
++    // Paper start - add missing structure set configs
++    public int ancientCitySeed;
++    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 );
+@@ -0,0 +0,0 @@ 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.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 );
+     }