From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Sun, 28 Jun 2020 03:59:10 -0400
Subject: [PATCH] Fix Per World Difficulty / Remembering Difficulty

Fixes per world difficulty with /difficulty command and also
makes it so that the server keeps the last difficulty used instead
of restoring the server.properties every single load.

diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 84999223df47a9eccbcec4e1ea80786090d8d9c5..83848e30eeff52ccad591deb0d9e3a8ba1a122ed 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -850,7 +850,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
         worldloadlistener.stop();
         // CraftBukkit start
         // this.updateMobSpawningFlags();
-        worldserver.setSpawnSettings(this.isSpawningMonsters());
+        worldserver.setSpawnSettings(worldserver.serverLevelData.getDifficulty() != Difficulty.PEACEFUL && ((DedicatedServer) this).settings.getProperties().spawnMonsters); // Paper - per level difficulty (from setDifficulty(ServerLevel, Difficulty, boolean))
 
         this.forceTicks = false;
         // CraftBukkit end
@@ -1890,11 +1890,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
         }
     }
 
-    public void setDifficulty(Difficulty difficulty, boolean forceUpdate) {
-        if (forceUpdate || !this.worldData.isDifficultyLocked()) {
-            this.worldData.setDifficulty(this.worldData.isHardcore() ? Difficulty.HARD : difficulty);
-            this.updateMobSpawningFlags();
-            this.getPlayerList().getPlayers().forEach(this::sendDifficultyUpdate);
+    // Paper start - per level difficulty
+    public void setDifficulty(ServerLevel level, Difficulty difficulty, boolean forceUpdate) {
+        PrimaryLevelData worldData = level.serverLevelData;
+        if (forceUpdate || !worldData.isDifficultyLocked()) {
+            worldData.setDifficulty(worldData.isHardcore() ? Difficulty.HARD : difficulty);
+            level.setSpawnSettings(worldData.getDifficulty() != Difficulty.PEACEFUL && ((DedicatedServer) this).settings.getProperties().spawnMonsters);
+            // this.getPlayerList().getPlayers().forEach(this::sendDifficultyUpdate);
+            // Paper end - per level difficulty
         }
     }
 
@@ -1908,7 +1911,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
         while (iterator.hasNext()) {
             ServerLevel worldserver = (ServerLevel) iterator.next();
 
-            worldserver.setSpawnSettings(this.isSpawningMonsters());
+            worldserver.setSpawnSettings(worldserver.serverLevelData.getDifficulty() != Difficulty.PEACEFUL && ((DedicatedServer) this).settings.getProperties().spawnMonsters); // Paper - per level difficulty (from setDifficulty(ServerLevel, Difficulty, boolean))
         }
 
     }
diff --git a/src/main/java/net/minecraft/server/commands/DifficultyCommand.java b/src/main/java/net/minecraft/server/commands/DifficultyCommand.java
index 997a96a21440ae72696d68f8031ece4ba487d3ef..3d8584929cee000ae7df10c5bd94f35820e70294 100644
--- a/src/main/java/net/minecraft/server/commands/DifficultyCommand.java
+++ b/src/main/java/net/minecraft/server/commands/DifficultyCommand.java
@@ -49,7 +49,7 @@ public class DifficultyCommand {
         if (worldServer.getDifficulty() == difficulty) { // CraftBukkit
             throw DifficultyCommand.ERROR_ALREADY_DIFFICULT.create(difficulty.getKey());
         } else {
-            worldServer.serverLevelData.setDifficulty(difficulty); // CraftBukkit
+            minecraftserver.setDifficulty(worldServer, difficulty, true); // Paper - per level difficulty; don't skip other difficulty-changing logic (fix upstream's fix)
             source.sendSuccess(() -> {
                 return Component.translatable("commands.difficulty.success", difficulty.getDisplayName());
             }, true);
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
index fe1975675189c6d1a63c42b7959fa40b5ac95ef8..9a3e73a5c206b78dfcf6f41a47b614342e52acc8 100644
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
@@ -337,7 +337,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
 
     @Override
     public void forceDifficulty() {
-        this.setDifficulty(this.getProperties().difficulty, true);
+        // this.setDifficulty(this.getProperties().difficulty, true); // Paper - per level difficulty; Don't overwrite level.dat's difficulty, keep current
     }
 
     @Override
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index e3b238691a57e4d8317e84921f4c2e67ca994006..bc0380322ea9acd055ce844f23c6e38815e31831 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -3366,7 +3366,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
     public void handleChangeDifficulty(ServerboundChangeDifficultyPacket packet) {
         PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel());
         if (this.player.hasPermissions(2) || this.isSingleplayerOwner()) {
-            this.server.setDifficulty(packet.getDifficulty(), false);
+            // this.server.setDifficulty(packet.getDifficulty(), false); // Paper - per level difficulty; don't allow clients to change this
         }
     }
 
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index ca8eb3216c4331a95ab44f923f6b49641662505f..9905555f249db72649bde8401835dd816ed7b428 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -1000,8 +1000,8 @@ public final class CraftServer implements Server {
         org.spigotmc.SpigotConfig.init((File) this.console.options.valueOf("spigot-settings")); // Spigot
         this.console.paperConfigurations.reloadConfigs(this.console);
         for (ServerLevel world : this.console.getAllLevels()) {
-            world.serverLevelData.setDifficulty(config.difficulty);
-            world.setSpawnSettings(config.spawnMonsters);
+            // world.serverLevelData.setDifficulty(config.difficulty); // Paper - per level difficulty
+            world.setSpawnSettings(world.serverLevelData.getDifficulty() != Difficulty.PEACEFUL && config.spawnMonsters); // Paper - per level difficulty (from MinecraftServer#setDifficulty(ServerLevel, Difficulty, boolean))
 
             for (SpawnCategory spawnCategory : SpawnCategory.values()) {
                 if (CraftSpawnCategory.isValidForLimits(spawnCategory)) {
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
index c310a8bae84af6c20f7d976fb7cd683885407cc8..031c46e8cf1c49c4db2887dc32cbad75532f8faa 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -1170,7 +1170,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
 
     @Override
     public void setDifficulty(Difficulty difficulty) {
-        this.getHandle().serverLevelData.setDifficulty(net.minecraft.world.Difficulty.byId(difficulty.getValue()));
+        this.getHandle().getServer().setDifficulty(this.getHandle(), net.minecraft.world.Difficulty.byId(difficulty.getValue()), true); // Paper - per level difficulty; don't skip other difficulty-changing logic
     }
 
     @Override