Delay unsafe actions until after entity ticking is done - Fixes #3114

This will help prevent many cases of unregistering entities during entity ticking

Currently delays Chunk Unloads and Async Chunk load callbacks

Also dropped mid ticking chunk tasks during entity ticking to reduce this risk
This commit is contained in:
Aikar 2020-04-11 21:31:40 -04:00
parent 5553e6b3e3
commit bc17ce69d4
No known key found for this signature in database
GPG key ID: 401ADFC9891FAAFE
2 changed files with 81 additions and 14 deletions

View file

@ -1,4 +1,4 @@
From a7827fe0ade0d2345c605f97c29de347ae9b9068 Mon Sep 17 00:00:00 2001
From aa7f8a74b12bd4d919113eac6132707eddf0ef1a Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Thu, 9 Apr 2020 00:09:26 -0400
Subject: [PATCH] Speed up processing of chunk loads and generation
@ -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
@@ -420,6 +420,7 @@ public class WorldServer extends World {
@ -195,17 +195,7 @@ 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
@@ -470,7 +472,9 @@ 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<Entity> entry = (Entry) objectiterator.next();
Entity entity1 = (Entity) entry.getValue();
Entity entity2 = entity1.getVehicle();
@@ -517,6 +521,7 @@ public class WorldServer extends World {
@@ -517,6 +519,7 @@ public class WorldServer extends World {
timings.entityTick.stopTiming(); // Spigot
this.tickingEntities = false;
@ -213,7 +203,7 @@ index c7ec8cbc11..43573287f2 100644
try (co.aikar.timings.Timing ignored = this.timings.newEntities.startTiming()) { // Paper - timings
while ((entity = (Entity) this.entitiesToAdd.poll()) != null) {
@@ -527,7 +532,9 @@ public class WorldServer extends World {
@@ -527,7 +530,9 @@ public class WorldServer extends World {
gameprofilerfiller.exit();
timings.tickEntities.stopTiming(); // Spigot

View file

@ -0,0 +1,77 @@
From aa1a9266fcdeb4ffc727142df482ebca0d03828d Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
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
@@ -490,8 +490,11 @@ 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
@@ -60,6 +60,16 @@ public class WorldServer extends World {
private final Queue<Entity> entitiesToAdd = Queues.newArrayDeque();
public final List<EntityPlayer> players = Lists.newArrayList(); // Paper - private -> public
boolean tickingEntities;
+ // Paper start
+ List<java.lang.Runnable> 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;
@@ -519,6 +529,16 @@ 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
@@ -2469,7 +2469,7 @@ public class CraftWorld implements World {
CompletableFuture<Chunk> 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;
--
2.25.1