Files
CatServer/patches/net/minecraft/world/chunk/Chunk.java.patch

732 lines
32 KiB
Diff

--- ../src-base/minecraft/net/minecraft/world/chunk/Chunk.java
+++ ../src-work/minecraft/net/minecraft/world/chunk/Chunk.java
@@ -8,20 +8,27 @@
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.stream.Collectors;
import javax.annotation.Nullable;
import net.minecraft.block.Block;
-import net.minecraft.block.ITileEntityProvider;
+import net.minecraft.block.BlockSand;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState;
import net.minecraft.crash.CrashReport;
import net.minecraft.crash.CrashReportCategory;
import net.minecraft.crash.ICrashReportDetail;
import net.minecraft.entity.Entity;
+import net.minecraft.entity.EntityLiving;
+import net.minecraft.entity.EnumCreatureType;
+import net.minecraft.entity.item.EntityItem;
+import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.init.Biomes;
import net.minecraft.init.Blocks;
+import net.minecraft.inventory.IInventory;
import net.minecraft.network.PacketBuffer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ClassInheritanceMultiMap;
+import net.minecraft.util.EntitySelectors;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.ReportedException;
import net.minecraft.util.math.AxisAlignedBB;
@@ -41,7 +48,9 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
-public class Chunk
+import org.bukkit.Server;
+
+public class Chunk implements net.minecraftforge.common.capabilities.ICapabilityProvider
{
private static final Logger LOGGER = LogManager.getLogger();
public static final ExtendedBlockStorage NULL_BLOCK_STORAGE = null;
@@ -50,13 +59,13 @@
private final int[] precipitationHeightMap;
private final boolean[] updateSkylightColumns;
private boolean loaded;
- private final World world;
- private final int[] heightMap;
+ public final World world; // CatServer - private -> public
+ public final int[] heightMap; // CatServer - private -> public
public final int x;
public final int z;
private boolean isGapLightingUpdated;
- private final Map<BlockPos, TileEntity> tileEntities;
- private final ClassInheritanceMultiMap<Entity>[] entityLists;
+ public final Map<BlockPos, TileEntity> tileEntities; // CatServer - private -> public
+ public final ClassInheritanceMultiMap<Entity>[] entityLists; // Spigot
private boolean isTerrainPopulated;
private boolean isLightPopulated;
private boolean ticked;
@@ -68,7 +77,95 @@
private int queuedLightChecks;
private final ConcurrentLinkedQueue<BlockPos> tileEntityPosQueue;
public boolean unloadQueued;
+ public gnu.trove.map.hash.TObjectIntHashMap<Class> entityCount = new gnu.trove.map.hash.TObjectIntHashMap<Class>(); // Spigot
+ // Paper start
+ // Track the number of minecarts and items
+ // Keep this synced with entitySlices.add() and entitySlices.remove()
+ private final int[] itemCounts = new int[16];
+ private final int[] inventoryEntityCounts = new int[16];
+ // Paper end
+
+ // CraftBukkit start - Neighbor loaded cache for chunk lighting and entity ticking
+ private int neighbors = 0x1 << 12;
+ public long chunkKey;
+
+ 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
+
+ private boolean loadingTileentity = false; // CatServer - prevent modification of tileentity list when chunk loading
+
+ // CatServer start
+ private void handleSpigotEntityAdd(Entity entityIn) {
+ // Spigot start - increment creature type count
+ // Keep this synced up with World.a(Class)
+ if (entityIn instanceof EntityLiving) {
+ EntityLiving entityinsentient = (EntityLiving) entityIn;
+ if (entityinsentient.canDespawn() && entityinsentient.isNoDespawnRequired()) {
+ return;
+ }
+ }
+ for ( EnumCreatureType creatureType : EnumCreatureType.values() )
+ {
+ if ( creatureType.getCreatureClass().isAssignableFrom( entityIn.getClass() ) )
+ {
+ this.entityCount.adjustOrPutValue( creatureType.getCreatureClass(), 1, 1 );
+ }
+ }
+ // Spigot end
+ }
+
+ private void handleSpigotEntityRemove(Entity entityIn) {
+ // Spigot start - decrement creature type count
+ // Keep this synced up with World.a(Class)
+ if (entityIn instanceof EntityLiving) {
+ EntityLiving entityinsentient = (EntityLiving) entityIn;
+ if (entityinsentient.canDespawn() && entityinsentient.isNoDespawnRequired()) {
+ return;
+ }
+ }
+ for ( EnumCreatureType creatureType : EnumCreatureType.values() )
+ {
+ if ( creatureType.getCreatureClass().isAssignableFrom( entityIn.getClass() ) )
+ {
+ this.entityCount.adjustValue( creatureType.getCreatureClass(), -1 );
+ }
+ }
+ // Spigot end
+ }
+
+ private void handleSpigotOnLoaded() {
+ this.entityCount.clear();
+ for (ClassInheritanceMultiMap<Entity> entityList : entityLists) {
+ for (Entity entity : entityList) {
+ handleSpigotEntityAdd(entity);
+ }
+ }
+ }
+ // CatServer end
+
public Chunk(World worldIn, int x, int z)
{
this.storageArrays = new ExtendedBlockStorage[16];
@@ -91,8 +188,14 @@
Arrays.fill(this.precipitationHeightMap, -999);
Arrays.fill(this.blockBiomeArray, (byte) - 1);
+ capabilities = net.minecraftforge.event.ForgeEventFactory.gatherCapabilities(this);
+ this.bukkitChunk = new org.bukkit.craftbukkit.CraftChunk(this);
+ this.chunkKey = ChunkPos.asLong(this.x, this.z);
}
+ public org.bukkit.Chunk bukkitChunk;
+ public boolean mustSave;
+
public Chunk(World worldIn, ChunkPrimer primer, int x, int z)
{
this(worldIn, x, z);
@@ -179,7 +282,7 @@
{
IBlockState iblockstate = this.getBlockState(j, l - 1, k);
- if (iblockstate.getLightOpacity() != 0)
+ if (this.getBlockLightOpacity(j, l - 1, k) != 0)
{
this.heightMap[k << 4 | j] = l;
@@ -452,12 +555,13 @@
public int getBlockLightOpacity(BlockPos pos)
{
- return this.getBlockState(pos).getLightOpacity();
+ return this.getBlockState(pos).getLightOpacity(this.world, pos);
}
private int getBlockLightOpacity(int x, int y, int z)
{
- return this.getBlockState(x, y, z).getLightOpacity();
+ IBlockState state = this.getBlockState(x, y, z); //Forge: Can sometimes be called before we are added to the global world list. So use the less accurate one during that. It'll be recalculated later
+ return !loaded ? state.getLightOpacity() : state.getLightOpacity(world, new BlockPos(this.x << 4 | x & 15, y, this.z << 4 | z & 15));
}
public IBlockState getBlockState(BlockPos pos)
@@ -539,6 +643,7 @@
{
Block block = state.getBlock();
Block block1 = iblockstate.getBlock();
+ int k1 = iblockstate.getLightOpacity(this.world, pos); // Relocate old light value lookup here, so that it is called before TE is removed.
ExtendedBlockStorage extendedblockstorage = this.storageArrays[j >> 4];
boolean flag = false;
@@ -556,14 +661,19 @@
extendedblockstorage.set(i, j & 15, k, state);
- if (block1 != block)
+ //if (block1 != block)
{
if (!this.world.isRemote)
{
+ if (block1 != block) //Only fire block breaks when the block changes.
block1.breakBlock(this.world, pos, iblockstate);
+ TileEntity te = this.getTileEntity(pos, EnumCreateEntityType.CHECK);
+ if (te != null && te.shouldRefresh(this.world, pos, iblockstate, state)) this.world.removeTileEntity(pos);
}
- else if (block1 instanceof ITileEntityProvider)
+ else if (block1.hasTileEntity(iblockstate))
{
+ TileEntity te = this.getTileEntity(pos, EnumCreateEntityType.CHECK);
+ if (te != null && te.shouldRefresh(this.world, pos, iblockstate, state))
this.world.removeTileEntity(pos);
}
}
@@ -580,8 +690,7 @@
}
else
{
- int j1 = state.getLightOpacity();
- int k1 = iblockstate.getLightOpacity();
+ int j1 = state.getLightOpacity(this.world, pos);
if (j1 > 0)
{
@@ -601,28 +710,20 @@
}
}
- if (block1 instanceof ITileEntityProvider)
+ // CraftBukkit - Don't place while processing the BlockPlaceEvent, unless it's a BlockContainer. Prevents blocks such as TNT from activating when cancelled.
+ // If capturing blocks, only run block physics for TE's. Non-TE's are handled in ForgeHooks.onPlaceItemIntoWorld
+ if (!this.world.isRemote && block1 != block && (!this.world.captureBlockSnapshots || block.hasTileEntity(state)))
{
- TileEntity tileentity = this.getTileEntity(pos, Chunk.EnumCreateEntityType.CHECK);
-
- if (tileentity != null)
- {
- tileentity.updateContainingBlockInfo();
- }
- }
-
- if (!this.world.isRemote && block1 != block)
- {
block.onBlockAdded(this.world, pos, state);
}
- if (block instanceof ITileEntityProvider)
+ if (block.hasTileEntity(state))
{
- TileEntity tileentity1 = this.getTileEntity(pos, Chunk.EnumCreateEntityType.CHECK);
+ TileEntity tileentity1 = this.getTileEntity(pos, EnumCreateEntityType.CHECK);
if (tileentity1 == null)
{
- tileentity1 = ((ITileEntityProvider)block).createNewTileEntity(this.world, block.getMetaFromState(state));
+ tileentity1 = block.createTileEntity(this.world, state);
this.world.setTileEntity(pos, tileentity1);
}
@@ -738,11 +839,22 @@
k = this.entityLists.length - 1;
}
+ net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(new net.minecraftforge.event.entity.EntityEvent.EnteringChunk(entityIn, this.x, this.z, entityIn.chunkCoordX, entityIn.chunkCoordZ));
entityIn.addedToChunk = true;
entityIn.chunkCoordX = this.x;
entityIn.chunkCoordY = k;
entityIn.chunkCoordZ = this.z;
this.entityLists[k].add(entityIn);
+ this.markDirty(); // Forge - ensure chunks are marked to save after an entity add
+ // Paper start
+ if (entityIn instanceof EntityItem) {
+ itemCounts[k]++;
+ } else if (entityIn instanceof IInventory) {
+ inventoryEntityCounts[k]++;
+ }
+ // Paper end
+ if (loaded) handleSpigotEntityAdd(entityIn); // CatServer
+
}
public void removeEntity(Entity entityIn)
@@ -763,6 +875,15 @@
}
this.entityLists[index].remove(entityIn);
+ this.markDirty(); // Forge - ensure chunks are marked to save after entity removals
+ // Paper start
+ if (entityIn instanceof EntityItem) {
+ itemCounts[index]--;
+ } else if (entityIn instanceof IInventory) {
+ inventoryEntityCounts[index]--;
+ }
+ // Paper end
+ if (loaded) handleSpigotEntityRemove(entityIn); // CatServer
}
public boolean canSeeSky(BlockPos pos)
@@ -778,31 +899,35 @@
{
IBlockState iblockstate = this.getBlockState(pos);
Block block = iblockstate.getBlock();
- return !block.hasTileEntity() ? null : ((ITileEntityProvider)block).createNewTileEntity(this.world, iblockstate.getBlock().getMetaFromState(iblockstate));
+ return !block.hasTileEntity(iblockstate) ? null : block.createTileEntity(this.world, iblockstate);
}
@Nullable
- public TileEntity getTileEntity(BlockPos pos, Chunk.EnumCreateEntityType p_177424_2_)
+ public TileEntity getTileEntity(BlockPos pos, EnumCreateEntityType p_177424_2_)
{
- TileEntity tileentity = this.tileEntities.get(pos);
+ TileEntity tileentity = null;
+ if (tileentity == null) {
+ tileentity = this.tileEntities.get(pos);
+ }
+ if (tileentity != null && tileentity.isInvalid())
+ {
+ tileEntities.remove(pos);
+ tileentity = null;
+ }
+
if (tileentity == null)
{
- if (p_177424_2_ == Chunk.EnumCreateEntityType.IMMEDIATE)
+ if (p_177424_2_ == EnumCreateEntityType.IMMEDIATE && !this.loadingTileentity) // CatServer
{
tileentity = this.createNewTileEntity(pos);
this.world.setTileEntity(pos, tileentity);
}
- else if (p_177424_2_ == Chunk.EnumCreateEntityType.QUEUED)
+ else if (p_177424_2_ == EnumCreateEntityType.QUEUED)
{
- this.tileEntityPosQueue.add(pos);
+ this.tileEntityPosQueue.add(pos.toImmutable());
}
}
- else if (tileentity.isInvalid())
- {
- this.tileEntities.remove(pos);
- return null;
- }
return tileentity;
}
@@ -819,10 +944,12 @@
public void addTileEntity(BlockPos pos, TileEntity tileEntityIn)
{
+ if (loadingTileentity) throw new IllegalStateException("Try to add tileentity when chunk loading"); // CatServer
+ if (tileEntityIn.getWorld() != this.world) //Forge don't call unless it's changed, could screw up bad mods.
tileEntityIn.setWorld(this.world);
tileEntityIn.setPos(pos);
- if (this.getBlockState(pos).getBlock() instanceof ITileEntityProvider)
+ if (this.getBlockState(pos).getBlock().hasTileEntity(this.getBlockState(pos)))
{
if (this.tileEntities.containsKey(pos))
{
@@ -836,6 +963,7 @@
public void removeTileEntity(BlockPos pos)
{
+ if (loadingTileentity) throw new IllegalStateException("Try to remove tileentity when chunk loading"); // CatServer
if (this.loaded)
{
TileEntity tileentity = this.tileEntities.remove(pos);
@@ -850,16 +978,21 @@
public void onLoad()
{
this.loaded = true;
+ handleSpigotOnLoaded(); // CatServer
+ loadingTileentity = true; // CatServer
this.world.addTileEntities(this.tileEntities.values());
+ loadingTileentity = false; // CatServer
for (ClassInheritanceMultiMap<Entity> classinheritancemultimap : this.entityLists)
{
- this.world.loadEntities(classinheritancemultimap);
+ this.world.loadEntities(com.google.common.collect.ImmutableList.copyOf(classinheritancemultimap));
}
+ net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(new net.minecraftforge.event.world.ChunkEvent.Load(this));
}
public void onUnload()
{
+ Arrays.stream(entityLists).forEach(multimap -> com.google.common.collect.Lists.newArrayList(multimap.getByClass(net.minecraft.entity.player.EntityPlayer.class)).forEach(player -> world.updateEntityWithOptionalForce(player, false))); // FORGE - Fix for MC-92916
this.loaded = false;
for (TileEntity tileentity : this.tileEntities.values())
@@ -869,8 +1002,16 @@
for (ClassInheritanceMultiMap<Entity> classinheritancemultimap : this.entityLists)
{
- this.world.unloadEntities(classinheritancemultimap);
+ // 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)
+ this.world.unloadEntities(
+ classinheritancemultimap
+ .stream()
+ .filter(entity -> !(entity instanceof EntityPlayerMP))
+ .collect(Collectors.toCollection(() -> new ClassInheritanceMultiMap<>(Entity.class)))
+ );
}
+ net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(new net.minecraftforge.event.world.ChunkEvent.Unload(this));
}
public void markDirty()
@@ -880,8 +1021,8 @@
public void getEntitiesWithinAABBForEntity(@Nullable Entity entityIn, AxisAlignedBB aabb, List<Entity> listToFill, Predicate <? super Entity > filter)
{
- int i = MathHelper.floor((aabb.minY - 2.0D) / 16.0D);
- int j = MathHelper.floor((aabb.maxY + 2.0D) / 16.0D);
+ int i = MathHelper.floor((aabb.minY - World.MAX_ENTITY_RADIUS) / 16.0D);
+ int j = MathHelper.floor((aabb.maxY + World.MAX_ENTITY_RADIUS) / 16.0D);
i = MathHelper.clamp(i, 0, this.entityLists.length - 1);
j = MathHelper.clamp(j, 0, this.entityLists.length - 1);
@@ -889,6 +1030,15 @@
{
if (!this.entityLists[k].isEmpty())
{
+ // Paper start - Don't search for inventories if we have none, and that is all we want
+ /*
+ * We check if they want inventories by seeing if it is the static `IEntitySelector.c`
+ *
+ * Make sure the inventory selector stays in sync.
+ * It should be the one that checks `var1 instanceof IInventory && var1.isAlive()`
+ */
+ if (filter == EntitySelectors.HAS_INVENTORY && inventoryEntityCounts[k] <= 0) continue;
+ // Paper end
for (Entity entity : this.entityLists[k])
{
if (entity.getEntityBoundingBox().intersects(aabb) && entity != entityIn)
@@ -918,13 +1068,24 @@
public <T extends Entity> void getEntitiesOfTypeWithinAABB(Class <? extends T > entityClass, AxisAlignedBB aabb, List<T> listToFill, Predicate <? super T > filter)
{
- int i = MathHelper.floor((aabb.minY - 2.0D) / 16.0D);
- int j = MathHelper.floor((aabb.maxY + 2.0D) / 16.0D);
+ int i = MathHelper.floor((aabb.minY - World.MAX_ENTITY_RADIUS) / 16.0D);
+ int j = MathHelper.floor((aabb.maxY + World.MAX_ENTITY_RADIUS) / 16.0D);
i = MathHelper.clamp(i, 0, this.entityLists.length - 1);
j = MathHelper.clamp(j, 0, this.entityLists.length - 1);
+ // Paper start
+ int[] counts;
+ if (EntityItem.class.isAssignableFrom(entityClass)) {
+ counts = itemCounts;
+ } else if (IInventory.class.isAssignableFrom(entityClass)) {
+ counts = inventoryEntityCounts;
+ } else {
+ counts = null;
+ }
+ // Paper end
for (int k = i; k <= j; ++k)
{
+ if (counts != null && counts[k] <= 0) continue; // Paper - Don't check a chunk if it doesn't have the type we are looking for
for (T t : this.entityLists[k].getByClass(entityClass))
{
if (t.getEntityBoundingBox().intersects(aabb) && (filter == null || filter.apply(t)))
@@ -995,8 +1156,70 @@
}
}
+ public void populateCB(IChunkProvider chunkProvider, IChunkGenerator chunkGenrator, boolean newChunk)
+ {
+ world.timings.syncChunkLoadPostTimer.startTiming(); // Spigot
+ Server server = world.getServer();
+ if (server != null) {
+ /*
+ * If it's a new world, the first few chunks are generated inside
+ * the World constructor. We can't reliably alter that, so we have
+ * no way of creating a CraftWorld/CraftServer at that point.
+ */
+ server.getPluginManager().callEvent(new org.bukkit.event.world.ChunkLoadEvent(bukkitChunk, newChunk));
+ }
+
+ // Update neighbor counts
+ for (int x = -2; x < 3; x++) {
+ for (int z = -2; z < 3; z++) {
+ if (x == 0 && z == 0) {
+ continue;
+ }
+
+ Chunk neighbor = getWorld().getChunkIfLoaded(this.x + x, this.z + z);
+ if (neighbor != null) {
+ neighbor.setNeighborLoaded(-x, -z);
+ setNeighborLoaded(x, z);
+ }
+ }
+ }
+ Chunk chunk = chunkProvider.getLoadedChunk(this.x, this.z - 1);
+ Chunk chunk1 = chunkProvider.getLoadedChunk(this.x + 1, this.z);
+ Chunk chunk2 = chunkProvider.getLoadedChunk(this.x, this.z + 1);
+ Chunk chunk3 = chunkProvider.getLoadedChunk(this.x - 1, this.z);
+
+ if (chunk1 != null && chunk2 != null && chunkProvider.getLoadedChunk(this.x + 1, this.z + 1) != null)
+ {
+ this.populate(chunkGenrator);
+ }
+
+ if (chunk3 != null && chunk2 != null && chunkProvider.getLoadedChunk(this.x - 1, this.z + 1) != null)
+ {
+ chunk3.populate(chunkGenrator);
+ }
+
+ if (chunk != null && chunk1 != null && chunkProvider.getLoadedChunk(this.x + 1, this.z - 1) != null)
+ {
+ chunk.populate(chunkGenrator);
+ }
+
+ if (chunk != null && chunk3 != null)
+ {
+ Chunk chunk4 = chunkProvider.getLoadedChunk(this.x - 1, this.z - 1);
+
+ if (chunk4 != null)
+ {
+ chunk4.populate(chunkGenrator);
+ }
+ }
+ world.timings.syncChunkLoadPostTimer.stopTiming(); // Spigot
+ }
+
protected void populate(IChunkGenerator generator)
{
+ if (populating != null && net.minecraftforge.common.ForgeModContainer.logCascadingWorldGeneration) logCascadingWorldGeneration();
+ ChunkPos prev = populating;
+ populating = this.getPos();
if (this.isTerrainPopulated())
{
if (generator.generateStructures(this, this.x, this.z))
@@ -1008,8 +1231,32 @@
{
this.checkLight();
generator.populate(this.x, this.z);
+ BlockSand.fallInstantly = true;
+ Random random = new Random();
+ random.setSeed(world.getSeed());
+ long xRand = random.nextLong() / 2L * 2L + 1L;
+ long zRand = random.nextLong() / 2L * 2L + 1L;
+ random.setSeed((long) this.x * xRand + (long) this.z * zRand ^ world.getSeed());
+
+ org.bukkit.World world = this.world.getWorld();
+ if (world != null) {
+ this.world.populating = true;
+ try {
+ for (org.bukkit.generator.BlockPopulator populator : world.getPopulators()) {
+ populator.populate(world, random, bukkitChunk);
+ }
+ } finally {
+ this.world.populating = false;
+ }
+ }
+ BlockSand.fallInstantly = false;
+ this.world.getServer().getPluginManager().callEvent(new org.bukkit.event.world.ChunkPopulateEvent(bukkitChunk));
+ if (world != null && !catserver.server.CatServer.getConfig().disableForgeGenerateWorlds.contains(world.getName())) {
+ net.minecraftforge.fml.common.registry.GameRegistry.generateWorld(this.x, this.z, this.world, generator, this.world.getChunkProvider());
+ }
this.markDirty();
}
+ populating = prev;
}
public BlockPos getPrecipitationHeight(BlockPos pos)
@@ -1064,7 +1311,7 @@
{
BlockPos blockpos = this.tileEntityPosQueue.poll();
- if (this.getTileEntity(blockpos, Chunk.EnumCreateEntityType.CHECK) == null && this.getBlockState(blockpos).getBlock().hasTileEntity())
+ if (this.getTileEntity(blockpos, EnumCreateEntityType.CHECK) == null && this.getBlockState(blockpos).getBlock().hasTileEntity(this.getBlockState(blockpos)))
{
TileEntity tileentity = this.createNewTileEntity(blockpos);
this.world.setTileEntity(blockpos, tileentity);
@@ -1075,7 +1322,7 @@
public boolean isPopulated()
{
- return this.ticked && this.isTerrainPopulated && this.isLightPopulated;
+ return this.ticked && this.isTerrainPopulated; // CatServer - remove light populated check
}
public boolean wasTicked()
@@ -1128,6 +1375,13 @@
@SideOnly(Side.CLIENT)
public void read(PacketBuffer buf, int availableSections, boolean groundUpContinuous)
{
+ for(TileEntity tileEntity : tileEntities.values())
+ {
+ tileEntity.updateContainingBlockInfo();
+ tileEntity.getBlockMetadata();
+ tileEntity.getBlockType();
+ }
+
boolean flag = this.world.provider.hasSkyLight();
for (int i = 0; i < this.storageArrays.length; ++i)
@@ -1176,10 +1430,16 @@
this.isTerrainPopulated = true;
this.generateHeightMap();
+ List<TileEntity> invalidList = new java.util.ArrayList<TileEntity>();
+
for (TileEntity tileentity : this.tileEntities.values())
{
+ if (tileentity.shouldRefresh(this.world, tileentity.getPos(), tileentity.getBlockType().getStateFromMeta(tileentity.getBlockMetadata()), getBlockState(tileentity.getPos())))
+ invalidList.add(tileentity);
tileentity.updateContainingBlockInfo();
}
+
+ for (TileEntity te : invalidList) te.invalidate();
}
public Biome getBiome(BlockPos pos, BiomeProvider provider)
@@ -1190,7 +1450,13 @@
if (k == 255)
{
- Biome biome = provider.getBiome(pos, Biomes.PLAINS);
+ // Forge: checking for client ensures that biomes are only generated on integrated server
+ // in singleplayer. Generating biomes on the client may corrupt the biome ID arrays on
+ // the server while they are being generated because IntCache can't be thread safe,
+ // so client and server may end up filling the same array.
+ // This is not necessary in 1.13 and newer versions.
+ Biome biome = world.isRemote ? Biomes.PLAINS : provider.getBiome(pos, Biomes.PLAINS);
+
k = Biome.getIdForBiome(biome);
this.blockBiomeArray[j << 4 | i] = (byte)(k & 255);
}
@@ -1244,13 +1510,13 @@
BlockPos blockpos1 = blockpos.add(k, (j << 4) + i1, l);
boolean flag = i1 == 0 || i1 == 15 || k == 0 || k == 15 || l == 0 || l == 15;
- if (this.storageArrays[j] == NULL_BLOCK_STORAGE && flag || this.storageArrays[j] != NULL_BLOCK_STORAGE && this.storageArrays[j].get(k, i1, l).getMaterial() == Material.AIR)
+ if (this.storageArrays[j] == NULL_BLOCK_STORAGE && flag || this.storageArrays[j] != NULL_BLOCK_STORAGE && this.storageArrays[j].get(k, i1, l).getBlock().isAir(this.storageArrays[j].get(k, i1, l), this.world, blockpos1))
{
for (EnumFacing enumfacing : EnumFacing.values())
{
BlockPos blockpos2 = blockpos1.offset(enumfacing);
- if (this.world.getBlockState(blockpos2).getLightValue() > 0)
+ if (this.world.getBlockState(blockpos2).getLightValue(this.world, blockpos2) > 0)
{
this.world.checkLight(blockpos2);
}
@@ -1381,7 +1647,7 @@
{
blockpos$mutableblockpos.setPos(blockpos$mutableblockpos.getX(), l, blockpos$mutableblockpos.getZ());
- if (this.getBlockState(blockpos$mutableblockpos).getLightValue() > 0)
+ if (this.getBlockState(blockpos$mutableblockpos).getLightValue(this.world, blockpos$mutableblockpos) > 0)
{
this.world.checkLight(blockpos$mutableblockpos);
}
@@ -1420,6 +1686,7 @@
else
{
System.arraycopy(newHeightMap, 0, this.heightMap, 0, this.heightMap.length);
+ this.heightMapMinimum = com.google.common.primitives.Ints.min(this.heightMap); // Forge: fix MC-117412
}
}
@@ -1489,4 +1756,55 @@
QUEUED,
CHECK;
}
+
+ /* ======================================== FORGE START =====================================*/
+ /**
+ * Removes the tile entity at the specified position, only if it's
+ * marked as invalid.
+ */
+ public void removeInvalidTileEntity(BlockPos pos)
+ {
+ if (loaded)
+ {
+ TileEntity entity = (TileEntity)tileEntities.get(pos);
+ if (entity != null && entity.isInvalid())
+ {
+ tileEntities.remove(pos);
+ }
+ }
+ }
+
+ private static ChunkPos populating = null; // keep track of cascading chunk generation during chunk population
+
+ private void logCascadingWorldGeneration()
+ {
+ net.minecraftforge.fml.common.ModContainer activeModContainer = net.minecraftforge.fml.common.Loader.instance().activeModContainer();
+ String format = "{} loaded a new chunk {} in dimension {} ({}) while populating chunk {}, causing cascading worldgen lag.";
+
+ if (activeModContainer == null) { // vanilla minecraft has problems too (MC-114332), log it at a quieter level.
+ net.minecraftforge.fml.common.FMLLog.log.debug(format, "Minecraft", this.getPos(), this.world.provider.getDimension(), this.world.provider.getDimensionType().getName(), populating);
+ net.minecraftforge.fml.common.FMLLog.log.debug("Consider setting 'fixVanillaCascading' to 'true' in the Forge config to fix many cases where this occurs in the base game.");
+ } else {
+ net.minecraftforge.fml.common.FMLLog.log.warn(format, activeModContainer.getName(), this.getPos(), this.world.provider.getDimension(), this.world.provider.getDimensionType().getName(), populating);
+ net.minecraftforge.fml.common.FMLLog.log.warn("Please report this to the mod's issue tracker. This log can be disabled in the Forge config.");
+ }
+ }
+
+ private final net.minecraftforge.common.capabilities.CapabilityDispatcher capabilities;
+ @Nullable
+ public net.minecraftforge.common.capabilities.CapabilityDispatcher getCapabilities()
+ {
+ return capabilities;
+ }
+ @Override
+ public boolean hasCapability(net.minecraftforge.common.capabilities.Capability<?> capability, @Nullable EnumFacing facing)
+ {
+ return capabilities == null ? false : capabilities.hasCapability(capability, facing);
+ }
+ @Override
+ @Nullable
+ public <T> T getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, @Nullable EnumFacing facing)
+ {
+ return capabilities == null ? null : capabilities.getCapability(capability, facing);
+ }
}