diff --git a/Spigot-Server-Patches/Delay-unsafe-actions-until-after-entity-ticking-is-d.patch b/Spigot-Server-Patches/Delay-unsafe-actions-until-after-entity-ticking-is-d.patch new file mode 100644 index 0000000000..2725912fd0 --- /dev/null +++ b/Spigot-Server-Patches/Delay-unsafe-actions-until-after-entity-ticking-is-d.patch @@ -0,0 +1,75 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Aikar +Date: Sat, 11 Apr 2020 21:23:42 -0400 +Subject: [PATCH] Delay unsafe actions until after entity ticking is done + +This will help prevent many cases of unregistering entities during entity ticking + +diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java +index a9a2ce3d3f..24cb88559b 100644 +--- a/src/main/java/net/minecraft/server/PlayerChunk.java ++++ b/src/main/java/net/minecraft/server/PlayerChunk.java +@@ -0,0 +0,0 @@ public class PlayerChunk { + // Minecraft will apply the chunks tick lists to the world once the chunk got loaded, and then store the tick + // lists again inside the chunk once the chunk becomes inaccessible and set the chunk's needsSaving flag. + // These actions may however happen deferred, so we manually set the needsSaving flag already here. ++ // Paper start ++ ((WorldServer) chunk.world).doIfNotEntityTicking(() -> { // Paper + chunk.setNeedsSaving(true); + chunk.unloadCallback(); ++ }); // Paper + }); + } + }).exceptionally((throwable) -> { +diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java +index cd8266f675..84a3367b87 100644 +--- a/src/main/java/net/minecraft/server/WorldServer.java ++++ b/src/main/java/net/minecraft/server/WorldServer.java +@@ -0,0 +0,0 @@ public class WorldServer extends World { + private final Queue entitiesToAdd = Queues.newArrayDeque(); + public final List players = Lists.newArrayList(); // Paper - private -> public + boolean tickingEntities; ++ // Paper start ++ List afterEntityTickingTasks = Lists.newArrayList(); ++ public void doIfNotEntityTicking(java.lang.Runnable run) { ++ if (tickingEntities) { ++ afterEntityTickingTasks.add(run); ++ } else { ++ run.run(); ++ } ++ } ++ // Paper end + private final MinecraftServer server; + private final WorldNBTStorage dataManager; + public boolean savingDisabled; +@@ -0,0 +0,0 @@ public class WorldServer extends World { + timings.entityTick.stopTiming(); // Spigot + + this.tickingEntities = false; ++ // Paper start ++ for (java.lang.Runnable run : this.afterEntityTickingTasks) { ++ try { ++ run.run(); ++ } catch (Exception e) { ++ LOGGER.error("Error in After Entity Ticking Task", e); ++ } ++ } ++ this.afterEntityTickingTasks.clear(); ++ // Paper end + this.getMinecraftServer().midTickLoadChunks(); // Paper + + try (co.aikar.timings.Timing ignored = this.timings.newEntities.startTiming()) { // Paper - timings +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +index 1fbb1344fc..f56131e3a5 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +@@ -0,0 +0,0 @@ public class CraftWorld implements World { + + CompletableFuture ret = new CompletableFuture<>(); + this.world.getChunkProvider().getChunkAtAsynchronously(x, z, gen, (net.minecraft.server.Chunk chunk) -> { +- ret.complete(chunk == null ? null : chunk.bukkitChunk); ++ this.world.doIfNotEntityTicking(() -> ret.complete(chunk == null ? null : chunk.bukkitChunk)); + }); + + return ret; +-- \ No newline at end of file diff --git a/Spigot-Server-Patches/Speed-up-processing-of-chunk-loads-and-generation.patch b/Spigot-Server-Patches/Speed-up-processing-of-chunk-loads-and-generation.patch index 73311ce667..069fab95b1 100644 --- a/Spigot-Server-Patches/Speed-up-processing-of-chunk-loads-and-generation.patch +++ b/Spigot-Server-Patches/Speed-up-processing-of-chunk-loads-and-generation.patch @@ -176,7 +176,7 @@ index e61ddeb1ff..92c9ab43d7 100644 }); }; diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java -index c7ec8cbc11..43573287f2 100644 +index c7ec8cbc11..cd8266f675 100644 --- a/src/main/java/net/minecraft/server/WorldServer.java +++ b/src/main/java/net/minecraft/server/WorldServer.java @@ -0,0 +0,0 @@ public class WorldServer extends World { @@ -195,16 +195,6 @@ index c7ec8cbc11..43573287f2 100644 this.ticking = false; gameprofilerfiller.exitEnter("entities"); boolean flag3 = true || !this.players.isEmpty() || !this.getForceLoadedChunks().isEmpty(); // CraftBukkit - this prevents entity cleanup, other issues on servers with no players -@@ -0,0 +0,0 @@ public class WorldServer extends World { - org.spigotmc.ActivationRange.activateEntities(this); // Spigot - timings.entityTick.startTiming(); // Spigot - TimingHistory.entityTicks += this.globalEntityList.size(); // Paper -+ int entitiesTicked = 0; // Paper - while (objectiterator.hasNext()) { -+ if (entitiesTicked++ % 100 == 0) this.getMinecraftServer().midTickLoadChunks(); // Paper - Entry entry = (Entry) objectiterator.next(); - Entity entity1 = (Entity) entry.getValue(); - Entity entity2 = entity1.getVehicle(); @@ -0,0 +0,0 @@ public class WorldServer extends World { timings.entityTick.stopTiming(); // Spigot