--- ../src-base/minecraft/net/minecraft/world/chunk/Chunk.java +++ ../src-work/minecraft/net/minecraft/world/chunk/Chunk.java @@ -36,6 +36,17 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +// CraftBukkit start +import net.minecraft.block.BlockContainer; +import org.bukkit.Bukkit; +// CraftBukkit end +// Spigot start +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EnumCreatureType; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.inventory.IInventory; +// Spigot end + public class Chunk { private static final Logger logger = LogManager.getLogger(); @@ -62,8 +73,38 @@ public int heightMapMinimum; public long inhabitedTime; private int queuedLightChecks; + public gnu.trove.map.hash.TObjectIntHashMap entityCount = new gnu.trove.map.hash.TObjectIntHashMap(); // Spigot (Cauldron protected -> public) + public int lastAccessedTick; // Cauldron track last time the chunk was accessed private static final String __OBFID = "CL_00000373"; + // CraftBukkit start - Neighbor loaded cache for chunk lighting and entity ticking + private int neighbors = 0x1 << 12; + + public boolean areNeighborsLoaded(final int radius) { + switch(radius) { + case 2: + return this.neighbors == Integer.MAX_VALUE >> 6; + case 1: + final int mask = + // x z offset x z offset x z offset + ( 0x1 << (1 * 5 + 1 + 12) ) | ( 0x1 << (0 * 5 + 1 + 12) ) | ( 0x1 << (-1 * 5 + 1 + 12) ) | + ( 0x1 << (1 * 5 + 0 + 12) ) | ( 0x1 << (0 * 5 + 0 + 12) ) | ( 0x1 << (-1 * 5 + 0 + 12) ) | + ( 0x1 << (1 * 5 + -1 + 12) ) | ( 0x1 << (0 * 5 + -1 + 12) ) | ( 0x1 << (-1 * 5 + -1 + 12) ); + return (this.neighbors & mask) == mask; + default: + throw new UnsupportedOperationException(String.valueOf(radius)); + } + } + + public void setNeighborLoaded(final int x, final int z) { + this.neighbors |= 0x1 << (x * 5 + 12 + z); + } + + public void setNeighborUnloaded(final int x, final int z) { + this.neighbors &= ~(0x1 << (x * 5 + 12 + z)); + } + // CraftBukkit end + public Chunk(World p_i1995_1_, int p_i1995_2_, int p_i1995_3_) { this.storageArrays = new ExtendedBlockStorage[16]; @@ -80,13 +121,22 @@ for (int k = 0; k < this.entityLists.length; ++k) { - this.entityLists[k] = new ArrayList(); + this.entityLists[k] = new org.bukkit.craftbukkit.util.UnsafeList(); // CraftBukkit - ArrayList -> UnsafeList } Arrays.fill(this.precipitationHeightMap, -999); Arrays.fill(this.blockBiomeArray, (byte) - 1); + // CraftBukkit start + if (!(this instanceof EmptyChunk)) + { + this.bukkitChunk = new org.bukkit.craftbukkit.CraftChunk(this); + } } + public org.bukkit.Chunk bukkitChunk; + public boolean mustSave; + // CraftBukkit end + public Chunk(World p_i45446_1_, Block[] p_i45446_2_, int p_i45446_3_, int p_i45446_4_) { this(p_i45446_1_, p_i45446_3_, p_i45446_4_); @@ -512,10 +562,10 @@ if (extendedblockstorage != null) { - try - { + /*try + {*/ // Exception handled by high-level class with safier deletion block = extendedblockstorage.getBlockByExtId(p_150810_1_, p_150810_2_ & 15, p_150810_3_); - } + /*} catch (Throwable throwable) { CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Getting block"); @@ -529,7 +579,7 @@ } }); throw new ReportedException(crashreport); - } + }*/ } } @@ -589,9 +639,10 @@ if (!this.worldObj.isRemote) { + if (block1 == null) return false; // Cauldron block1.onBlockPreDestroy(this.worldObj, l1, p_150807_2_, i2, k1); } - + // Cauldron - Removed CB patch that fixes BUKKIT-5238 to prevent stackoverflows. See issue #1165 and #1169 extendedblockstorage.func_150818_a(p_150807_1_, p_150807_2_ & 15, p_150807_3_, p_150807_4_); extendedblockstorage.setExtBlockMetadata(p_150807_1_, p_150807_2_ & 15, p_150807_3_, p_150807_5_); // This line duplicates the one below, so breakBlock fires with valid worldstate @@ -777,8 +828,20 @@ if (i != this.xPosition || j != this.zPosition) { - logger.warn("Wrong location! " + p_76612_1_ + " (at " + i + ", " + j + " instead of " + this.xPosition + ", " + this.zPosition + ")"); - Thread.dumpStack(); + // CraftBukkit start + Bukkit.getLogger().warning("Wrong location for " + p_76612_1_ + " in world '" + worldObj.getWorld().getName() + "'!"); + //logger.warn("Wrong location! " + p_76612_1_ + " (at " + i + ", " + j + " instead of " + this.xPosition + ", " + this.zPosition + ")"); + //Thread.dumpStack(); + Bukkit.getLogger().warning( + "Entity is at " + p_76612_1_.posX + "," + p_76612_1_.posZ + " (chunk " + i + "," + j + ") but was stored in chunk " + this.xPosition + "," + + this.zPosition); + if (!(p_76612_1_ instanceof EntityPlayerMP)) + { + Bukkit.getLogger().warning("Cauldron has removed " + p_76612_1_ + " to prevent a server crash."); + p_76612_1_.setDead(); + return; + } + // CraftBukkit end } int k = MathHelper.floor_double(p_76612_1_.posY / 16.0D); @@ -799,6 +862,26 @@ p_76612_1_.chunkCoordY = k; p_76612_1_.chunkCoordZ = this.zPosition; this.entityLists[k].add(p_76612_1_); + // Spigot start - increment creature type count + // Keep this synced up with World.a(Class) + if (p_76612_1_ instanceof EntityLiving) + { + EntityLiving entityliving = (EntityLiving) p_76612_1_; + + if (entityliving.canDespawn_CB() && entityliving.isNoDespawnRequired()) + { + return; + } + } + + for (EnumCreatureType creatureType : EnumCreatureType.values()) + { + if (creatureType.getCreatureClass().isAssignableFrom(p_76612_1_.getClass())) + { + this.entityCount.adjustOrPutValue(creatureType.getCreatureClass(), 1, 1); + } + } + // Spigot end } public void removeEntity(Entity p_76622_1_) @@ -819,6 +902,26 @@ } this.entityLists[p_76608_2_].remove(p_76608_1_); + // Spigot start - decrement creature type count + // Keep this synced up with World.a(Class) + if (p_76608_1_ instanceof EntityLiving) + { + EntityLiving entityliving = (EntityLiving) p_76608_1_; + + if (entityliving.canDespawn_CB() && entityliving.isNoDespawnRequired()) + { + return; + } + } + + for (EnumCreatureType creatureType : EnumCreatureType.values()) + { + if (creatureType.getCreatureClass().isAssignableFrom(p_76608_1_.getClass())) + { + this.entityCount.adjustValue(creatureType.getCreatureClass(), -1); + } + } + // Spigot end } public boolean canBlockSeeTheSky(int p_76619_1_, int p_76619_2_, int p_76619_3_) @@ -874,9 +977,23 @@ p_150812_4_.xCoord = this.xPosition * 16 + p_150812_1_; p_150812_4_.yCoord = p_150812_2_; p_150812_4_.zCoord = this.zPosition * 16 + p_150812_3_; + // Cauldron start - validate TE for corrupted data + Block block = null; + try + { + block = this.getBlock(p_150812_1_, p_150812_2_, p_150812_3_); + } + catch (ArrayIndexOutOfBoundsException e) + { + System.out.println("ERROR: Detected corrupted TileEntity " + p_150812_4_ + " with bad extended block ID of " + + ((p_150812_2_ & 15) << 8 | p_150812_3_ << 4 | p_150812_1_) + " @ " + p_150812_4_.xCoord + ", " + p_150812_4_.yCoord + ", " + + p_150812_4_.zCoord + ". Removing TE to avoid crash..."); + p_150812_4_.invalidate(); + return; + } int metadata = getBlockMetadata(p_150812_1_, p_150812_2_, p_150812_3_); - if (this.getBlock(p_150812_1_, p_150812_2_, p_150812_3_).hasTileEntity(metadata)) + if (block != null && block.hasTileEntity(metadata)) // Cauldron end { if (this.chunkTileEntityMap.containsKey(chunkposition)) { @@ -886,6 +1003,16 @@ p_150812_4_.validate(); this.chunkTileEntityMap.put(chunkposition, p_150812_4_); } + // CraftBukkit start + else if (net.minecraft.server.MinecraftServer.getServer().tileEntityConfig.enableTEPlaceWarning.getValue()) // Cauldron + { + System.out.println("Attempted to place a tile entity (" + p_150812_4_ + ") at " + p_150812_4_.xCoord + "," + p_150812_4_.yCoord + "," + + p_150812_4_.zCoord + " (" + org.bukkit.Material.getMaterial(Block.getIdFromBlock(getBlock(p_150812_1_, p_150812_2_, p_150812_3_))) + + ") where there was no entity tile!"); + System.out.println("Chunk coordinates: " + (this.xPosition * 16) + "," + (this.zPosition * 16)); + new Exception().printStackTrace(); + } + // CraftBukkit end } public void removeTileEntity(int p_150805_1_, int p_150805_2_, int p_150805_3_) @@ -936,6 +1063,21 @@ for (int i = 0; i < this.entityLists.length; ++i) { + // CraftBukkit start + java.util.Iterator iter = this.entityLists[i].iterator(); + + while (iter.hasNext()) + { + Entity entity = (Entity) iter.next(); + + // Do not pass along players, as doing so can get them stuck outside of time. + // (which for example disables inventory icon updates and prevents block breaking) + if (entity instanceof EntityPlayerMP) + { + iter.remove(); + } + } + // CraftBukkit end this.worldObj.unloadEntities(this.entityLists[i]); } MinecraftForge.EVENT_BUS.post(new ChunkEvent.Unload(this)); @@ -961,7 +1103,7 @@ { Entity entity1 = (Entity)list1.get(l); - if (entity1 != p_76588_1_ && entity1.boundingBox.intersectsWith(p_76588_2_) && (p_76588_4_ == null || p_76588_4_.isEntityApplicable(entity1))) + if (!entity1.isDead && entity1 != p_76588_1_ && entity1.boundingBox.intersectsWith(p_76588_2_) && (p_76588_4_ == null || p_76588_4_.isEntityApplicable(entity1))) { p_76588_3_.add(entity1); Entity[] aentity = entity1.getParts(); @@ -998,7 +1140,7 @@ { Entity entity = (Entity)list1.get(l); - if (p_76618_1_.isAssignableFrom(entity.getClass()) && entity.boundingBox.intersectsWith(p_76618_2_) && (p_76618_4_ == null || p_76618_4_.isEntityApplicable(entity))) + if (!entity.isDead && p_76618_1_.isAssignableFrom(entity.getClass()) && entity.boundingBox.intersectsWith(p_76618_2_) && (p_76618_4_ == null || p_76618_4_.isEntityApplicable(entity))) { p_76618_3_.add(entity); } @@ -1015,7 +1157,7 @@ return true; } } - else if (this.hasEntities && this.worldObj.getTotalWorldTime() >= this.lastSaveTime + 600L) + else if (this.hasEntities && this.worldObj.getTotalWorldTime() >= this.lastSaveTime + net.minecraft.server.MinecraftServer.getServer().autosavePeriod * 4) // PaperSpigot - Only save if we've passed 4 auto save intervals without modification { return true; } @@ -1025,7 +1167,7 @@ public Random getRandomWithSeed(long p_76617_1_) { - return new Random(this.worldObj.getSeed() + (long)(this.xPosition * this.xPosition * 4987142) + (long)(this.xPosition * 5947611) + (long)(this.zPosition * this.zPosition) * 4392871L + (long)(this.zPosition * 389711) ^ p_76617_1_); + return new Random(this.worldObj.getSeed() + (4987142L * this.xPosition * this.xPosition) + (5947611L * this.xPosition) + (4392871L * this.zPosition * this.zPosition) + (389711L * this.zPosition) ^ p_76617_1_); } public boolean isEmpty() @@ -1035,6 +1177,7 @@ public void populateChunk(IChunkProvider p_76624_1_, IChunkProvider p_76624_2_, int p_76624_3_, int p_76624_4_) { + worldObj.timings.syncChunkLoadPostTimer.startTiming(); // Spigot if (!this.isTerrainPopulated && p_76624_1_.chunkExists(p_76624_3_ + 1, p_76624_4_ + 1) && p_76624_1_.chunkExists(p_76624_3_, p_76624_4_ + 1) && p_76624_1_.chunkExists(p_76624_3_ + 1, p_76624_4_)) { p_76624_1_.populate(p_76624_2_, p_76624_3_, p_76624_4_); @@ -1054,6 +1197,7 @@ { p_76624_1_.populate(p_76624_2_, p_76624_3_ - 1, p_76624_4_ - 1); } + worldObj.timings.syncChunkLoadPostTimer.stopTiming(); // Spigot } public int getPrecipitationHeight(int p_76626_1_, int p_76626_2_) @@ -1091,7 +1235,7 @@ { if (this.isGapLightingUpdated && !this.worldObj.provider.hasNoSky && !p_150804_1_) { - this.recheckGaps(this.worldObj.isRemote); + this.recheckGapsAsync(this.worldObj.isRemote); } this.field_150815_m = true; @@ -1184,8 +1328,10 @@ if ((p_76607_2_ & 1 << l) != 0 && this.storageArrays[l] != null) { nibblearray = this.storageArrays[l].getMetadataArray(); - System.arraycopy(p_76607_1_, k, nibblearray.data, 0, nibblearray.data.length); - k += nibblearray.data.length; + // Spigot start + System.arraycopy(p_76607_1_, k, nibblearray.getValueArray(), 0, nibblearray.getValueArray().length); + k += nibblearray.getValueArray().length; + // Spigot end } } @@ -1194,8 +1340,10 @@ if ((p_76607_2_ & 1 << l) != 0 && this.storageArrays[l] != null) { nibblearray = this.storageArrays[l].getBlocklightArray(); - System.arraycopy(p_76607_1_, k, nibblearray.data, 0, nibblearray.data.length); - k += nibblearray.data.length; + // Spigot start + System.arraycopy(p_76607_1_, k, nibblearray.getValueArray(), 0, nibblearray.getValueArray().length); + k += nibblearray.getValueArray().length; + // Spigot end } } @@ -1206,8 +1354,10 @@ if ((p_76607_2_ & 1 << l) != 0 && this.storageArrays[l] != null) { nibblearray = this.storageArrays[l].getSkylightArray(); - System.arraycopy(p_76607_1_, k, nibblearray.data, 0, nibblearray.data.length); - k += nibblearray.data.length; + // Spigot start + System.arraycopy(p_76607_1_, k, nibblearray.getValueArray(), 0, nibblearray.getValueArray().length); + k += nibblearray.getValueArray().length; + // Spigot end } } } @@ -1229,8 +1379,8 @@ nibblearray = this.storageArrays[l].createBlockMSBArray(); } - System.arraycopy(p_76607_1_, k, nibblearray.data, 0, nibblearray.data.length); - k += nibblearray.data.length; + System.arraycopy(p_76607_1_, k, nibblearray.getValueArray(), 0, nibblearray.getValueArray().length); + k += nibblearray.getValueArray().length; } } else if (p_76607_4_ && this.storageArrays[l] != null && this.storageArrays[l].getBlockMSBArray() != null) @@ -1523,4 +1673,24 @@ } } } + + /** + * PaperSpigot - Recheck gaps asynchronously. + */ + public void recheckGapsAsync(final boolean isStatic) { + if (!worldObj.spigotConfig.useAsyncLighting) { + this.recheckGaps(isStatic); + return; + } + + worldObj.lightingExecutor.submit(new Runnable() { + @Override + public void run() { + Chunk.this.recheckGaps(isStatic); + } + }); + } + + // KCauldron hold block updates into each chunk individually + public final java.util.Set blockUpdates = new java.util.TreeSet(); }