From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <spottedleaf@spottedleaf.dev>
Date: Thu, 18 Jun 2020 10:04:26 -0700
Subject: [PATCH] Allow Entities to be removed from a world while ticking

Fixes issues like disconnecting players while ticking them, or
issues where teleporting players across worlds while ticking.

Also allows us to run mid tick while ticking entities.

diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java
index 74cd5d412b285469f6f77bef73e65b139179992e..1f7cb91cc4f8d77b2ed0ffbf3a6063fc127f3871 100644
--- a/src/main/java/net/minecraft/server/WorldServer.java
+++ b/src/main/java/net/minecraft/server/WorldServer.java
@@ -61,7 +61,7 @@ public class WorldServer extends World implements GeneratorAccessSeed {
 
     public static final BlockPosition a = new BlockPosition(100, 50, 0);
     private static final Logger LOGGER = LogManager.getLogger();
-    public final Int2ObjectMap<Entity> entitiesById = new Int2ObjectLinkedOpenHashMap();
+    public final Int2ObjectMap<Entity> entitiesById = new Int2ObjectLinkedOpenHashMap(); final com.tuinity.tuinity.util.maplist.IteratorSafeOrderedReferenceSet<Entity> entitiesForIteration = new com.tuinity.tuinity.util.maplist.IteratorSafeOrderedReferenceSet<>(2048, 0.5f, 2048, 0.2, true); // Tuinity - make removing entities while ticking safe
     private final Map<UUID, Entity> entitiesByUUID = Maps.newHashMap();
     private final Queue<Entity> entitiesToAdd = Queues.newArrayDeque();
     public final List<EntityPlayer> players = Lists.newArrayList(); // Paper - private -> public
@@ -85,7 +85,7 @@ public class WorldServer extends World implements GeneratorAccessSeed {
     private final PortalTravelAgent portalTravelAgent;
     private final TickListServer<Block> nextTickListBlock;
     private final TickListServer<FluidType> nextTickListFluid;
-    private final Set<NavigationAbstract> navigators;
+    private final Set<NavigationAbstract> navigators; final com.tuinity.tuinity.util.maplist.IteratorSafeOrderedReferenceSet<NavigationAbstract> navigatorsForIteration = new com.tuinity.tuinity.util.maplist.IteratorSafeOrderedReferenceSet<>(2048, 0.5f, 2048, 0.2, true); // Tuinity - make removing entities while ticking safe
     protected final PersistentRaid persistentRaid;
     private final ObjectLinkedOpenHashSet<BlockActionData> L;
     private boolean ticking;
@@ -833,13 +833,12 @@ public class WorldServer extends World implements GeneratorAccessSeed {
             }
 
             this.tickingEntities = true;
-            ObjectIterator objectiterator = this.entitiesById.int2ObjectEntrySet().iterator();
+            com.tuinity.tuinity.util.maplist.IteratorSafeOrderedReferenceSet.Iterator<Entity> objectiterator = this.entitiesForIteration.iterator(); // Tuinity
 
             org.spigotmc.ActivationRange.activateEntities(this); // Spigot
             timings.entityTick.startTiming(); // Spigot
             while (objectiterator.hasNext()) {
-                Entry<Entity> entry = (Entry) objectiterator.next();
-                Entity entity = (Entity) entry.getValue();
+                Entity entity = (Entity) objectiterator.next(); // Tuinity
                 Entity entity1 = entity.getVehicle();
 
                 /* CraftBukkit start - We prevent spawning in general, so this butchering is not needed
@@ -875,7 +874,7 @@ public class WorldServer extends World implements GeneratorAccessSeed {
                 gameprofilerfiller.enter("remove");
                 if (entity.dead) {
                     this.removeEntityFromChunk(entity);
-                    objectiterator.remove();
+                    this.entitiesById.remove(entity.getId()); // Tuinity
                     this.unregisterEntity(entity);
                 }
 
@@ -883,6 +882,7 @@ public class WorldServer extends World implements GeneratorAccessSeed {
             }
             timings.entityTick.stopTiming(); // Spigot
 
+            objectiterator.finishedIterating(); // Tuinity
             this.tickingEntities = false;
             // Paper start
             for (java.lang.Runnable run : this.afterEntityTickingTasks) {
@@ -1662,7 +1662,7 @@ public class WorldServer extends World implements GeneratorAccessSeed {
                 Entity entity = (Entity) iterator.next();
 
                 if (!(entity instanceof EntityPlayer)) {
-                    if (this.tickingEntities) {
+                    if (false && this.tickingEntities) { // Tuinity
                         throw (IllegalStateException) SystemUtils.c((Throwable) (new IllegalStateException("Removing entity while ticking!")));
                     }
 
@@ -1690,6 +1690,7 @@ public class WorldServer extends World implements GeneratorAccessSeed {
 
     public void unregisterEntity(Entity entity) {
         org.spigotmc.AsyncCatcher.catchOp("entity unregister"); // Spigot
+        this.entitiesForIteration.remove(entity); // Tuinity
         // Paper start - fix entity registration issues
         if (entity instanceof EntityComplexPart) {
             // Usually this is a no-op for complex parts, and ID's should be removed, but go ahead and remove it anyways
@@ -1756,12 +1757,16 @@ public class WorldServer extends World implements GeneratorAccessSeed {
         this.getScoreboard().a(entity);
         // CraftBukkit start - SPIGOT-5278
         if (entity instanceof EntityDrowned) {
-            this.navigators.remove(((EntityDrowned) entity).navigationWater);
-            this.navigators.remove(((EntityDrowned) entity).navigationLand);
+            // Tuinity start
+            this.navigators.remove(((EntityDrowned) entity).navigationWater); this.navigatorsForIteration.remove(((EntityDrowned) entity).navigationWater);
+            this.navigators.remove(((EntityDrowned) entity).navigationLand); this.navigatorsForIteration.remove(((EntityDrowned) entity).navigationLand);
+            // Tuinity end
         } else
         // CraftBukkit end
         if (entity instanceof EntityInsentient) {
-            this.navigators.remove(((EntityInsentient) entity).getNavigation());
+            // Tuinity start
+            this.navigators.remove(((EntityInsentient) entity).getNavigation()); this.navigatorsForIteration.remove(((EntityInsentient) entity).getNavigation());
+            // Tuinity end
         }
         new com.destroystokyo.paper.event.entity.EntityRemoveFromWorldEvent(entity.getBukkitEntity()).callEvent(); // Paper - fire while valid
         entity.valid = false; // CraftBukkit
@@ -1777,7 +1782,7 @@ public class WorldServer extends World implements GeneratorAccessSeed {
             return;
         }
         // Paper end
-        if (this.tickingEntities) {
+        if (false && this.tickingEntities) { // Tuinity
             if (!entity.isQueuedForRegister) { // Paper
                 this.entitiesToAdd.add(entity);
                 entity.isQueuedForRegister = true; // Paper
@@ -1785,6 +1790,7 @@ public class WorldServer extends World implements GeneratorAccessSeed {
         } else {
             entity.isQueuedForRegister = false; // Paper
             this.entitiesById.put(entity.getId(), entity);
+            this.entitiesForIteration.add(entity); // Tuinity
             if (entity instanceof EntityEnderDragon) {
                 EntityComplexPart[] aentitycomplexpart = ((EntityEnderDragon) entity).eJ();
                 int i = aentitycomplexpart.length;
@@ -1793,6 +1799,7 @@ public class WorldServer extends World implements GeneratorAccessSeed {
                     EntityComplexPart entitycomplexpart = aentitycomplexpart[j];
 
                     this.entitiesById.put(entitycomplexpart.getId(), entitycomplexpart);
+                    this.entitiesForIteration.add(entitycomplexpart); // Tuinity
                 }
             }
 
@@ -1817,12 +1824,16 @@ public class WorldServer extends World implements GeneratorAccessSeed {
             // this.getChunkProvider().addEntity(entity); // Paper - moved down below valid=true
             // CraftBukkit start - SPIGOT-5278
             if (entity instanceof EntityDrowned) {
-                this.navigators.add(((EntityDrowned) entity).navigationWater);
-                this.navigators.add(((EntityDrowned) entity).navigationLand);
+                // Tuinity start
+                this.navigators.add(((EntityDrowned) entity).navigationWater); this.navigatorsForIteration.add(((EntityDrowned) entity).navigationWater);
+                this.navigators.add(((EntityDrowned) entity).navigationLand); this.navigatorsForIteration.add(((EntityDrowned) entity).navigationLand);
+                // Tuinity end
             } else
             // CraftBukkit end
             if (entity instanceof EntityInsentient) {
-                this.navigators.add(((EntityInsentient) entity).getNavigation());
+                // Tuinity start
+                this.navigators.add(((EntityInsentient) entity).getNavigation()); this.navigatorsForIteration.add(((EntityInsentient) entity).getNavigation());
+                // Tuinity end
             }
             entity.valid = true; // CraftBukkit
             this.getChunkProvider().addEntity(entity); // Paper - from above to be below valid=true
@@ -1838,7 +1849,7 @@ public class WorldServer extends World implements GeneratorAccessSeed {
     }
 
     public void removeEntity(Entity entity) {
-        if (this.tickingEntities) {
+        if (false && this.tickingEntities) {  // Tuinity
             throw (IllegalStateException) SystemUtils.c((Throwable) (new IllegalStateException("Removing entity while ticking!")));
         } else {
             this.removeEntityFromChunk(entity);
@@ -1942,7 +1953,9 @@ public class WorldServer extends World implements GeneratorAccessSeed {
 
         if (VoxelShapes.c(voxelshape, voxelshape1, OperatorBoolean.NOT_SAME)) {
             boolean wasTicking = this.tickingEntities; this.tickingEntities = true; // Paper
-            Iterator iterator = this.navigators.iterator();
+            // Tuinity start
+            com.tuinity.tuinity.util.maplist.IteratorSafeOrderedReferenceSet.Iterator iterator = this.navigatorsForIteration.iterator();
+            try { // Tuinity end
 
             while (iterator.hasNext()) {
                 // CraftBukkit start - fix SPIGOT-6362
@@ -1962,6 +1975,9 @@ public class WorldServer extends World implements GeneratorAccessSeed {
                     navigationabstract.b(blockposition);
                 }
             }
+            } finally { // Tuinity start
+                iterator.finishedIterating();
+            } // Tuinity end
 
             this.tickingEntities = wasTicking; // Paper
         }
