1
0
Fork 0

Implement thread-safe, async-like and not buggy BlockUpdatesTracker

close #320
kcx-1614
Sergey Shatunov 2016-02-03 10:54:09 +07:00
parent 3359c026df
commit 7114a28fc6
7 changed files with 658 additions and 65 deletions

View File

@ -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_)

View File

@ -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<kcauldron.BlockUpdateEntry>) 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);
}

View File

@ -377,5 +377,5 @@
+ }
+
+ // KCauldron hold block updates into each chunk individually
+ public final java.util.Set<net.minecraft.world.NextTickListEntry> blockUpdates = new java.util.TreeSet<net.minecraft.world.NextTickListEntry>();
+ public final java.util.Set<kcauldron.BlockUpdateEntry> blockUpdates = new java.util.TreeSet<kcauldron.BlockUpdateEntry>();
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,69 @@
package kcauldron;
import net.minecraft.block.Block;
import net.minecraft.world.NextTickListEntry;
public class BlockUpdateEntry extends BlockCoords implements Comparable<BlockUpdateEntry> {
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
}
}

View File

@ -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<BlockUpdateEntry> {
private long lastEntryId = Long.MIN_VALUE;
private final WorldServer world;
private final NavigableSet<BlockUpdateEntry> sortedTree = new TreeSet<BlockUpdateEntry>();
private final TLongObjectMap<BlockUpdateEntry> trackerMap = new TLongObjectHashMap<BlockUpdateEntry>();
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<BlockUpdateEntry> iterator() {
return new Iterator<BlockUpdateEntry>() {
private final Iterator<BlockUpdateEntry> 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<NextTickListEntry> hashSet = new VanillaHashSetWrapper();
public final TreeSet<NextTickListEntry> treeSet = new VanillaTreeSetWrapper();
private final class VanillaWrapper {
private Set<NextTickListEntry> add = new HashSet<NextTickListEntry>();
private Set<NextTickListEntry> remove = new HashSet<NextTickListEntry>();
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<NextTickListEntry> {
@Override
public Iterator<NextTickListEntry> iterator() {
return new Iterator<NextTickListEntry>() {
private final TLongObjectIterator<BlockUpdateEntry> 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<NextTickListEntry> {
@Override
public Iterator<NextTickListEntry> iterator() {
return new Iterator<NextTickListEntry>() {
private final Iterator<BlockUpdateEntry> 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();
}
}
}