From 826643c606061a9ee6c5dc7b17aac16c2e1bab56 Mon Sep 17 00:00:00 2001 From: Travis Watkins Date: Mon, 10 Feb 2014 14:59:44 -0600 Subject: [PATCH] Handle removing entity while ticking. Fixes BUKKIT-1331 If a plugin causes an entity to be removed from the world while the world is ticking entities the ticking loop gets out of sync and fails due to trying to go beyond the end of the entity array. To ensure this doesn't happen we store the loop position as a field so we can fix it up in the entity remove method just like the tick method does when it removes an entity. --- src/main/java/net/minecraft/server/World.java | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java index f850a0f136..419149ed28 100644 --- a/src/main/java/net/minecraft/server/World.java +++ b/src/main/java/net/minecraft/server/World.java @@ -14,7 +14,6 @@ import java.util.concurrent.Callable; import org.bukkit.Bukkit; import org.bukkit.craftbukkit.util.CraftMagicNumbers; import org.bukkit.craftbukkit.util.LongHashSet; -import org.bukkit.craftbukkit.util.UnsafeList; import org.bukkit.generator.ChunkGenerator; import org.bukkit.craftbukkit.CraftServer; import org.bukkit.craftbukkit.CraftWorld; @@ -71,6 +70,7 @@ public abstract class World implements IBlockAccess { public long ticksPerAnimalSpawns; public long ticksPerMonsterSpawns; public boolean populating; + private int tickPosition; // CraftBukkit end private ArrayList M; private boolean N; @@ -974,7 +974,14 @@ public abstract class World implements IBlockAccess { this.getChunkAt(i, j).b(entity); } - this.entityList.remove(entity); + // CraftBukkit start - Decrement loop variable field if we've already ticked this entity + int index = this.entityList.indexOf(entity); + if (index <= this.tickPosition) { + this.tickPosition--; + } + this.entityList.remove(index); + // CraftBukkit end + this.b(entity); } @@ -1182,10 +1189,11 @@ public abstract class World implements IBlockAccess { this.f.clear(); this.methodProfiler.c("regular"); - for (i = 0; i < this.entityList.size(); ++i) { - entity = (Entity) this.entityList.get(i); + // CraftBukkit start - Use field for loop variable + for (this.tickPosition = 0; this.tickPosition < this.entityList.size(); ++this.tickPosition) { + entity = (Entity) this.entityList.get(this.tickPosition); - // CraftBukkit start - Don't tick entities in chunks queued for unload + // Don't tick entities in chunks queued for unload ChunkProviderServer chunkProviderServer = ((WorldServer) this).chunkProviderServer; if (chunkProviderServer.unloadQueue.contains(MathHelper.floor(entity.locX) >> 4, MathHelper.floor(entity.locZ) >> 4)) { continue; @@ -1222,7 +1230,7 @@ public abstract class World implements IBlockAccess { this.getChunkAt(j, k).b(entity); } - this.entityList.remove(i--); + this.entityList.remove(this.tickPosition--); // CraftBukkit - Use field for loop variable this.b(entity); }