From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
Date: Thu, 20 Aug 2020 11:20:12 -0700
Subject: [PATCH] Add Wandering Trader spawn rate config options

Adds config options for modifying the spawn rates of Wandering Traders.
These values are all easy to understand and configure after a quick read of this
page on the Minecraft wiki: https://minecraft.gamepedia.com/Wandering_Trader#Spawning
Usages of the vanilla WanderingTraderSpawnDelay and WanderingTraderSpawnChance values
in IWorldServerData are removed as they were only used in certain places, with hardcoded
values used in other places.

diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
index 86cddde870f3ec45e3708aadc1f3e4b19cbaebcc..681985c8412562fedea841738656ce0b7391964a 100644
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
@@ -81,6 +81,19 @@ public class PaperWorldConfig {
         }
     }
 
+    public int wanderingTraderSpawnMinuteTicks = 1200;
+    public int wanderingTraderSpawnDayTicks = 24000;
+    public int wanderingTraderSpawnChanceFailureIncrement = 25;
+    public int wanderingTraderSpawnChanceMin = 25;
+    public int wanderingTraderSpawnChanceMax = 75;
+    private void wanderingTraderSettings() {
+        wanderingTraderSpawnMinuteTicks = getInt("wandering-trader.spawn-minute-length", wanderingTraderSpawnMinuteTicks);
+        wanderingTraderSpawnDayTicks = getInt("wandering-trader.spawn-day-length", wanderingTraderSpawnDayTicks);
+        wanderingTraderSpawnChanceFailureIncrement = getInt("wandering-trader.spawn-chance-failure-increment", wanderingTraderSpawnChanceFailureIncrement);
+        wanderingTraderSpawnChanceMin = getInt("wandering-trader.spawn-chance-min", wanderingTraderSpawnChanceMin);
+        wanderingTraderSpawnChanceMax = getInt("wandering-trader.spawn-chance-max", wanderingTraderSpawnChanceMax);
+    }
+
     public short keepLoadedRange;
     private void keepLoadedRange() {
         keepLoadedRange = (short) (getInt("keep-spawn-loaded-range", Math.min(spigotConfig.viewDistance, 10)) * 16);
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 7215501f554f88c49fa35a063743caa8af46369c..0eddd793fa590a68bd094d417ffb90941532522c 100644
--- a/src/main/java/net/minecraft/world/entity/npc/WanderingTraderSpawner.java
+++ b/src/main/java/net/minecraft/world/entity/npc/WanderingTraderSpawner.java
@@ -43,43 +43,53 @@ public class WanderingTraderSpawner implements CustomSpawner {
 
     public WanderingTraderSpawner(ServerLevelData properties) {
         this.serverLevelData = properties;
-        this.tickDelay = 1200;
-        this.spawnDelay = properties.getWanderingTraderSpawnDelay();
-        this.spawnChance = properties.getWanderingTraderSpawnChance();
-        if (this.spawnDelay == 0 && this.spawnChance == 0) {
-            this.spawnDelay = 24000;
-            properties.setWanderingTraderSpawnDelay(this.spawnDelay);
-            this.spawnChance = 25;
-            properties.setWanderingTraderSpawnChance(this.spawnChance);
-        }
+        // Paper start
+        this.tickDelay = Integer.MIN_VALUE;
+        //this.spawnDelay = properties.getWanderingTraderSpawnDelay(); // Paper - This value is read from the world file only for the first spawn, after which vanilla uses a hardcoded value
+        //this.spawnChance = properties.getWanderingTraderSpawnChance(); // Paper - This value is read from the world file only for the first spawn, after which vanilla uses a hardcoded value
+        //if (this.spawnDelay == 0 && this.spawnChance == 0) {
+        //    this.spawnDelay = 24000;
+        //    properties.setWanderingTraderSpawnDelay(this.spawnDelay);
+        //    this.spawnChance = 25;
+        //    properties.setWanderingTraderSpawnChance(this.spawnChance);
+        //}
+        // Paper end
 
     }
 
     @Override
     public int tick(ServerLevel world, boolean spawnMonsters, boolean spawnAnimals) {
+        // Paper start
+        if (this.tickDelay == Integer.MIN_VALUE) {
+            this.tickDelay = world.paperConfig.wanderingTraderSpawnMinuteTicks;
+            this.spawnDelay = world.paperConfig.wanderingTraderSpawnDayTicks;
+            this.spawnChance = world.paperConfig.wanderingTraderSpawnChanceMin;
+        }
         if (!world.getGameRules().getBoolean(GameRules.RULE_DO_TRADER_SPAWNING)) {
             return 0;
-        } else if (--this.tickDelay > 0) {
+        } else if (this.tickDelay - 1 > 0) {
+            this.tickDelay = this.tickDelay - 1;
             return 0;
         } else {
-            this.tickDelay = 1200;
-            this.spawnDelay -= 1200;
-            this.serverLevelData.setWanderingTraderSpawnDelay(this.spawnDelay);
+            this.tickDelay = world.paperConfig.wanderingTraderSpawnMinuteTicks;
+            this.spawnDelay = this.spawnDelay - world.paperConfig.wanderingTraderSpawnMinuteTicks;
+            //this.serverLevelData.setWanderingTraderSpawnDelay(this.spawnDelay); // Paper - We don't need to save this value to disk if it gets set back to a hardcoded value anyways
             if (this.spawnDelay > 0) {
                 return 0;
             } else {
-                this.spawnDelay = 24000;
+                this.spawnDelay = world.paperConfig.wanderingTraderSpawnDayTicks;
                 if (!world.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING)) {
                     return 0;
                 } else {
                     int i = this.spawnChance;
 
-                    this.spawnChance = Mth.clamp(this.spawnChance + 25, (int) 25, (int) 75);
-                    this.serverLevelData.setWanderingTraderSpawnChance(this.spawnChance);
+                    this.spawnChance = Mth.clamp(i + world.paperConfig.wanderingTraderSpawnChanceFailureIncrement, world.paperConfig.wanderingTraderSpawnChanceMin, world.paperConfig.wanderingTraderSpawnChanceMax);
+                    //this.serverLevelData.setWanderingTraderSpawnChance(this.spawnChance); // Paper - We don't need to save this value to disk if it gets set back to a hardcoded value anyways
                     if (this.random.nextInt(100) > i) {
                         return 0;
                     } else if (this.spawn(world)) {
-                        this.spawnChance = 25;
+                        this.spawnChance = world.paperConfig.wanderingTraderSpawnChanceMin;
+                        // Paper end
                         return 1;
                     } else {
                         return 0;
