From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: jmp <jasonpenilla2@me.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 97870622e41cca36d9c7493bfad796f35f3831f4..5a451cc855de57f79a57670ba38e3af2343cb510 100644
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
@@ -699,4 +699,17 @@ public class PaperWorldConfig {
             log("The Ender Dragon will be removed if she already exists without a portal.");
         }
     }
+
+    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);
+    }
 }
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 c297051bc48ff20a6764496cbf11eef601761d13..9074d57e1576db2da3e4c76add4f7e07e5567879 100644
--- a/src/main/java/net/minecraft/world/entity/npc/WanderingTraderSpawner.java
+++ b/src/main/java/net/minecraft/world/entity/npc/WanderingTraderSpawner.java
@@ -30,49 +30,59 @@ public class WanderingTraderSpawner implements CustomSpawner {
 
     private final Random random = new Random();
     private final ServerLevelData serverLevelData;
-    private int tickDelay;
-    private int spawnDelay;
-    private int spawnChance;
+    private int tickDelay; public final int getMinuteTimer() { return this.tickDelay; } public final void setMinuteTimer(int x) { this.tickDelay = x; } // Paper - OBFHELPER
+    private int spawnDelay; public final int getDayTimer() { return this.spawnDelay; } public final void setDayTimer(int x) { this.spawnDelay = x; } // Paper - OBFHELPER
+    private int spawnChance; public final int getSpawnChance() { return this.spawnChance; } public final void setSpawnChance(int x) { this.spawnChance = x; } // Paper - OBFHELPER
 
     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.setMinuteTimer(Integer.MIN_VALUE);
+        //this.d = iworlddataserver.v(); // Paper - This value is read from the world file only for the first spawn, after which vanilla uses a hardcoded value
+        //this.e = iworlddataserver.w(); // Paper - This value is read from the world file only for the first spawn, after which vanilla uses a hardcoded value
+        //if (this.d == 0 && this.e == 0) {
+        //    this.d = 24000;
+        //    iworlddataserver.g(this.d);
+        //    this.e = 25;
+        //    iworlddataserver.h(this.e);
+        //}
+        // Paper end
 
     }
 
     @Override
     public int tick(ServerLevel world, boolean spawnMonsters, boolean spawnAnimals) {
+        // Paper start
+        if (this.getMinuteTimer() == Integer.MIN_VALUE) {
+            this.setMinuteTimer(world.paperConfig.wanderingTraderSpawnMinuteTicks);
+            this.setDayTimer(world.paperConfig.wanderingTraderSpawnDayTicks);
+            this.setSpawnChance(world.paperConfig.wanderingTraderSpawnChanceMin);
+        }
         if (!world.getGameRules().getBoolean(GameRules.RULE_DO_TRADER_SPAWNING)) {
             return 0;
-        } else if (--this.tickDelay > 0) {
+        } else if (this.getMinuteTimer() - 1 > 0) {
+            this.setMinuteTimer(this.getMinuteTimer() - 1);
             return 0;
         } else {
-            this.tickDelay = 1200;
-            this.spawnDelay -= 1200;
-            this.serverLevelData.setWanderingTraderSpawnDelay(this.spawnDelay);
-            if (this.spawnDelay > 0) {
+            this.setMinuteTimer(world.paperConfig.wanderingTraderSpawnMinuteTicks);
+            this.setDayTimer(getDayTimer() - world.paperConfig.wanderingTraderSpawnMinuteTicks);
+            //this.b.g(this.d); // Paper - We don't need to save this value to disk if it gets set back to a hardcoded value anyways
+            if (this.getDayTimer() > 0) {
                 return 0;
             } else {
-                this.spawnDelay = 24000;
+                this.setDayTimer(world.paperConfig.wanderingTraderSpawnDayTicks);
                 if (!world.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING)) {
                     return 0;
                 } else {
-                    int i = this.spawnChance;
+                    int i = this.getSpawnChance();
 
-                    this.spawnChance = Mth.clamp(this.spawnChance + 25, 25, 75);
-                    this.serverLevelData.setWanderingTraderSpawnChance(this.spawnChance);
+                    this.setSpawnChance(Mth.clamp(i + world.paperConfig.wanderingTraderSpawnChanceFailureIncrement, world.paperConfig.wanderingTraderSpawnChanceMin, world.paperConfig.wanderingTraderSpawnChanceMax));
+                    //this.b.h(this.e); // 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.setSpawnChance(world.paperConfig.wanderingTraderSpawnChanceMin);
+                        // Paper end
                         return 1;
                     } else {
                         return 0;