From e6404134e65bbc8cd3511970a0cfa38c98e54820 Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Wed, 24 Apr 2013 01:43:33 -0500 Subject: [PATCH] Improve NextTickList Performance Improve next-tick-list performance on chunk unloads, large queues diff --git a/src/main/java/net/minecraft/server/NextTickListEntry.java b/src/main/java/net/minecraft/server/NextTickListEntry.java index acf8838..1e3e0f8 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 74a2d45..b860ce8 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 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); @@ -438,10 +441,17 @@ 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); + public boolean a(int i, int j, int k, Block block) { // Block was l + // 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.b(ent.d, l)) { + return true; + } + } + return false; + // Spigot end } public void a(int i, int j, int k, Block block, int l) { @@ -475,10 +485,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 } } @@ -490,10 +499,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() { @@ -513,11 +521,12 @@ public class WorldServer extends World implements org.bukkit.BlockChangeDelegate } public boolean a(boolean flag) { - int i = this.N.size(); + // Spigot start + int i = this.tickEntryQueue.size(); - if (i != this.M.size()) { - throw new IllegalStateException("TickNextTick list out of synch"); - } else { + 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) { @@ -533,23 +542,24 @@ public class WorldServer extends World implements org.bukkit.BlockChangeDelegate NextTickListEntry nextticklistentry; for (int j = 0; j < i; ++j) { - nextticklistentry = (NextTickListEntry) this.N.first(); - if (!flag && nextticklistentry.d > this.worldData.getTime()) { + nextticklistentry = (NextTickListEntry) this.tickEntryQueue.first(); // Spigot + if (!flag && nextticklistentry.e > 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)) { @@ -580,50 +590,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 @@ -695,13 +673,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); @@ -1031,4 +1011,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.x, chunk.z); + 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.x << 4); + int xmax = xmin + 16; + int zmin = (chunk.z << 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.2