From a0deef28ee760201ad27e9b20771e4eb78178041 Mon Sep 17 00:00:00 2001 From: md_5 Date: Mon, 20 Jan 2014 13:44:41 +1100 Subject: [PATCH] Remove NextTickList processing whilst we look into an issue. http://www.spigotmc.org/threads/lwc-locks-randomly-going-missing-after-using-1249.10505/ Catch stalling on corrupted map data / NBT arrays. --- CraftBukkit-Patches/0002-mc-dev-imports.patch | 279 +++++++++++++++- ...ing-on-corrupted-map-data-NBT-arrays.patch | 33 ++ ...097-Optimize-NextTickList-processing.patch | 308 ------------------ 3 files changed, 310 insertions(+), 310 deletions(-) create mode 100644 CraftBukkit-Patches/0097-Catch-stalling-on-corrupted-map-data-NBT-arrays.patch delete mode 100644 CraftBukkit-Patches/0097-Optimize-NextTickList-processing.patch diff --git a/CraftBukkit-Patches/0002-mc-dev-imports.patch b/CraftBukkit-Patches/0002-mc-dev-imports.patch index d8236cfa9..39d6aebbd 100644 --- a/CraftBukkit-Patches/0002-mc-dev-imports.patch +++ b/CraftBukkit-Patches/0002-mc-dev-imports.patch @@ -1,4 +1,4 @@ -From 728698cfb07683d5ce15c07ea8cf32591cc4c95c Mon Sep 17 00:00:00 2001 +From 1d5b81555def55b67178090ba0e5f3ecf27f5bda Mon Sep 17 00:00:00 2001 From: md_5 Date: Sun, 1 Dec 2013 15:10:48 +1100 Subject: [PATCH] mc-dev imports @@ -1187,6 +1187,281 @@ index 0000000..90a2a80 + c.put(ChunkCoordinates.class, Integer.valueOf(6)); + } +} +diff --git a/src/main/java/net/minecraft/server/NBTBase.java b/src/main/java/net/minecraft/server/NBTBase.java +new file mode 100644 +index 0000000..6e7c3a2 +--- /dev/null ++++ b/src/main/java/net/minecraft/server/NBTBase.java +@@ -0,0 +1,129 @@ ++package net.minecraft.server; ++ ++import java.io.DataInput; ++import java.io.DataOutput; ++import java.io.IOException; ++ ++public abstract class NBTBase { ++ ++ public static final String[] a = new String[] { "END", "BYTE", "SHORT", "INT", "LONG", "FLOAT", "DOUBLE", "BYTE[]", "STRING", "LIST", "COMPOUND", "INT[]"}; ++ ++ abstract void write(DataOutput dataoutput) throws IOException; ++ ++ abstract void load(DataInput datainput, int i) throws IOException; ++ ++ public abstract String toString(); ++ ++ public abstract byte getTypeId(); ++ ++ protected NBTBase() {} ++ ++ protected static NBTBase createTag(byte b0) { ++ switch (b0) { ++ case 0: ++ return new NBTTagEnd(); ++ ++ case 1: ++ return new NBTTagByte(); ++ ++ case 2: ++ return new NBTTagShort(); ++ ++ case 3: ++ return new NBTTagInt(); ++ ++ case 4: ++ return new NBTTagLong(); ++ ++ case 5: ++ return new NBTTagFloat(); ++ ++ case 6: ++ return new NBTTagDouble(); ++ ++ case 7: ++ return new NBTTagByteArray(); ++ ++ case 8: ++ return new NBTTagString(); ++ ++ case 9: ++ return new NBTTagList(); ++ ++ case 10: ++ return new NBTTagCompound(); ++ ++ case 11: ++ return new NBTTagIntArray(); ++ ++ default: ++ return null; ++ } ++ } ++ ++ public static String getTagName(int i) { ++ switch (i) { ++ case 0: ++ return "TAG_End"; ++ ++ case 1: ++ return "TAG_Byte"; ++ ++ case 2: ++ return "TAG_Short"; ++ ++ case 3: ++ return "TAG_Int"; ++ ++ case 4: ++ return "TAG_Long"; ++ ++ case 5: ++ return "TAG_Float"; ++ ++ case 6: ++ return "TAG_Double"; ++ ++ case 7: ++ return "TAG_Byte_Array"; ++ ++ case 8: ++ return "TAG_String"; ++ ++ case 9: ++ return "TAG_List"; ++ ++ case 10: ++ return "TAG_Compound"; ++ ++ case 11: ++ return "TAG_Int_Array"; ++ ++ case 99: ++ return "Any Numeric Tag"; ++ ++ default: ++ return "UNKNOWN"; ++ } ++ } ++ ++ public abstract NBTBase clone(); ++ ++ public boolean equals(Object object) { ++ if (!(object instanceof NBTBase)) { ++ return false; ++ } else { ++ NBTBase nbtbase = (NBTBase) object; ++ ++ return this.getTypeId() == nbtbase.getTypeId(); ++ } ++ } ++ ++ public int hashCode() { ++ return this.getTypeId(); ++ } ++ ++ protected String a_() { ++ return this.toString(); ++ } ++} +diff --git a/src/main/java/net/minecraft/server/NBTTagByteArray.java b/src/main/java/net/minecraft/server/NBTTagByteArray.java +new file mode 100644 +index 0000000..916d935 +--- /dev/null ++++ b/src/main/java/net/minecraft/server/NBTTagByteArray.java +@@ -0,0 +1,56 @@ ++package net.minecraft.server; ++ ++import java.io.DataInput; ++import java.io.DataOutput; ++import java.io.IOException; ++import java.util.Arrays; ++ ++public class NBTTagByteArray extends NBTBase { ++ ++ private byte[] data; ++ ++ NBTTagByteArray() {} ++ ++ public NBTTagByteArray(byte[] abyte) { ++ this.data = abyte; ++ } ++ ++ void write(DataOutput dataoutput) throws IOException { ++ dataoutput.writeInt(this.data.length); ++ dataoutput.write(this.data); ++ } ++ ++ void load(DataInput datainput, int i) throws IOException { ++ int j = datainput.readInt(); ++ ++ this.data = new byte[j]; ++ datainput.readFully(this.data); ++ } ++ ++ public byte getTypeId() { ++ return (byte) 7; ++ } ++ ++ public String toString() { ++ return "[" + this.data.length + " bytes]"; ++ } ++ ++ public NBTBase clone() { ++ byte[] abyte = new byte[this.data.length]; ++ ++ System.arraycopy(this.data, 0, abyte, 0, this.data.length); ++ return new NBTTagByteArray(abyte); ++ } ++ ++ public boolean equals(Object object) { ++ return super.equals(object) ? Arrays.equals(this.data, ((NBTTagByteArray) object).data) : false; ++ } ++ ++ public int hashCode() { ++ return super.hashCode() ^ Arrays.hashCode(this.data); ++ } ++ ++ public byte[] c() { ++ return this.data; ++ } ++} +diff --git a/src/main/java/net/minecraft/server/NBTTagIntArray.java b/src/main/java/net/minecraft/server/NBTTagIntArray.java +new file mode 100644 +index 0000000..49b3f14 +--- /dev/null ++++ b/src/main/java/net/minecraft/server/NBTTagIntArray.java +@@ -0,0 +1,72 @@ ++package net.minecraft.server; ++ ++import java.io.DataInput; ++import java.io.DataOutput; ++import java.io.IOException; ++import java.util.Arrays; ++ ++public class NBTTagIntArray extends NBTBase { ++ ++ private int[] data; ++ ++ NBTTagIntArray() {} ++ ++ public NBTTagIntArray(int[] aint) { ++ this.data = aint; ++ } ++ ++ void write(DataOutput dataoutput) throws IOException { ++ dataoutput.writeInt(this.data.length); ++ ++ for (int i = 0; i < this.data.length; ++i) { ++ dataoutput.writeInt(this.data[i]); ++ } ++ } ++ ++ void load(DataInput datainput, int i) throws IOException { ++ int j = datainput.readInt(); ++ ++ this.data = new int[j]; ++ ++ for (int k = 0; k < j; ++k) { ++ this.data[k] = datainput.readInt(); ++ } ++ } ++ ++ public byte getTypeId() { ++ return (byte) 11; ++ } ++ ++ public String toString() { ++ String s = "["; ++ int[] aint = this.data; ++ int i = aint.length; ++ ++ for (int j = 0; j < i; ++j) { ++ int k = aint[j]; ++ ++ s = s + k + ","; ++ } ++ ++ return s + "]"; ++ } ++ ++ public NBTBase clone() { ++ int[] aint = new int[this.data.length]; ++ ++ System.arraycopy(this.data, 0, aint, 0, this.data.length); ++ return new NBTTagIntArray(aint); ++ } ++ ++ public boolean equals(Object object) { ++ return super.equals(object) ? Arrays.equals(this.data, ((NBTTagIntArray) object).data) : false; ++ } ++ ++ public int hashCode() { ++ return super.hashCode() ^ Arrays.hashCode(this.data); ++ } ++ ++ public int[] c() { ++ return this.data; ++ } ++} diff --git a/src/main/java/net/minecraft/server/NextTickListEntry.java b/src/main/java/net/minecraft/server/NextTickListEntry.java new file mode 100644 index 0000000..06934a1 @@ -2282,5 +2557,5 @@ index 0000000..c0db754 + } +} -- -1.8.4.msysgit.0 +1.8.3.2 diff --git a/CraftBukkit-Patches/0097-Catch-stalling-on-corrupted-map-data-NBT-arrays.patch b/CraftBukkit-Patches/0097-Catch-stalling-on-corrupted-map-data-NBT-arrays.patch new file mode 100644 index 000000000..9acded7db --- /dev/null +++ b/CraftBukkit-Patches/0097-Catch-stalling-on-corrupted-map-data-NBT-arrays.patch @@ -0,0 +1,33 @@ +From 51cc9fc818ece90ca3349895c2d8d7d7f210b99e Mon Sep 17 00:00:00 2001 +From: md_5 +Date: Mon, 20 Jan 2014 13:44:07 +1100 +Subject: [PATCH] Catch stalling on corrupted map data / NBT arrays. + + +diff --git a/src/main/java/net/minecraft/server/NBTTagByteArray.java b/src/main/java/net/minecraft/server/NBTTagByteArray.java +index 916d935..3fa9c1a 100644 +--- a/src/main/java/net/minecraft/server/NBTTagByteArray.java ++++ b/src/main/java/net/minecraft/server/NBTTagByteArray.java +@@ -22,6 +22,7 @@ public class NBTTagByteArray extends NBTBase { + + void load(DataInput datainput, int i) throws IOException { + int j = datainput.readInt(); ++ com.google.common.base.Preconditions.checkArgument( i < 1 << 24); + + this.data = new byte[j]; + datainput.readFully(this.data); +diff --git a/src/main/java/net/minecraft/server/NBTTagIntArray.java b/src/main/java/net/minecraft/server/NBTTagIntArray.java +index 49b3f14..6c33462 100644 +--- a/src/main/java/net/minecraft/server/NBTTagIntArray.java ++++ b/src/main/java/net/minecraft/server/NBTTagIntArray.java +@@ -25,6 +25,7 @@ public class NBTTagIntArray extends NBTBase { + + void load(DataInput datainput, int i) throws IOException { + int j = datainput.readInt(); ++ com.google.common.base.Preconditions.checkArgument( i < 1 << 24); + + this.data = new int[j]; + +-- +1.8.3.2 + diff --git a/CraftBukkit-Patches/0097-Optimize-NextTickList-processing.patch b/CraftBukkit-Patches/0097-Optimize-NextTickList-processing.patch deleted file mode 100644 index d956a4010..000000000 --- a/CraftBukkit-Patches/0097-Optimize-NextTickList-processing.patch +++ /dev/null @@ -1,308 +0,0 @@ -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) -