From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
Date: Sat, 22 Aug 2020 20:47:11 -0700
Subject: [PATCH] Allow toggling special MobSpawners per world

In vanilla, these are all hardcoded on for world type 0 (overworld) and hardcoded off for every other world type. Default config behaviour matches this.

diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index e4651dc35114ee9fd296f7efb6a2d6b808c27d45..f8801c73d4e96949143a5917761b35c7563a6725 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -83,6 +83,7 @@ import net.minecraft.world.entity.MobCategory;
 import net.minecraft.world.entity.ReputationEventHandler;
 import net.minecraft.world.entity.ai.navigation.PathNavigation;
 import net.minecraft.world.entity.ai.village.ReputationEventType;
+import net.minecraft.world.entity.ai.village.VillageSiege;
 import net.minecraft.world.entity.ai.village.poi.PoiManager;
 import net.minecraft.world.entity.ai.village.poi.PoiType;
 import net.minecraft.world.entity.animal.Animal;
@@ -90,7 +91,9 @@ import net.minecraft.world.entity.animal.WaterAnimal;
 import net.minecraft.world.entity.animal.horse.SkeletonHorse;
 import net.minecraft.world.entity.boss.EnderDragonPart;
 import net.minecraft.world.entity.boss.enderdragon.EnderDragon;
+import net.minecraft.world.entity.npc.CatSpawner;
 import net.minecraft.world.entity.npc.Npc;
+import net.minecraft.world.entity.npc.WanderingTraderSpawner;
 import net.minecraft.world.entity.player.Player;
 import net.minecraft.world.entity.raid.Raid;
 import net.minecraft.world.entity.raid.Raids;
@@ -131,6 +134,8 @@ import net.minecraft.world.level.gameevent.GameEvent;
 import net.minecraft.world.level.gameevent.GameEventListenerRegistrar;
 import net.minecraft.world.level.gameevent.vibrations.VibrationPath;
 import net.minecraft.world.level.levelgen.Heightmap;
+import net.minecraft.world.level.levelgen.PatrolSpawner;
+import net.minecraft.world.level.levelgen.PhantomSpawner;
 import net.minecraft.world.level.levelgen.feature.StructureFeature;
 import net.minecraft.world.level.levelgen.structure.BoundingBox;
 import net.minecraft.world.level.levelgen.structure.StructureStart;
@@ -506,7 +511,24 @@ public class ServerLevel extends Level implements WorldGenLevel {
         this.dragonParts = new Int2ObjectOpenHashMap();
         this.tickTime = flag1;
         this.server = minecraftserver;
-        this.customSpawners = list;
+        // Purpur start - enable/disable MobSpawners per world
+        this.customSpawners = Lists.newArrayList();
+        if (purpurConfig.phantomSpawning) {
+            customSpawners.add(new PhantomSpawner());
+        }
+        if (purpurConfig.patrolSpawning) {
+            customSpawners.add(new PatrolSpawner());
+        }
+        if (purpurConfig.catSpawning) {
+            customSpawners.add(new CatSpawner());
+        }
+        if (purpurConfig.villageSiegeSpawning) {
+            customSpawners.add(new VillageSiege());
+        }
+        if (purpurConfig.villagerTraderSpawning) {
+            customSpawners.add(new WanderingTraderSpawner(iworlddataserver));
+        }
+        // Purpur end
         // CraftBukkit start
         this.serverLevelData = (PrimaryLevelData) iworlddataserver;
         serverLevelData.world = this;
diff --git a/src/main/java/net/minecraft/world/entity/npc/WanderingTraderSpawner.java b/src/main/java/net/minecraft/world/entity/npc/WanderingTraderSpawner.java
index f8ede3588bfda9a7d4d5807311a3e9c2651fd0a3..56967cef0f184def046935e20148574f24524f44 100644
--- a/src/main/java/net/minecraft/world/entity/npc/WanderingTraderSpawner.java
+++ b/src/main/java/net/minecraft/world/entity/npc/WanderingTraderSpawner.java
@@ -5,6 +5,7 @@ import java.util.Optional;
 import java.util.Random;
 import javax.annotation.Nullable;
 import net.minecraft.core.BlockPos;
+import net.minecraft.core.Direction;
 import net.minecraft.nbt.CompoundTag;
 import net.minecraft.network.chat.Component;
 import net.minecraft.server.level.ServerLevel;
@@ -160,7 +161,17 @@ public class WanderingTraderSpawner implements CustomSpawner {
             int k = pos.getX() + this.random.nextInt(range * 2) - range;
             int l = pos.getZ() + this.random.nextInt(range * 2) - range;
             int i1 = world.getHeight(Heightmap.Types.WORLD_SURFACE, k, l);
-            BlockPos blockposition2 = new BlockPos(k, i1, l);
+            // Purpur start - allow traders to spawn below nether roof
+            BlockPos.MutableBlockPos blockposition2 = new BlockPos.MutableBlockPos(k, i1, l);
+            if (world.dimensionType().hasCeiling()) {
+                do {
+                    blockposition2.relative(Direction.DOWN);
+                } while (!world.getBlockState(blockposition2).isAir());
+                do {
+                    blockposition2.relative(Direction.DOWN);
+                } while (world.getBlockState(blockposition2).isAir() && blockposition2.getY() > 0);
+            }
+            // Purpur end
 
             if (NaturalSpawner.isSpawnPositionOk(SpawnPlacements.Type.ON_GROUND, world, blockposition2, EntityType.WANDERING_TRADER)) {
                 blockposition1 = blockposition2;
diff --git a/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java b/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java
index 21133a596e586e6713ccec9c78fde6b07bcff3e0..316469509ed3ff88229323c488e48b1fc439a0df 100644
--- a/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java
+++ b/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java
@@ -72,6 +72,12 @@ public class PurpurWorldConfig {
         return PurpurConfig.config.getBoolean("world-settings." + worldName + "." + path, PurpurConfig.config.getBoolean("world-settings.default." + path));
     }
 
+    private boolean getBoolean(String path, Predicate<Boolean> predicate) {
+        String val = getString(path, "default").toLowerCase();
+        Boolean bool = BooleanUtils.toBooleanObject(val, "true", "false", "default");
+        return predicate.test(bool);
+    }
+
     private double getDouble(String path, double def) {
         PurpurConfig.config.addDefault("world-settings.default." + path, def);
         return PurpurConfig.config.getDouble("world-settings." + worldName + "." + path, PurpurConfig.config.getDouble("world-settings.default." + path));
@@ -221,6 +227,21 @@ public class PurpurWorldConfig {
         }
     }
 
+    public boolean catSpawning;
+    public boolean patrolSpawning;
+    public boolean phantomSpawning;
+    public boolean villagerTraderSpawning;
+    public boolean villageSiegeSpawning;
+    private void mobSpawnerSettings() {
+        // values of "default" or null will default to true only if the world environment is normal (aka overworld)
+        Predicate<Boolean> predicate = (bool) -> (bool != null && bool) || (bool == null && environment == World.Environment.NORMAL);
+        catSpawning = getBoolean("gameplay-mechanics.mob-spawning.village-cats", predicate);
+        patrolSpawning = getBoolean("gameplay-mechanics.mob-spawning.raid-patrols", predicate);
+        phantomSpawning = getBoolean("gameplay-mechanics.mob-spawning.phantoms", predicate);
+        villagerTraderSpawning = getBoolean("gameplay-mechanics.mob-spawning.wandering-traders", predicate);
+        villageSiegeSpawning = getBoolean("gameplay-mechanics.mob-spawning.village-sieges", predicate);
+    }
+
     public boolean idleTimeoutKick = true;
     public boolean idleTimeoutTickNearbyEntities = true;
     public boolean idleTimeoutCountAsSleeping = false;
