From 6db12ad6017b2f85d480e1f3609a269573a65adc Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Wed, 24 Apr 2013 01:43:33 -0500 Subject: [PATCH] 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 52a70a1..08a4240 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 a2f7fee..b0c2c96 100644 --- a/src/main/java/net/minecraft/server/WorldServer.java +++ b/src/main/java/net/minecraft/server/WorldServer.java @@ -28,8 +28,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 L; - private TreeSet M; + private LongObjectHashMap> tickEntriesByChunk; // Spigot - switch to something better for chunk-wise access + private TreeSet tickEntryQueue; // Spigot public ChunkProviderServer chunkProviderServer; public boolean savingDisabled; private boolean N; @@ -38,7 +38,8 @@ public class WorldServer extends World implements org.bukkit.BlockChangeDelegate private NoteDataList[] Q = new NoteDataList[] { new NoteDataList((EmptyClass2) null), new NoteDataList((EmptyClass2) null)}; private int R = 0; private static final StructurePieceTreasure[] S = new StructurePieceTreasure[] { new StructurePieceTreasure(Item.STICK.id, 0, 1, 3, 10), new StructurePieceTreasure(Block.WOOD.id, 0, 1, 3, 10), new StructurePieceTreasure(Block.LOG.id, 0, 1, 3, 10), new StructurePieceTreasure(Item.STONE_AXE.id, 0, 1, 1, 3), new StructurePieceTreasure(Item.WOOD_AXE.id, 0, 1, 1, 5), new StructurePieceTreasure(Item.STONE_PICKAXE.id, 0, 1, 1, 3), new StructurePieceTreasure(Item.WOOD_PICKAXE.id, 0, 1, 1, 5), new StructurePieceTreasure(Item.APPLE.id, 0, 2, 3, 5), new StructurePieceTreasure(Item.BREAD.id, 0, 2, 3, 3)}; - private ArrayList T = new ArrayList(); + private ArrayList pendingTickEntries = new ArrayList(); // Spigot + private int nextPendingTickEntry; // Spigot private IntHashMap entitiesById; // CraftBukkit start @@ -56,13 +57,15 @@ public class WorldServer extends World implements org.bukkit.BlockChangeDelegate this.entitiesById = new IntHashMap(); } - if (this.L == null) { - this.L = new HashSet(); + // Spigot start + if (this.tickEntriesByChunk == null) { + this.tickEntriesByChunk = new LongObjectHashMap>(); } - if (this.M == null) { - this.M = new TreeSet(); + if (this.tickEntryQueue == null) { + this.tickEntryQueue = new TreeSet(); } + // Spigot end this.P = new org.bukkit.craftbukkit.CraftTravelAgent(this); // CraftBukkit this.scoreboard = new ScoreboardServer(minecraftserver); @@ -444,9 +447,16 @@ public class WorldServer extends World implements org.bukkit.BlockChangeDelegate } public boolean a(int i, int j, int k, int l) { - NextTickListEntry nextticklistentry = new NextTickListEntry(i, j, k, l); - - return this.T.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.b(ent.d, l)) { + return true; + } + } + return false; + // Spigot end } public void a(int i, int j, int k, int l, int i1) { @@ -479,10 +489,9 @@ public class WorldServer extends World implements org.bukkit.BlockChangeDelegate nextticklistentry.a(j1); } - if (!this.L.contains(nextticklistentry)) { - this.L.add(nextticklistentry); - this.M.add(nextticklistentry); - } + // Spigot start + addNextTickIfNeeded(nextticklistentry); + // Spigot end } } @@ -494,10 +503,9 @@ public class WorldServer extends World implements org.bukkit.BlockChangeDelegate nextticklistentry.a((long) i1 + this.worldData.getTime()); } - if (!this.L.contains(nextticklistentry)) { - this.L.add(nextticklistentry); - this.M.add(nextticklistentry); - } + // Spigot start + addNextTickIfNeeded(nextticklistentry); + // Spigot end } public void tickEntities() { @@ -517,11 +525,12 @@ public class WorldServer extends World implements org.bukkit.BlockChangeDelegate } public boolean a(boolean flag) { - int i = this.M.size(); + // Spigot start + int i = this.tickEntryQueue.size(); - if (i != this.L.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) { @@ -537,23 +546,24 @@ public class WorldServer extends World implements org.bukkit.BlockChangeDelegate NextTickListEntry nextticklistentry; for (int j = 0; j < i; ++j) { - nextticklistentry = (NextTickListEntry) this.M.first(); + nextticklistentry = (NextTickListEntry) this.tickEntryQueue.first(); // Spigot if (!flag && nextticklistentry.e > this.worldData.getTime()) { break; } - this.M.remove(nextticklistentry); - this.L.remove(nextticklistentry); - this.T.add(nextticklistentry); + // Spigot start + this.removeNextTickIfNeeded(nextticklistentry); + this.pendingTickEntries.add(nextticklistentry); + // Spigot end } this.methodProfiler.b(); this.methodProfiler.a("ticking"); - Iterator iterator = this.T.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.e(nextticklistentry.a - b0, nextticklistentry.b - b0, nextticklistentry.c - b0, nextticklistentry.a + b0, nextticklistentry.b + b0, nextticklistentry.c + b0)) { @@ -584,52 +594,18 @@ public class WorldServer extends World implements org.bukkit.BlockChangeDelegate } this.methodProfiler.b(); - this.T.clear(); - return !this.M.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.M.iterator(); - } else { - iterator = this.T.iterator(); - /* CraftBukkit start - Comment out debug spam - if (!this.T.isEmpty()) { - System.out.println(this.T.size()); - } - // CraftBukkit end */ - } - - while (iterator.hasNext()) { - NextTickListEntry nextticklistentry = (NextTickListEntry) iterator.next(); - - if (nextticklistentry.a >= i && nextticklistentry.a < j && nextticklistentry.c >= k && nextticklistentry.c < l) { - if (flag) { - this.L.remove(nextticklistentry); - iterator.remove(); - } - - if (arraylist == null) { - arraylist = new ArrayList(); - } - - arraylist.add(nextticklistentry); - } - } - } - - return arraylist; + // Spigot start + return this.getNextTickEntriesForChunk(chunk, flag); + // Spigot end } public void entityJoinedWorld(Entity entity, boolean flag) { @@ -706,13 +682,15 @@ public class WorldServer extends World implements org.bukkit.BlockChangeDelegate this.entitiesById = new IntHashMap(); } - if (this.L == null) { - this.L = new HashSet(); + // Spigot start + if (this.tickEntriesByChunk == null) { + this.tickEntriesByChunk = new LongObjectHashMap>(); } - if (this.M == null) { - this.M = new TreeSet(); + if (this.tickEntryQueue == null) { + this.tickEntryQueue = new TreeSet(); } + // Spigot end this.b(worldsettings); super.a(worldsettings); @@ -999,4 +977,62 @@ public class WorldServer extends World implements org.bukkit.BlockChangeDelegate return this.setTypeIdAndData(x, y, z, typeId, data, 3); } // 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.2.1