diff --git a/CraftBukkit-Patches/0097-Optimize-NextTickList-processing.patch b/CraftBukkit-Patches/0097-Optimize-NextTickList-processing.patch new file mode 100644 index 000000000..d956a4010 --- /dev/null +++ b/CraftBukkit-Patches/0097-Optimize-NextTickList-processing.patch @@ -0,0 +1,308 @@ +From dc7f9783926c0f66e83fcca98161f4d513dccf04 Mon Sep 17 00:00:00 2001 +From: Mike Primm +Date: Sat, 18 Jan 2014 13:54:38 -0600 +Subject: [PATCH] Optimize NextTickList processing + + +diff --git a/src/main/java/net/minecraft/server/NextTickListEntry.java b/src/main/java/net/minecraft/server/NextTickListEntry.java +index 06934a1..d90acfc 100644 +--- a/src/main/java/net/minecraft/server/NextTickListEntry.java ++++ b/src/main/java/net/minecraft/server/NextTickListEntry.java +@@ -30,7 +30,7 @@ public class NextTickListEntry implements Comparable { + } + + public int hashCode() { +- return (this.a * 1024 * 1024 + this.c * 1024 + this.b) * 256; ++ return (this.a * 257) ^ this.b ^ (this.c * 60217); // Spigot - better hash + } + + public NextTickListEntry a(long i) { +diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java +index 9672508..74a3294 100644 +--- a/src/main/java/net/minecraft/server/WorldServer.java ++++ b/src/main/java/net/minecraft/server/WorldServer.java +@@ -29,8 +29,8 @@ public class WorldServer extends World implements org.bukkit.BlockChangeDelegate + private final MinecraftServer server; + public EntityTracker tracker; // CraftBukkit - private final -> public + private final PlayerChunkMap manager; +- private Set M; +- private TreeSet N; ++ private org.bukkit.craftbukkit.util.LongObjectHashMap> tickEntriesByChunk; // Spigot - switch to something better for chunk-wise access ++ private TreeSet tickEntryQueue; // Spigot public ChunkProviderServer chunkProviderServer; + public ChunkProviderServer chunkProviderServer; + public boolean savingDisabled; + private boolean O; +@@ -40,7 +40,8 @@ public class WorldServer extends World implements org.bukkit.BlockChangeDelegate + private NoteDataList[] S = new NoteDataList[] { new NoteDataList((EmptyClass2) null), new NoteDataList((EmptyClass2) null)}; + private int T; + private static final StructurePieceTreasure[] U = new StructurePieceTreasure[] { new StructurePieceTreasure(Items.STICK, 0, 1, 3, 10), new StructurePieceTreasure(Item.getItemOf(Blocks.WOOD), 0, 1, 3, 10), new StructurePieceTreasure(Item.getItemOf(Blocks.LOG), 0, 1, 3, 10), new StructurePieceTreasure(Items.STONE_AXE, 0, 1, 1, 3), new StructurePieceTreasure(Items.WOOD_AXE, 0, 1, 1, 5), new StructurePieceTreasure(Items.STONE_PICKAXE, 0, 1, 1, 3), new StructurePieceTreasure(Items.WOOD_PICKAXE, 0, 1, 1, 5), new StructurePieceTreasure(Items.APPLE, 0, 2, 3, 5), new StructurePieceTreasure(Items.BREAD, 0, 2, 3, 3), new StructurePieceTreasure(Item.getItemOf(Blocks.LOG2), 0, 1, 3, 10)}; +- private List V = new ArrayList(); ++ private ArrayList pendingTickEntries = new ArrayList(); // Spigot ++ private int nextPendingTickEntry; // Spigot + private IntHashMap entitiesById; + + // CraftBukkit start +@@ -59,13 +60,15 @@ public class WorldServer extends World implements org.bukkit.BlockChangeDelegate + this.entitiesById = new IntHashMap(); + } + +- if (this.M == null) { +- this.M = new HashSet(); ++ // Spigot start ++ if (this.tickEntriesByChunk == null) { ++ this.tickEntriesByChunk = new org.bukkit.craftbukkit.util.LongObjectHashMap>(); + } + +- if (this.N == null) { +- this.N = new TreeSet(); ++ if (this.tickEntryQueue == null) { ++ this.tickEntryQueue = new TreeSet(); + } ++ // Spigot end + + this.Q = new org.bukkit.craftbukkit.CraftTravelAgent(this); // CraftBukkit + this.scoreboard = new ScoreboardServer(minecraftserver); +@@ -445,9 +448,16 @@ public class WorldServer extends World implements org.bukkit.BlockChangeDelegate + } + + public boolean a(int i, int j, int k, Block block) { +- NextTickListEntry nextticklistentry = new NextTickListEntry(i, j, k, block); +- +- return this.V.contains(nextticklistentry); ++ // Spigot start ++ int te_cnt = this.pendingTickEntries.size(); ++ for (int idx = this.nextPendingTickEntry; idx < te_cnt; idx++) { ++ NextTickListEntry ent = this.pendingTickEntries.get(idx); ++ if ((ent.a == i) && (ent.b == j) && (ent.c == k) && Block.a(ent.a(), block)) { ++ return true; ++ } ++ } ++ return false; ++ // Spigot end + } + + public void a(int i, int j, int k, Block block, int l) { +@@ -481,10 +491,9 @@ public class WorldServer extends World implements org.bukkit.BlockChangeDelegate + nextticklistentry.a(i1); + } + +- if (!this.M.contains(nextticklistentry)) { +- this.M.add(nextticklistentry); +- this.N.add(nextticklistentry); +- } ++ // Spigot start ++ addNextTickIfNeeded(nextticklistentry); ++ // Spigot end + } + } + +@@ -496,10 +505,9 @@ public class WorldServer extends World implements org.bukkit.BlockChangeDelegate + nextticklistentry.a((long) l + this.worldData.getTime()); + } + +- if (!this.M.contains(nextticklistentry)) { +- this.M.add(nextticklistentry); +- this.N.add(nextticklistentry); +- } ++ // Spigot start ++ addNextTickIfNeeded(nextticklistentry); ++ // Spigot end + } + + public void tickEntities() { +@@ -519,11 +527,11 @@ public class WorldServer extends World implements org.bukkit.BlockChangeDelegate + } + + public boolean a(boolean flag) { +- int i = this.N.size(); +- +- if (i != this.M.size()) { +- throw new IllegalStateException("TickNextTick list out of synch"); +- } else { ++ // Spigot start ++ int i = this.tickEntryQueue.size(); ++ this.nextPendingTickEntry = 0; ++ { ++ // Spigot end + if (i > 1000) { + // CraftBukkit start - If the server has too much to process over time, try to alleviate that + if (i > 20 * 1000) { +@@ -539,23 +547,24 @@ public class WorldServer extends World implements org.bukkit.BlockChangeDelegate + NextTickListEntry nextticklistentry; + + for (int j = 0; j < i; ++j) { +- nextticklistentry = (NextTickListEntry) this.N.first(); ++ nextticklistentry = (NextTickListEntry) this.tickEntryQueue.first(); // Spigot + if (!flag && nextticklistentry.d > this.worldData.getTime()) { + break; + } + +- this.N.remove(nextticklistentry); +- this.M.remove(nextticklistentry); +- this.V.add(nextticklistentry); ++ // Spigot start ++ this.removeNextTickIfNeeded(nextticklistentry); ++ this.pendingTickEntries.add(nextticklistentry); ++ // Spigot end + } + + this.methodProfiler.b(); + this.methodProfiler.a("ticking"); +- Iterator iterator = this.V.iterator(); +- +- while (iterator.hasNext()) { +- nextticklistentry = (NextTickListEntry) iterator.next(); +- iterator.remove(); ++ // Spigot start ++ for (int j = 0, te_cnt = this.pendingTickEntries.size(); j < te_cnt; j++) { ++ nextticklistentry = pendingTickEntries.get(j); ++ this.nextPendingTickEntry = j + 1; // treat this as dequeued ++ // Spigot end + byte b0 = 0; + + if (this.b(nextticklistentry.a - b0, nextticklistentry.b - b0, nextticklistentry.c - b0, nextticklistentry.a + b0, nextticklistentry.b + b0, nextticklistentry.c + b0)) { +@@ -586,50 +595,18 @@ public class WorldServer extends World implements org.bukkit.BlockChangeDelegate + } + + this.methodProfiler.b(); +- this.V.clear(); +- return !this.N.isEmpty(); ++ // Spigot start ++ this.pendingTickEntries.clear(); ++ this.nextPendingTickEntry = 0; ++ return !this.tickEntryQueue.isEmpty(); ++ // Spigot end + } + } + + public List a(Chunk chunk, boolean flag) { +- ArrayList arraylist = null; +- ChunkCoordIntPair chunkcoordintpair = chunk.l(); +- int i = (chunkcoordintpair.x << 4) - 2; +- int j = i + 16 + 2; +- int k = (chunkcoordintpair.z << 4) - 2; +- int l = k + 16 + 2; +- +- for (int i1 = 0; i1 < 2; ++i1) { +- Iterator iterator; +- +- if (i1 == 0) { +- iterator = this.N.iterator(); +- } else { +- iterator = this.V.iterator(); +- if (!this.V.isEmpty()) { +- a.debug("toBeTicked = " + this.V.size()); +- } +- } +- +- while (iterator.hasNext()) { +- NextTickListEntry nextticklistentry = (NextTickListEntry) iterator.next(); +- +- if (nextticklistentry.a >= i && nextticklistentry.a < j && nextticklistentry.c >= k && nextticklistentry.c < l) { +- if (flag) { +- this.M.remove(nextticklistentry); +- iterator.remove(); +- } +- +- if (arraylist == null) { +- arraylist = new ArrayList(); +- } +- +- arraylist.add(nextticklistentry); +- } +- } +- } +- +- return arraylist; ++ // Spigot start ++ return this.getNextTickEntriesForChunk(chunk, flag); ++ // Spigot end + } + + /* CraftBukkit start - We prevent spawning in general, so this butchering is not needed +@@ -701,13 +678,15 @@ public class WorldServer extends World implements org.bukkit.BlockChangeDelegate + this.entitiesById = new IntHashMap(); + } + +- if (this.M == null) { +- this.M = new HashSet(); ++ // Spigot start ++ if (this.tickEntriesByChunk == null) { ++ this.tickEntriesByChunk = new org.bukkit.craftbukkit.util.LongObjectHashMap>(); + } + +- if (this.N == null) { +- this.N = new TreeSet(); ++ if (this.tickEntryQueue == null) { ++ this.tickEntryQueue = new TreeSet(); + } ++ // Spigot end + + this.b(worldsettings); + super.a(worldsettings); +@@ -1037,4 +1016,62 @@ public class WorldServer extends World implements org.bukkit.BlockChangeDelegate + return Block.b(getType(x, y, z)); + } + // CraftBukkit end ++ // Spigot start ++ private void addNextTickIfNeeded(NextTickListEntry ent) { ++ long coord = LongHash.toLong(ent.a >> 4, ent.c >> 4); ++ Set chunkset = this.tickEntriesByChunk.get(coord); ++ if (chunkset == null) { ++ chunkset = new HashSet(); ++ this.tickEntriesByChunk.put(coord, chunkset); ++ } else if (chunkset.contains(ent)) { ++ return; ++ } ++ chunkset.add(ent); ++ this.tickEntryQueue.add(ent); ++ } ++ ++ private void removeNextTickIfNeeded(NextTickListEntry ent) { ++ long coord = LongHash.toLong(ent.a >> 4, ent.c >> 4); ++ Set chunkset = this.tickEntriesByChunk.get(coord); ++ if (chunkset != null) { ++ chunkset.remove(ent); ++ if (chunkset.isEmpty()) { ++ this.tickEntriesByChunk.remove(coord); ++ } ++ } ++ this.tickEntryQueue.remove(ent); ++ } ++ ++ private List getNextTickEntriesForChunk(Chunk chunk, boolean remove) { ++ long coord = LongHash.toLong(chunk.locX, chunk.locZ); ++ Set chunkset = this.tickEntriesByChunk.get(coord); ++ List list = null; ++ if (chunkset != null) { ++ list = new ArrayList(chunkset); ++ if (remove) { ++ this.tickEntriesByChunk.remove(coord); ++ this.tickEntryQueue.removeAll(list); ++ chunkset.clear(); ++ } ++ } ++ // See if any on list of ticks being processed now ++ if (this.nextPendingTickEntry < this.pendingTickEntries.size()) { ++ int xmin = (chunk.locX << 4); ++ int xmax = xmin + 16; ++ int zmin = (chunk.locZ << 4); ++ int zmax = zmin + 16; ++ int te_cnt = this.pendingTickEntries.size(); ++ for (int i = this.nextPendingTickEntry; i < te_cnt; i++) { ++ NextTickListEntry ent = this.pendingTickEntries.get(i); ++ if ((ent.a >= xmin) && (ent.a < xmax) && (ent.c >= zmin) && (ent.c < zmax)) { ++ if (list == null) { ++ list = new ArrayList(); ++ } ++ list.add(ent); ++ } ++ } ++ } ++ return list; ++ } ++ // Spigot end + } +-- +1.8.3.4 (Apple Git-47) +