mirror of
https://github.com/PaperMC/Paper.git
synced 2024-11-28 19:22:50 +01:00
Fix race condition with chunks, dead tile entities
Fixes PaperMC/Paper#883 same issue as MinecraftForge/MinecraftForge#4386 A more detailed anaylsis of what is probably going on, courtesy of @bs2609 and the MCForge Issue Tracker is: When a chunk is unloaded, the entities and tile entities it contains are marked for removal. The actual removal (from the world) occurs later, when the world ticks its entities. Conversely, when a chunk is loaded, it generally adds its entities to the world promptly, without queuing. Here's the normal sequence of events: Chunk unloaded Old entities removed Chunk loaded New entities added However, what can happen: Chunk unloaded Chunk loaded New entities added Old entities removed This occurs when an unloaded chunk is reloaded before its corresponding entities have been removed.
This commit is contained in:
parent
2104c3a683
commit
6d9375d222
1 changed files with 14 additions and 81 deletions
|
@ -1,32 +1,9 @@
|
||||||
From f2bcb786e5eed44c74b3ebc8573f5735e589cd6d Mon Sep 17 00:00:00 2001
|
From ed74bb4fedd0ddfc6385eb36ae705420be3b6c50 Mon Sep 17 00:00:00 2001
|
||||||
From: mezz <tehgeek@gmail.com>
|
From: mezz <tehgeek@gmail.com>
|
||||||
Date: Wed, 9 Aug 2017 17:51:22 -0500
|
Date: Wed, 9 Aug 2017 17:51:22 -0500
|
||||||
Subject: [PATCH] Fix MC-117075: TE Unload Lag Spike
|
Subject: [PATCH] Fix MC-117075: TE Unload Lag Spike
|
||||||
|
|
||||||
|
|
||||||
diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java
|
|
||||||
index ed595955..228792fa 100644
|
|
||||||
--- a/src/main/java/net/minecraft/server/Chunk.java
|
|
||||||
+++ b/src/main/java/net/minecraft/server/Chunk.java
|
|
||||||
@@ -823,6 +823,9 @@ public class Chunk {
|
|
||||||
|
|
||||||
public void removeEntities() {
|
|
||||||
this.j = false;
|
|
||||||
+ // Paper start - Fix MC-117075: TE Unload Lag Spike
|
|
||||||
+ this.world.markTileEntitiesForRemoval(this);
|
|
||||||
+ /*
|
|
||||||
Iterator iterator = this.tileEntities.values().iterator();
|
|
||||||
|
|
||||||
while (iterator.hasNext()) {
|
|
||||||
@@ -842,6 +845,8 @@ public class Chunk {
|
|
||||||
|
|
||||||
this.world.b(tileentity);
|
|
||||||
}
|
|
||||||
+ */
|
|
||||||
+ // Paper end
|
|
||||||
|
|
||||||
List[] aentityslice = this.entitySlices; // Spigot
|
|
||||||
int i = aentityslice.length;
|
|
||||||
diff --git a/src/main/java/net/minecraft/server/ChunkCoordIntPair.java b/src/main/java/net/minecraft/server/ChunkCoordIntPair.java
|
diff --git a/src/main/java/net/minecraft/server/ChunkCoordIntPair.java b/src/main/java/net/minecraft/server/ChunkCoordIntPair.java
|
||||||
index 23944088..e8d1a1c6 100644
|
index 23944088..e8d1a1c6 100644
|
||||||
--- a/src/main/java/net/minecraft/server/ChunkCoordIntPair.java
|
--- a/src/main/java/net/minecraft/server/ChunkCoordIntPair.java
|
||||||
|
@ -40,66 +17,22 @@ index 23944088..e8d1a1c6 100644
|
||||||
return (long) i & 4294967295L | ((long) j & 4294967295L) << 32;
|
return (long) i & 4294967295L | ((long) j & 4294967295L) << 32;
|
||||||
}
|
}
|
||||||
diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java
|
diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java
|
||||||
index 12938b9f..e4fd7fa7 100644
|
index 12938b9f..4585f2ef 100644
|
||||||
--- a/src/main/java/net/minecraft/server/World.java
|
--- a/src/main/java/net/minecraft/server/World.java
|
||||||
+++ b/src/main/java/net/minecraft/server/World.java
|
+++ b/src/main/java/net/minecraft/server/World.java
|
||||||
@@ -147,6 +147,7 @@ public abstract class World implements IBlockAccess {
|
@@ -1576,7 +1576,11 @@ public abstract class World implements IBlockAccess {
|
||||||
private org.spigotmc.TickLimiter tileLimiter;
|
timings.tileEntityTick.startTiming(); // Spigot
|
||||||
private int tileTickPosition;
|
// CraftBukkit start - From below, clean up tile entities before ticking them
|
||||||
public final Map<Explosion.CacheKey, Float> explosionDensityCache = new HashMap<>(); // Paper - Optimize explosions
|
if (!this.tileEntityListUnload.isEmpty()) {
|
||||||
+ private it.unimi.dsi.fastutil.longs.LongCollection tileEntitiesChunkToBeRemoved = new it.unimi.dsi.fastutil.longs.LongOpenHashSet(); // Paper - Fix MC-117075: TE Unload Lag Spike
|
- this.tileEntityListTick.removeAll(this.tileEntityListUnload);
|
||||||
|
+ // Paper start - Use alternate implementation with faster contains
|
||||||
public CraftWorld getWorld() {
|
+ java.util.Set<TileEntity> toRemove = java.util.Collections.newSetFromMap(new java.util.IdentityHashMap<>());
|
||||||
return this.world;
|
+ toRemove.addAll(tileEntityListUnload);
|
||||||
@@ -1581,6 +1582,7 @@ public abstract class World implements IBlockAccess {
|
+ this.tileEntityListTick.removeAll(toRemove);
|
||||||
|
+ // Paper end
|
||||||
|
//this.tileEntityList.removeAll(this.tileEntityListUnload); // Paper - remove unused list
|
||||||
this.tileEntityListUnload.clear();
|
this.tileEntityListUnload.clear();
|
||||||
}
|
}
|
||||||
// CraftBukkit end
|
|
||||||
+ this.removeTileEntitiesForRemovedChunks(); // Paper - Fix MC-117075: TE Unload Lag Spike
|
|
||||||
|
|
||||||
// Spigot start
|
|
||||||
// Iterator iterator = this.tileEntityListTick.iterator();
|
|
||||||
@@ -3261,4 +3263,40 @@ public abstract class World implements IBlockAccess {
|
|
||||||
public BlockPosition a(String s, BlockPosition blockposition, boolean flag) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
+
|
|
||||||
+ // Paper start - Fix MC-117075: TE Unload Lag Spike
|
|
||||||
+ public void markTileEntitiesForRemoval(Chunk chunk) {
|
|
||||||
+ if (!chunk.getTileEntities().isEmpty()) {
|
|
||||||
+ long pos = net.minecraft.server.ChunkCoordIntPair.asLong(chunk.locX, chunk.locZ);
|
|
||||||
+ this.tileEntitiesChunkToBeRemoved.add(pos);
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ private void removeTileEntitiesForRemovedChunks() {
|
|
||||||
+ if (!this.tileEntitiesChunkToBeRemoved.isEmpty()) {
|
|
||||||
+ java.util.function.Predicate<TileEntity> isInChunkOrNull = (tileEntity) -> {
|
|
||||||
+ if (tileEntity == null) {
|
|
||||||
+ return true;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ BlockPosition tilePos = tileEntity.getPosition();
|
|
||||||
+ long tileChunkPos = net.minecraft.server.ChunkCoordIntPair.asLong(tilePos.getX() >> 4, tilePos.getZ() >> 4);
|
|
||||||
+ final boolean willRemove = this.tileEntitiesChunkToBeRemoved.contains(tileChunkPos);
|
|
||||||
+ // Brought over from Chunk#removeEntities
|
|
||||||
+ if (willRemove && tileEntity instanceof IInventory) {
|
|
||||||
+ for (org.bukkit.entity.HumanEntity human : Lists.newArrayList(((IInventory) tileEntity).getViewers())) {
|
|
||||||
+ if (human instanceof org.bukkit.craftbukkit.entity.CraftHumanEntity) {
|
|
||||||
+ ((org.bukkit.craftbukkit.entity.CraftHumanEntity) human).getHandle().closeInventory();
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return willRemove;
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ this.tileEntityListTick.removeIf(isInChunkOrNull);
|
|
||||||
+ this.tileEntitiesChunkToBeRemoved.clear();
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ // Paper end
|
|
||||||
}
|
|
||||||
--
|
--
|
||||||
2.14.1
|
2.14.1.windows.1
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue