From 7114a28fc64dcfa8f29056fc8aa01b651cbfce5b Mon Sep 17 00:00:00 2001 From: Sergey Shatunov Date: Wed, 3 Feb 2016 10:54:09 +0700 Subject: [PATCH] Implement thread-safe, async-like and not buggy BlockUpdatesTracker close #320 --- .../world/NextTickListEntry.java.patch | 11 - .../minecraft/world/WorldServer.java.patch | 287 +++++++++++++---- .../minecraft/world/chunk/Chunk.java.patch | 2 +- .../chunk/storage/AnvilChunkLoader.java.patch | 22 +- src/main/java/kcauldron/BlockCoords.java | 39 +++ src/main/java/kcauldron/BlockUpdateEntry.java | 69 +++++ .../java/kcauldron/BlockUpdatesTracker.java | 293 ++++++++++++++++++ 7 files changed, 658 insertions(+), 65 deletions(-) delete mode 100644 patches/net/minecraft/world/NextTickListEntry.java.patch create mode 100644 src/main/java/kcauldron/BlockCoords.java create mode 100644 src/main/java/kcauldron/BlockUpdateEntry.java create mode 100644 src/main/java/kcauldron/BlockUpdatesTracker.java diff --git a/patches/net/minecraft/world/NextTickListEntry.java.patch b/patches/net/minecraft/world/NextTickListEntry.java.patch deleted file mode 100644 index 1f3022f..0000000 --- a/patches/net/minecraft/world/NextTickListEntry.java.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- ../src-base/minecraft/net/minecraft/world/NextTickListEntry.java -+++ ../src-work/minecraft/net/minecraft/world/NextTickListEntry.java -@@ -38,7 +38,7 @@ - - public int hashCode() - { -- return (this.xCoord * 1024 * 1024 + this.zCoord * 1024 + this.yCoord) * 256; -+ return (((yCoord * 31 + xCoord) * 17 + zCoord) * 13 + yCoord) * 7 + Block.getIdFromBlock(field_151352_g); - } - - public NextTickListEntry setScheduledTime(long p_77176_1_) diff --git a/patches/net/minecraft/world/WorldServer.java.patch b/patches/net/minecraft/world/WorldServer.java.patch index d6c1d04..1adcb53 100644 --- a/patches/net/minecraft/world/WorldServer.java.patch +++ b/patches/net/minecraft/world/WorldServer.java.patch @@ -69,6 +69,21 @@ this.mcServer = p_i45284_1_; this.theEntityTracker = new EntityTracker(this); this.thePlayerManager = new PlayerManager(this); +@@ -106,12 +129,12 @@ + + if (this.pendingTickListEntriesHashSet == null) + { +- this.pendingTickListEntriesHashSet = new HashSet(); ++ this.pendingTickListEntriesHashSet = blockUpdatesTracker.hashSet; + } + + if (this.pendingTickListEntriesTreeSet == null) + { +- this.pendingTickListEntriesTreeSet = new TreeSet(); ++ this.pendingTickListEntriesTreeSet = blockUpdatesTracker.treeSet; + } + + this.worldTeleporter = new Teleporter(this); @@ -124,6 +147,47 @@ this.mapStorage.setData("scoreboard", scoreboardsavedata); } @@ -96,12 +111,12 @@ + + if (this.pendingTickListEntriesHashSet == null) + { -+ this.pendingTickListEntriesHashSet = new HashSet(); ++ this.pendingTickListEntriesHashSet = blockUpdatesTracker.hashSet; + } + + if (this.pendingTickListEntriesTreeSet == null) + { -+ this.pendingTickListEntriesTreeSet = new TreeSet(); ++ this.pendingTickListEntriesTreeSet = blockUpdatesTracker.treeSet; + } + + this.worldTeleporter = new org.bukkit.craftbukkit.CraftTravelAgent(this); // CraftBukkit @@ -366,7 +381,7 @@ block.updateTick(this, j2 + k, l2 + extendedblockstorage.getYLocation(), k2 + l, this.rand); } } -@@ -396,6 +569,13 @@ +@@ -396,12 +569,19 @@ this.theProfiler.endSection(); } @@ -380,33 +395,86 @@ } public boolean isBlockTickScheduledThisTick(int p_147477_1_, int p_147477_2_, int p_147477_3_, Block p_147477_4_) -@@ -447,10 +627,11 @@ - nextticklistentry.setPriority(p_147454_6_); - } + { +- NextTickListEntry nextticklistentry = new NextTickListEntry(p_147477_1_, p_147477_2_, p_147477_3_, p_147477_4_); +- return this.pendingTickListEntriesThisTick.contains(nextticklistentry); ++ for (kcauldron.BlockUpdateEntry entry : (Iterable) pendingTickListEntriesThisTick) if (entry.x == p_147477_1_ && entry.y == p_147477_2_ && entry.z == p_147477_3_ && entry.block == p_147477_4_) return true; ++ return false; + } + public void scheduleBlockUpdate(int p_147464_1_, int p_147464_2_, int p_147464_3_, Block p_147464_4_, int p_147464_5_) +@@ -411,70 +591,29 @@ + + public void scheduleBlockUpdateWithPriority(int p_147454_1_, int p_147454_2_, int p_147454_3_, Block p_147454_4_, int p_147454_5_, int p_147454_6_) + { +- NextTickListEntry nextticklistentry = new NextTickListEntry(p_147454_1_, p_147454_2_, p_147454_3_, p_147454_4_); +- //Keeping here as a note for future when it may be restored. +- //boolean isForced = getPersistentChunks().containsKey(new ChunkCoordIntPair(nextticklistentry.xCoord >> 4, nextticklistentry.zCoord >> 4)); +- //byte b0 = isForced ? 0 : 8; +- byte b0 = 0; +- +- if (this.scheduledUpdatesAreImmediate && p_147454_4_.getMaterial() != Material.air) +- { +- if (p_147454_4_.func_149698_L()) +- { +- b0 = 8; +- +- if (this.checkChunksExist(nextticklistentry.xCoord - b0, nextticklistentry.yCoord - b0, nextticklistentry.zCoord - b0, nextticklistentry.xCoord + b0, nextticklistentry.yCoord + b0, nextticklistentry.zCoord + b0)) +- { +- Block block1 = this.getBlock(nextticklistentry.xCoord, nextticklistentry.yCoord, nextticklistentry.zCoord); +- +- if (block1.getMaterial() != Material.air && block1 == nextticklistentry.func_151351_a()) +- { +- block1.updateTick(this, nextticklistentry.xCoord, nextticklistentry.yCoord, nextticklistentry.zCoord, this.rand); +- } ++ if (this.scheduledUpdatesAreImmediate && p_147454_4_.getMaterial() != Material.air) { ++ if (p_147454_4_.func_149698_L()) { ++ if (checkChunksExist(p_147454_1_ - 8, p_147454_2_ - 8, p_147454_3_ - 8, p_147454_1_ + 8, p_147454_2_ + 8, p_147454_3_ + 8)) { ++ Block block = getBlock(p_147454_1_, p_147454_2_, p_147454_3_); ++ if (block.getMaterial() != Material.air && block == p_147454_4_) ++ block.updateTick(this, p_147454_1_, p_147454_2_, p_147454_3_, rand); + } +- + return; + } +- + p_147454_5_ = 1; + } +- +- if (this.checkChunksExist(p_147454_1_ - b0, p_147454_2_ - b0, p_147454_3_ - b0, p_147454_1_ + b0, p_147454_2_ + b0, p_147454_3_ + b0)) +- { +- if (p_147454_4_.getMaterial() != Material.air) +- { +- nextticklistentry.setScheduledTime((long)p_147454_5_ + this.worldInfo.getWorldTotalTime()); +- nextticklistentry.setPriority(p_147454_6_); +- } +- - if (!this.pendingTickListEntriesHashSet.contains(nextticklistentry)) -+ if (this.pendingTickListEntriesHashSet.add(nextticklistentry)) - { +- { - this.pendingTickListEntriesHashSet.add(nextticklistentry); - this.pendingTickListEntriesTreeSet.add(nextticklistentry); -+ -+ if (!this.pendingTickListEntriesTreeSet.add(nextticklistentry)) pendingTickListEntriesHashSet.remove(nextticklistentry); else { -+ Chunk chunk = getChunkIfLoaded(p_147454_1_ >> 4, p_147454_3_ >> 4); if (chunk != null) chunk.blockUpdates.add(nextticklistentry); } // KCauldron - } - } +- } +- } ++ if (getChunkIfLoaded(p_147454_1_ >> 4, p_147454_3_ >> 4) != null) ++ blockUpdatesTracker.allocateEntry(p_147454_1_, p_147454_2_, p_147454_3_, p_147454_6_, p_147454_5_ + this.worldInfo.getWorldTotalTime(), p_147454_4_); } -@@ -465,16 +646,16 @@ - nextticklistentry.setScheduledTime((long)p_147446_5_ + this.worldInfo.getWorldTotalTime()); - } + public void func_147446_b(int p_147446_1_, int p_147446_2_, int p_147446_3_, Block p_147446_4_, int p_147446_5_, int p_147446_6_) + { +- NextTickListEntry nextticklistentry = new NextTickListEntry(p_147446_1_, p_147446_2_, p_147446_3_, p_147446_4_); +- nextticklistentry.setPriority(p_147446_6_); +- +- if (p_147446_4_.getMaterial() != Material.air) +- { +- nextticklistentry.setScheduledTime((long)p_147446_5_ + this.worldInfo.getWorldTotalTime()); +- } +- - if (!this.pendingTickListEntriesHashSet.contains(nextticklistentry)) -+ if (this.pendingTickListEntriesHashSet.add(nextticklistentry)) - { +- { - this.pendingTickListEntriesHashSet.add(nextticklistentry); - this.pendingTickListEntriesTreeSet.add(nextticklistentry); -+ if (!this.pendingTickListEntriesTreeSet.add(nextticklistentry)) pendingTickListEntriesHashSet.remove(nextticklistentry); else { -+ Chunk chunk = getChunkIfLoaded(p_147446_1_ >> 4, p_147446_3_ >> 4); if (chunk != null) chunk.blockUpdates.add(nextticklistentry); } - } +- } ++ blockUpdatesTracker.allocateEntry(p_147446_1_, p_147446_2_, p_147446_3_, p_147446_6_, p_147446_5_ + this.worldInfo.getWorldTotalTime(), p_147446_4_); } public void updateEntities() @@ -416,7 +484,7 @@ { if (this.updateEntityTick++ >= 1200) { -@@ -487,6 +668,7 @@ +@@ -487,6 +626,7 @@ } super.updateEntities(); @@ -424,8 +492,24 @@ } public void resetUpdateEntityTick() -@@ -506,7 +688,16 @@ +@@ -496,33 +636,40 @@ + + public boolean tickUpdates(boolean p_72955_1_) + { +- int i = this.pendingTickListEntriesTreeSet.size(); ++ int i = blockUpdatesTracker.size(); + +- if (i != this.pendingTickListEntriesHashSet.size()) ++ //if (i != this.pendingTickListEntriesHashSet.size()) ++ //{ ++ // throw new IllegalStateException("TickNextTick list out of synch"); ++ //} ++ //else { +- throw new IllegalStateException("TickNextTick list out of synch"); +- } +- else +- { if (i > 1000) { - i = 1000; @@ -442,24 +526,114 @@ } this.theProfiler.startSection("cleaning"); -@@ -515,7 +706,7 @@ +- NextTickListEntry nextticklistentry; ++ kcauldron.BlockUpdateEntry nextticklistentry; + for (int j = 0; j < i; ++j) { - nextticklistentry = (NextTickListEntry)this.pendingTickListEntriesTreeSet.first(); +- nextticklistentry = (NextTickListEntry)this.pendingTickListEntriesTreeSet.first(); - -+ Chunk chunk = getChunkIfLoaded(nextticklistentry.xCoord >> 4, nextticklistentry.zCoord >> 4); - if (!p_72955_1_ && nextticklistentry.scheduledTime > this.worldInfo.getWorldTotalTime()) +- if (!p_72955_1_ && nextticklistentry.scheduledTime > this.worldInfo.getWorldTotalTime()) ++ nextticklistentry = blockUpdatesTracker.peek(); ++ Chunk chunk = getChunkIfLoaded(nextticklistentry.x >> 4, nextticklistentry.z >> 4); ++ if (!p_72955_1_ && nextticklistentry.time > this.worldInfo.getWorldTotalTime()) { break; -@@ -523,6 +714,7 @@ - - this.pendingTickListEntriesTreeSet.remove(nextticklistentry); - this.pendingTickListEntriesHashSet.remove(nextticklistentry); -+ if (chunk != null) chunk.blockUpdates.remove(nextticklistentry); // KCauldron + } +- +- this.pendingTickListEntriesTreeSet.remove(nextticklistentry); +- this.pendingTickListEntriesHashSet.remove(nextticklistentry); ++ blockUpdatesTracker.removeEntry(nextticklistentry, true); this.pendingTickListEntriesThisTick.add(nextticklistentry); } -@@ -651,7 +843,37 @@ +@@ -532,22 +679,22 @@ + + while (iterator.hasNext()) + { +- nextticklistentry = (NextTickListEntry)iterator.next(); ++ nextticklistentry = (kcauldron.BlockUpdateEntry)iterator.next(); + iterator.remove(); + //Keeping here as a note for future when it may be restored. + //boolean isForced = getPersistentChunks().containsKey(new ChunkCoordIntPair(nextticklistentry.xCoord >> 4, nextticklistentry.zCoord >> 4)); + //byte b0 = isForced ? 0 : 8; + byte b0 = 0; + +- if (this.checkChunksExist(nextticklistentry.xCoord - b0, nextticklistentry.yCoord - b0, nextticklistentry.zCoord - b0, nextticklistentry.xCoord + b0, nextticklistentry.yCoord + b0, nextticklistentry.zCoord + b0)) ++ if (this.checkChunksExist(nextticklistentry.x - b0, nextticklistentry.y - b0, nextticklistentry.z - b0, nextticklistentry.x + b0, nextticklistentry.y + b0, nextticklistentry.z + b0)) + { +- Block block = this.getBlock(nextticklistentry.xCoord, nextticklistentry.yCoord, nextticklistentry.zCoord); ++ Block block = this.getBlock(nextticklistentry.x, nextticklistentry.y, nextticklistentry.z); + +- if (block.getMaterial() != Material.air && Block.isEqualTo(block, nextticklistentry.func_151351_a())) ++ if (block.getMaterial() != Material.air && Block.isEqualTo(block, nextticklistentry.block)) + { + try + { +- block.updateTick(this, nextticklistentry.xCoord, nextticklistentry.yCoord, nextticklistentry.zCoord, this.rand); ++ block.updateTick(this, nextticklistentry.x, nextticklistentry.y, nextticklistentry.z, this.rand); + } + catch (Throwable throwable1) + { +@@ -557,27 +704,27 @@ + + try + { +- k = this.getBlockMetadata(nextticklistentry.xCoord, nextticklistentry.yCoord, nextticklistentry.zCoord); ++ k = this.getBlockMetadata(nextticklistentry.x, nextticklistentry.y, nextticklistentry.z); + } + catch (Throwable throwable) + { + k = -1; + } + +- CrashReportCategory.func_147153_a(crashreportcategory, nextticklistentry.xCoord, nextticklistentry.yCoord, nextticklistentry.zCoord, block, k); ++ CrashReportCategory.func_147153_a(crashreportcategory, nextticklistentry.x, nextticklistentry.y, nextticklistentry.z, block, k); + throw new ReportedException(crashreport); + } + } + } + else + { +- this.scheduleBlockUpdate(nextticklistentry.xCoord, nextticklistentry.yCoord, nextticklistentry.zCoord, nextticklistentry.func_151351_a(), 0); ++ this.scheduleBlockUpdate(nextticklistentry.x, nextticklistentry.y, nextticklistentry.z, nextticklistentry.block, 0); + } + } + + this.theProfiler.endSection(); + this.pendingTickListEntriesThisTick.clear(); +- return !this.pendingTickListEntriesTreeSet.isEmpty(); ++ return !blockUpdatesTracker.isEmpty(); + } + } + +@@ -596,7 +743,7 @@ + + if (i1 == 0) + { +- iterator = this.pendingTickListEntriesTreeSet.iterator(); ++ iterator = this.blockUpdatesTracker.iterator(); + } + else + { +@@ -610,13 +757,13 @@ + + while (iterator.hasNext()) + { +- NextTickListEntry nextticklistentry = (NextTickListEntry)iterator.next(); ++ kcauldron.BlockUpdateEntry nextticklistentry = (kcauldron.BlockUpdateEntry)iterator.next(); + +- if (nextticklistentry.xCoord >= i && nextticklistentry.xCoord < j && nextticklistentry.zCoord >= k && nextticklistentry.zCoord < l) ++ if (nextticklistentry.x >= i && nextticklistentry.x < j && nextticklistentry.z >= k && nextticklistentry.z < l) + { + if (p_72920_2_) + { +- this.pendingTickListEntriesHashSet.remove(nextticklistentry); ++ //this.pendingTickListEntriesHashSet.remove(nextticklistentry); + iterator.remove(); + } + +@@ -651,7 +798,37 @@ protected IChunkProvider createChunkProvider() { IChunkLoader ichunkloader = this.saveHandler.getChunkLoader(this.provider); @@ -498,7 +672,7 @@ return this.theChunkProviderServer; } -@@ -659,29 +881,31 @@ +@@ -659,29 +836,31 @@ { ArrayList arraylist = new ArrayList(); @@ -545,7 +719,26 @@ return arraylist; } -@@ -733,7 +957,28 @@ +@@ -702,14 +881,14 @@ + this.entityIdMap = new IntHashMap(); + } + +- if (this.pendingTickListEntriesHashSet == null) ++ if (this.pendingTickListEntriesHashSet == null && blockUpdatesTracker != null) + { +- this.pendingTickListEntriesHashSet = new HashSet(); ++ this.pendingTickListEntriesHashSet = blockUpdatesTracker.hashSet; + } + +- if (this.pendingTickListEntriesTreeSet == null) ++ if (this.pendingTickListEntriesTreeSet == null && blockUpdatesTracker != null) + { +- this.pendingTickListEntriesTreeSet = new TreeSet(); ++ this.pendingTickListEntriesTreeSet = blockUpdatesTracker.treeSet; + } + + this.createSpawnPosition(p_72963_1_); +@@ -733,7 +912,28 @@ int i = 0; int j = this.provider.getAverageGroundLevel(); int k = 0; @@ -574,7 +767,7 @@ if (chunkposition != null) { i = chunkposition.chunkPosX; -@@ -876,6 +1121,20 @@ +@@ -876,6 +1076,20 @@ public boolean addWeatherEffect(Entity p_72942_1_) { @@ -595,7 +788,7 @@ if (super.addWeatherEffect(p_72942_1_)) { this.mcServer.getConfigurationManager().sendToAllNear(p_72942_1_.posX, p_72942_1_.posY, p_72942_1_.posZ, 512.0D, this.provider.dimensionId, new S2CPacketSpawnGlobalEntity(p_72942_1_)); -@@ -894,13 +1153,23 @@ +@@ -894,13 +1108,23 @@ public Explosion newExplosion(Entity p_72885_1_, double p_72885_2_, double p_72885_4_, double p_72885_6_, float p_72885_8_, boolean p_72885_9_, boolean p_72885_10_) { @@ -620,7 +813,7 @@ if (!p_72885_10_) { explosion.affectedBlockPositions.clear(); -@@ -977,7 +1246,7 @@ +@@ -977,7 +1201,7 @@ { boolean flag = this.isRaining(); super.updateWeather(); @@ -629,7 +822,7 @@ if (this.prevRainingStrength != this.rainingStrength) { this.mcServer.getConfigurationManager().sendPacketToAllPlayersInDimension(new S2BPacketChangeGameState(7, this.rainingStrength), this.provider.dimensionId); -@@ -988,10 +1257,6 @@ +@@ -988,10 +1212,6 @@ this.mcServer.getConfigurationManager().sendPacketToAllPlayersInDimension(new S2BPacketChangeGameState(8, this.thunderingStrength), this.provider.dimensionId); } @@ -640,7 +833,7 @@ if (flag != this.isRaining()) { if (flag) -@@ -1006,6 +1271,33 @@ +@@ -1006,6 +1226,33 @@ this.mcServer.getConfigurationManager().sendPacketToAllPlayersInDimension(new S2BPacketChangeGameState(7, this.rainingStrength), this.provider.dimensionId); this.mcServer.getConfigurationManager().sendPacketToAllPlayersInDimension(new S2BPacketChangeGameState(8, this.thunderingStrength), this.provider.dimensionId); } @@ -674,7 +867,7 @@ } protected int func_152379_p() -@@ -1069,4 +1361,64 @@ +@@ -1069,4 +1316,54 @@ this(); } } @@ -727,15 +920,5 @@ + // CraftBukkit end + + // KCauldron start -+ public NextTickListEntry allocateEntry(int x, int y, int z, Block block, int time, int priority) -+ { -+ NextTickListEntry nextticklistentry = new NextTickListEntry(x, y, z, block); -+ nextticklistentry.setPriority(priority); -+ if (block.getMaterial() != Material.air) -+ nextticklistentry.setScheduledTime((long)time + this.worldInfo.getWorldTotalTime()); -+ if (this.pendingTickListEntriesHashSet.add(nextticklistentry)) { -+ if (!this.pendingTickListEntriesTreeSet.add(nextticklistentry)) pendingTickListEntriesHashSet.remove(nextticklistentry); -+ } -+ return nextticklistentry; -+ } ++ public final kcauldron.BlockUpdatesTracker blockUpdatesTracker = new kcauldron.BlockUpdatesTracker(this); } diff --git a/patches/net/minecraft/world/chunk/Chunk.java.patch b/patches/net/minecraft/world/chunk/Chunk.java.patch index a0e1148..048f353 100644 --- a/patches/net/minecraft/world/chunk/Chunk.java.patch +++ b/patches/net/minecraft/world/chunk/Chunk.java.patch @@ -377,5 +377,5 @@ + } + + // KCauldron hold block updates into each chunk individually -+ public final java.util.Set blockUpdates = new java.util.TreeSet(); ++ public final java.util.Set blockUpdates = new java.util.TreeSet(); } diff --git a/patches/net/minecraft/world/chunk/storage/AnvilChunkLoader.java.patch b/patches/net/minecraft/world/chunk/storage/AnvilChunkLoader.java.patch index 63adb49..022251f 100644 --- a/patches/net/minecraft/world/chunk/storage/AnvilChunkLoader.java.patch +++ b/patches/net/minecraft/world/chunk/storage/AnvilChunkLoader.java.patch @@ -142,6 +142,26 @@ if (list != null) { +@@ -389,13 +410,13 @@ + + while (iterator.hasNext()) + { +- NextTickListEntry nextticklistentry = (NextTickListEntry)iterator.next(); ++ kcauldron.BlockUpdateEntry nextticklistentry = (kcauldron.BlockUpdateEntry)iterator.next(); + NBTTagCompound nbttagcompound2 = new NBTTagCompound(); +- nbttagcompound2.setInteger("i", Block.getIdFromBlock(nextticklistentry.func_151351_a())); +- nbttagcompound2.setInteger("x", nextticklistentry.xCoord); +- nbttagcompound2.setInteger("y", nextticklistentry.yCoord); +- nbttagcompound2.setInteger("z", nextticklistentry.zCoord); +- nbttagcompound2.setInteger("t", (int)(nextticklistentry.scheduledTime - k)); ++ nbttagcompound2.setInteger("i", Block.getIdFromBlock(nextticklistentry.block)); ++ nbttagcompound2.setInteger("x", nextticklistentry.x); ++ nbttagcompound2.setInteger("y", nextticklistentry.y); ++ nbttagcompound2.setInteger("z", nextticklistentry.z); ++ nbttagcompound2.setInteger("t", (int)(nextticklistentry.time - k)); + nbttagcompound2.setInteger("p", nextticklistentry.priority); + nbttaglist1.appendTag(nbttagcompound2); + } @@ -455,6 +476,7 @@ public void loadEntities(World p_75823_1_, NBTTagCompound p_75823_2_, Chunk chunk) @@ -232,7 +252,7 @@ { NBTTagCompound nbttagcompound5 = nbttaglist3.getCompoundTagAt(j1); - p_75823_1_.func_147446_b(nbttagcompound5.getInteger("x"), nbttagcompound5.getInteger("y"), nbttagcompound5.getInteger("z"), Block.getBlockById(nbttagcompound5.getInteger("i")), nbttagcompound5.getInteger("t"), nbttagcompound5.getInteger("p")); -+ NextTickListEntry entry = ((net.minecraft.world.WorldServer) p_75823_1_).allocateEntry(nbttagcompound5.getInteger("x"), nbttagcompound5.getInteger("y"), nbttagcompound5.getInteger("z"), Block.getBlockById(nbttagcompound5.getInteger("i")), nbttagcompound5.getInteger("t"), nbttagcompound5.getInteger("p")); ++ kcauldron.BlockUpdateEntry entry = ((net.minecraft.world.WorldServer) p_75823_1_).blockUpdatesTracker.allocateEntry(nbttagcompound5.getInteger("x"), nbttagcompound5.getInteger("y"), nbttagcompound5.getInteger("z"), nbttagcompound5.getInteger("p"), nbttagcompound5.getInteger("t"), Block.getBlockById(nbttagcompound5.getInteger("i"))); + chunk.blockUpdates.add(entry); } } diff --git a/src/main/java/kcauldron/BlockCoords.java b/src/main/java/kcauldron/BlockCoords.java new file mode 100644 index 0000000..2d9d3bc --- /dev/null +++ b/src/main/java/kcauldron/BlockCoords.java @@ -0,0 +1,39 @@ +package kcauldron; + +public class BlockCoords { + public final int x, y, z; + public final long key; + private final int hash; + + public BlockCoords(int x, int y, int z) { + this.x = x; + this.y = y; + this.z = z; + + key = ((long) y << 56) | (((long) z & 0xFFFFFFF) << 28) | (x & 0xFFFFFFF); + hash = (int) (key ^ (key >>> 32)); + } + + public BlockCoords(BlockCoords coords) { + this.x = coords.x; + this.y = coords.y; + this.z = coords.z; + this.key = coords.key; + this.hash = coords.hash; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) + return true; + if (!(obj instanceof BlockCoords)) + return false; + BlockCoords coords = (BlockCoords) obj; + return x == coords.x && y == coords.y && z == coords.z; + } + + @Override + public int hashCode() { + return hash; + } +} diff --git a/src/main/java/kcauldron/BlockUpdateEntry.java b/src/main/java/kcauldron/BlockUpdateEntry.java new file mode 100644 index 0000000..bf8aec3 --- /dev/null +++ b/src/main/java/kcauldron/BlockUpdateEntry.java @@ -0,0 +1,69 @@ +package kcauldron; + +import net.minecraft.block.Block; +import net.minecraft.world.NextTickListEntry; + +public class BlockUpdateEntry extends BlockCoords implements Comparable { + public final int priority; + public final long time; + public final Block block; + public final long id; + private NextTickListEntry mcEntry; + + public BlockUpdateEntry(int x, int y, int z, int priority, long time, Block block, long id) { + super(x, y, z); + this.priority = priority; + this.time = time; + this.block = block; + this.id = id; + } + + public NextTickListEntry asMCEntry() { + if (mcEntry == null) { + mcEntry = new NextTickListEntry(x, y, z, block); + mcEntry.setPriority(priority); + mcEntry.setScheduledTime(time); + } + return mcEntry; + } + + @Override + public int hashCode() { + int hash = super.hashCode(); + hash = hash * 31 + priority; + hash = hash * 31 + (int) (time | (time >>> 32)); + hash = hash * 31 + Block.getIdFromBlock(block); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (!super.equals(obj)) + return false; + if (!(obj instanceof BlockUpdateEntry)) + return false; + BlockUpdateEntry entry = (BlockUpdateEntry) obj; + if (priority != entry.priority) + return false; + if (time != entry.time) + return false; + if (block != entry.block) + return false; + return true; + } + + @Override + public int compareTo(BlockUpdateEntry o) { + if (o == this) return 0; + if (time < o.time) + return -1; + if (time > o.time) + return 1; + int diff = priority - o.priority; + if (diff != 0) + return diff; + if (id < o.id) return -1; + if (id > o.id) return 1; + return 0; // Normally never should happens + } +} diff --git a/src/main/java/kcauldron/BlockUpdatesTracker.java b/src/main/java/kcauldron/BlockUpdatesTracker.java new file mode 100644 index 0000000..259daf1 --- /dev/null +++ b/src/main/java/kcauldron/BlockUpdatesTracker.java @@ -0,0 +1,293 @@ +package kcauldron; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.NavigableSet; +import java.util.Set; +import java.util.TreeSet; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import gnu.trove.iterator.TLongObjectIterator; +import gnu.trove.map.TLongObjectMap; +import gnu.trove.map.hash.TLongObjectHashMap; +import net.minecraft.block.Block; +import net.minecraft.world.NextTickListEntry; +import net.minecraft.world.WorldServer; +import net.minecraft.world.chunk.Chunk; + +public final class BlockUpdatesTracker implements Iterable { + private long lastEntryId = Long.MIN_VALUE; + private final WorldServer world; + private final NavigableSet sortedTree = new TreeSet(); + private final TLongObjectMap trackerMap = new TLongObjectHashMap(); + private final ReadWriteLock lock = new ReentrantReadWriteLock(); + + public BlockUpdatesTracker(WorldServer world) { + this.world = world; + } + + public BlockUpdateEntry allocateEntry(int x, int y, int z, int priority, long time, Block block) { + return allocateEntry(x, y, z, priority, time, block, true); + } + + public BlockUpdateEntry allocateEntry(int x, int y, int z, int priority, long time, Block block, + boolean allowChunkTracking) { + lock.writeLock().lock(); + try { + Chunk chunk = allowChunkTracking ? world.getChunkIfLoaded(x >> 4, z >> 4) : null; + BlockUpdateEntry entry = new BlockUpdateEntry(x, y, z, priority, time, block, lastEntryId++); + BlockUpdateEntry oldEntry = trackerMap.remove(entry.key); + if (oldEntry != null) { + sortedTree.remove(oldEntry); + if (chunk != null) + chunk.blockUpdates.remove(oldEntry); + } + trackerMap.put(entry.key, entry); + sortedTree.add(entry); + if (chunk != null) + chunk.blockUpdates.add(entry); + return entry; + } finally { + lock.writeLock().unlock(); + } + } + + public BlockUpdateEntry removeEntry(int x, int y, int z) { + return removeEntry(x, y, z, true); + } + + public BlockUpdateEntry removeEntry(int x, int y, int z, boolean allowChunkTracking) { + lock.writeLock().lock(); + try { + Chunk chunk = allowChunkTracking ? world.getChunkIfLoaded(x >> 4, z >> 4) : null; + BlockUpdateEntry entry = trackerMap.remove(new BlockCoords(x, y, z).key); + if (entry != null) { + sortedTree.remove(entry); + if (chunk != null) + chunk.blockUpdates.remove(entry); + } + return entry; + } finally { + lock.writeLock().unlock(); + } + } + + public BlockUpdateEntry removeEntry(BlockUpdateEntry entry, boolean allowChunkTracking) { + lock.writeLock().lock(); + try { + Chunk chunk = allowChunkTracking ? world.getChunkIfLoaded(entry.x >> 4, entry.z >> 4) : null; + trackerMap.remove(entry.key); + if (entry != null) { + sortedTree.remove(entry); + if (chunk != null) + chunk.blockUpdates.remove(entry); + } + return entry; + } finally { + lock.writeLock().unlock(); + } + } + + public BlockUpdateEntry getEntry(int x, int y, int z) { + lock.readLock().lock(); + try { + return trackerMap.get(new BlockCoords(x, y, z).key); + } finally { + lock.readLock().unlock(); + } + } + + public BlockUpdateEntry peek() { + lock.readLock().lock(); + try { + return sortedTree.first(); + } finally { + lock.readLock().unlock(); + } + } + + public boolean isEmpty() { + lock.readLock().lock(); + try { + return trackerMap.isEmpty(); + } finally { + lock.readLock().unlock(); + } + } + + public int size() { + lock.readLock().lock(); + try { + return trackerMap.size(); + } finally { + lock.readLock().unlock(); + } + } + + @Override + public Iterator iterator() { + return new Iterator() { + private final Iterator iterator = sortedTree.iterator();; + private BlockUpdateEntry next; + + @Override + public boolean hasNext() { + next = null; + return iterator.hasNext(); + } + + @Override + public BlockUpdateEntry next() { + return next = iterator.next(); + } + + @Override + public void remove() { + if (next == null) + next(); + if (next != null) + removeEntry(next, true); + } + }; + } + + private final VanillaWrapper wrapper = new VanillaWrapper(); + public final HashSet hashSet = new VanillaHashSetWrapper(); + public final TreeSet treeSet = new VanillaTreeSetWrapper(); + + private final class VanillaWrapper { + private Set add = new HashSet(); + private Set remove = new HashSet(); + + public boolean add(NextTickListEntry e) { + if (add.remove(e)) { + allocateEntry(e.xCoord, e.yCoord, e.zCoord, e.priority, e.scheduledTime, e.func_151351_a()); + return true; + } + return add.add(e); + } + + public boolean remove(NextTickListEntry e) { + if (remove.remove(e)) { + removeEntry(e.xCoord, e.yCoord, e.zCoord); + return true; + } + return remove.add(e); + } + + public boolean contains(NextTickListEntry e, boolean allowInterscan) { + if (allowInterscan) { + if (add.contains(e)) + return true; + if (remove.contains(e)) + return false; + } + return getEntry(e.xCoord, e.yCoord, e.zCoord) != null; + } + } + + private final class VanillaHashSetWrapper extends HashSet { + @Override + public Iterator iterator() { + return new Iterator() { + private final TLongObjectIterator iterator = trackerMap.iterator();; + + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public NextTickListEntry next() { + iterator.advance(); + return iterator.value().asMCEntry(); + } + + @Override + public void remove() { + removeEntry(iterator.value(), true); + } + }; + } + + @Override + public boolean add(NextTickListEntry e) { + return wrapper.add(e); + } + + @Override + public boolean remove(Object o) { + return wrapper.remove((NextTickListEntry) o); + } + + @Override + public boolean contains(Object o) { + return wrapper.contains((NextTickListEntry) o, true); + } + + @Override + public boolean isEmpty() { + return BlockUpdatesTracker.this.sortedTree.isEmpty(); + } + + @Override + public int size() { + return BlockUpdatesTracker.this.sortedTree.size(); + } + } + + private final class VanillaTreeSetWrapper extends TreeSet { + @Override + public Iterator iterator() { + return new Iterator() { + private final Iterator iterator = sortedTree.iterator();; + private BlockUpdateEntry next; + + @Override + public boolean hasNext() { + next = null; + return iterator.hasNext(); + } + + @Override + public NextTickListEntry next() { + return (next = iterator.next()).asMCEntry(); + } + + @Override + public void remove() { + if (next == null) + next(); + if (next != null) + removeEntry(next, true); + } + }; + } + + @Override + public boolean add(NextTickListEntry e) { + return wrapper.add(e); + } + + @Override + public boolean remove(Object o) { + return wrapper.remove((NextTickListEntry) o); + } + + @Override + public boolean contains(Object o) { + return wrapper.contains((NextTickListEntry) o, false); + } + + @Override + public boolean isEmpty() { + return BlockUpdatesTracker.this.sortedTree.isEmpty(); + } + + @Override + public int size() { + return BlockUpdatesTracker.this.sortedTree.size(); + } + } +}