package micdoodle8.mods.galacticraft.core.tile;
import micdoodle8.mods.galacticraft.api.tile.ITileClientUpdates;
import micdoodle8.mods.galacticraft.api.vector.BlockVec3;
import micdoodle8.mods.galacticraft.core.Constants;
import micdoodle8.mods.galacticraft.core.GCBlocks;
import micdoodle8.mods.galacticraft.core.GalacticraftCore;
import micdoodle8.mods.galacticraft.core.network.IPacketReceiver;
import micdoodle8.mods.galacticraft.core.network.PacketDynamic;
import micdoodle8.mods.galacticraft.core.util.GCCoreUtil;
import micdoodle8.mods.galacticraft.core.util.RedstoneUtil;
import net.minecraft.block.Block;
import net.minecraft.block.BlockAir;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityCreature;
import net.minecraft.entity.ai.RandomPositionGenerator;
import net.minecraft.entity.monster.IMob;
import net.minecraft.init.Blocks;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.pathfinding.PathNavigate;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.BlockPos;
import net.minecraft.util.ITickable;
import net.minecraft.util.MathHelper;
import net.minecraft.util.Vec3;
import net.minecraft.world.EnumSkyBlock;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import io.netty.buffer.ByteBuf;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
//ITileClientUpdates for changing in facing; IPacketReceiver for initial transfer of NBT Data (airToRestore)
public class TileEntityArclamp extends TileEntity implements ITickable, ITileClientUpdates, IPacketReceiver
{
private static final int LIGHTRANGE = 14;
private int ticks = 0;
private int sideRear = 0;
public int facing = 0;
private HashSet<BlockVec3> airToRestore = new HashSet();
private intBucket[] buckets;
private static intBucket[] bucketsServer;
private static intBucket[] bucketsClient;
private static AtomicBoolean usingBucketsServer = new AtomicBoolean();
private static AtomicBoolean usingBucketsClient = new AtomicBoolean();
private AtomicBoolean usingBuckets;
private static final int SIZELIST = 65536;
private int[] lightUpdateBlockList;
private static int[] lightUpdateBlockListServer = null;
private static int[] lightUpdateBlockListClient = null;
private static AtomicBoolean usingLightListServer = new AtomicBoolean();
private static AtomicBoolean usingLightListClient = new AtomicBoolean();
private AtomicBoolean usingLightList;
private boolean isActive = false;
private AxisAlignedBB thisAABB;
private AxisAlignedBB renderAABB;
private Vec3 thisPos;
private int facingSide = 0;
static
{
bucketsServer = new intBucket[256];
bucketsClient = new intBucket[256];
checkedInit(bucketsServer);
checkedInit(bucketsClient);
}
@Override
public void update()
{
boolean initialLight = false;
// if (this.updateClientFlag)
// {
// this.updateAllInDimension();
// this.updateClientFlag = false;
// }
if (RedstoneUtil.isBlockReceivingRedstone(this.worldObj, this.getPos()))
{
if (this.isActive)
{
this.isActive = false;
this.revertAir();
this.markDirty();
}
}
else if (!this.isActive && this.pos.getX() >= -30000000 + 32 && this.pos.getZ() >= -30000000 + 32 && this.pos.getX() < 30000000 - 32 && this.pos.getZ() < 30000000 -32 )
{
this.isActive = true;
initialLight = true;
}
if (this.isActive)
{
//Test for first tick after placement
if (this.thisAABB == null)
{
initialLight = true;
int side = this.getBlockMetadata();
switch (side)
{
case 0:
this.sideRear = side; //Down
this.facingSide = this.facing + 2;
break;
case 1:
this.sideRear = side; //Up
this.facingSide = this.facing + 2;
break;
case 2:
this.sideRear = side; //North
this.facingSide = this.facing;
if (this.facing > 1)
{
this.facingSide = 7 - this.facing;
}
break;
case 3:
this.sideRear = side; //South
this.facingSide = this.facing;
if (this.facing > 1)
{
this.facingSide += 2;
}
break;
case 4:
this.sideRear = side; //West
this.facingSide = this.facing;
break;
case 5:
this.sideRear = side; //East
this.facingSide = this.facing;
if (this.facing > 1)
{
this.facingSide = 5 - this.facing;
}
break;
default:
}
this.thisAABB = getAABBforSideAndFacing();
}
if (initialLight || this.ticks % 100 == 0)
{
this.lightArea();
}
if (!this.worldObj.isRemote && this.worldObj.rand.nextInt(10) == 0)
{
List<Entity> moblist = this.worldObj.getEntitiesInAABBexcluding(null, this.thisAABB, IMob.mobSelector);
if (!moblist.isEmpty())
{
Vec3 thisVec3 = new Vec3(this.getPos().getX(), this.getPos().getY(), this.getPos().getZ());
for (Entity entry : moblist)
{
if (!(entry instanceof EntityCreature))
{
continue;
}
EntityCreature mob = (EntityCreature) entry;
//Check whether the mob can actually *see* the arclamp tile
//if (this.worldObj.func_147447_a(thisPos, Vec3.createVectorHelper(e.posX, e.posY, e.posZ), true, true, false) != null) continue;
PathNavigate nav = mob.getNavigator();
if (nav == null)
{
continue;
}
Vec3 vecNewTarget = RandomPositionGenerator.findRandomTargetBlockAwayFrom(mob, 28, 11, this.thisPos);
if (vecNewTarget == null)
{
continue;
}
double distanceNew = vecNewTarget.distanceTo(thisVec3);
double distanceCurrent = thisVec3.squareDistanceTo(new Vec3(mob.posX, mob.posY, mob.posZ));
if (distanceNew > distanceCurrent)
{
Vec3 vecOldTarget = null;
if (nav.getPath() != null && !nav.getPath().isFinished())
{
vecOldTarget = nav.getPath().getPosition(mob);
}
if (vecOldTarget == null || distanceCurrent > vecOldTarget.squareDistanceTo(thisVec3))
{
nav.tryMoveToXYZ(vecNewTarget.xCoord, vecNewTarget.yCoord, vecNewTarget.zCoord, 1.3D);
}
}
}
}
}
}
this.ticks++;
}
private AxisAlignedBB getAABBforSideAndFacing()
{
int x = this.pos.getX();
int y = this.pos.getY();
int z = this.pos.getZ();
int rangeForSide[] = new int[6];
for (int i = 0; i < 6; i++)
{
rangeForSide[i] = (i == this.sideRear) ? 2 : (i == (this.facingSide ^ 1)) ? 4 : 25;
}
return AxisAlignedBB.fromBounds(x - rangeForSide[4], y - rangeForSide[0], z - rangeForSide[2], x + rangeForSide[5], y + rangeForSide[1], z + rangeForSide[3]);
}
@Override
public void onLoad()
{
this.thisPos = new Vec3(this.getPos().getX() + 0.5D, this.getPos().getY() + 0.5D, this.getPos().getZ() + 0.5D);
this.ticks = 0;
this.thisAABB = null;
if (this.worldObj.isRemote)
{
this.buckets = bucketsClient;
this.usingBuckets = usingBucketsClient;
if (lightUpdateBlockListClient == null)
{
lightUpdateBlockListClient = new int[SIZELIST];
}
this.lightUpdateBlockList = lightUpdateBlockListClient;
this.usingLightList = usingLightListClient;
this.clientOnLoad();
GalacticraftCore.packetPipeline.sendToServer(new PacketDynamic(this));
}
else
{
this.buckets = bucketsServer;
this.usingBuckets = usingBucketsServer;
if (lightUpdateBlockListServer == null)
{
lightUpdateBlockListServer = new int[SIZELIST];
}
this.lightUpdateBlockList = lightUpdateBlockListServer;
this.usingLightList = usingLightListServer;
this.isActive = this.pos.getX() >= -30000000 + 32 && this.pos.getZ() >= -30000000 + 32 && this.pos.getX() < 30000000 - 32 && this.pos.getZ() < 30000000 -32;
}
}
@Override
public void invalidate()
{
this.revertAir();
this.isActive = false;
super.invalidate();
}
public void lightArea()
{
if (this.usingBuckets.getAndSet(true) || this.usingLightList.getAndSet(true))
{
return;
}
// long time1 = System.nanoTime();
int index = 0;
Block air = Blocks.air;
Block breatheableAirID = GCBlocks.breatheableAir;
IBlockState brightAir = GCBlocks.brightAir.getDefaultState();
IBlockState brightBreatheableAir = GCBlocks.brightBreatheableAir.getDefaultState();
boolean dirty = false;
checkedClear();
HashSet airToRevert = new HashSet();
airToRevert.addAll(airToRestore);
LinkedList airNew = new LinkedList();
LinkedList<BlockVec3> currentLayer = new LinkedList();
LinkedList<BlockVec3> nextLayer = new LinkedList();
BlockVec3 thisvec = new BlockVec3(this);
currentLayer.add(thisvec);
World world = this.worldObj;
int sideskip1 = this.sideRear;
int sideskip2 = this.facingSide ^ 1;
int side, bits;
for (int i = 0; i < 6; i++)
{
if (i != sideskip1 && i != sideskip2 && i != (sideskip1 ^ 1) && i != (sideskip2 ^ 1))
{
BlockVec3 onEitherSide = thisvec.newVecSide(i);
Block b = onEitherSide.getBlockIDsafe_noChunkLoad(world);
if (b != null && b.getLightOpacity() < 15)
{
currentLayer.add(onEitherSide);
}
}
}
BlockVec3 inFront = new BlockVec3(this);
for (int i = 0; i < 4; i++)
{
inFront = inFront.newVecSide(this.facingSide);
Block b = inFront.getBlockIDsafe_noChunkLoad(world);
if (b == null || b.getLightOpacity() == 15)
{
break;
}
inFront = inFront.newVecSide(sideskip1 ^ 1);
b = inFront.getBlockIDsafe_noChunkLoad(world);
if (b != null && b.getLightOpacity() < 15)
{
currentLayer.add(inFront);
}
else
{
break;
}
}
inFront = new BlockVec3(this).newVecSide(this.facingSide);
for (int count = 0; count < LIGHTRANGE; count++)
{
for (BlockVec3 vec : currentLayer)
{
//Shape the arc lamp lighted area to more of a cone in front of it
if (count > 1)
{
int offset = 0;
switch (this.facingSide)
{
case 0:
offset = inFront.y - vec.y;
break;
case 1:
offset = vec.y - inFront.y;
break;
case 2:
offset = inFront.z - vec.z;
break;
case 3:
offset = vec.z - inFront.z;
break;
case 4:
offset = inFront.x - vec.x;
break;
case 5:
offset = vec.x - inFront.x;
break;
}
int offset2 = 0;
switch (this.sideRear ^ 1)
{
case 0:
offset2 = inFront.y - vec.y;
break;
case 1:
offset2 = vec.y - inFront.y;
break;
case 2:
offset2 = inFront.z - vec.z;
break;
case 3:
offset2 = vec.z - inFront.z;
break;
case 4:
offset2 = inFront.x - vec.x;
break;
case 5:
offset2 = vec.x - inFront.x;
break;
}
if (offset2 - 2 > offset) offset = offset2 - 2;
if (Math.abs(vec.x - inFront.x) > offset + 2) continue;
if (Math.abs(vec.y - inFront.y) > offset + 2) continue;
if (Math.abs(vec.z - inFront.z) > offset + 2) continue;
}
//Now process each layer outwards from the source, finding new blocks to light (similar to ThreadFindSeal)
//This is high performance code using our own custom HashSet (that's intBucket)
side = 0;
bits = vec.sideDoneBits;
boolean doShine = false;
do
{
//Skip the side which this was entered from
//and never go 'backwards'
if ((bits & (1 << side)) == 0)
{
BlockVec3 sideVec = vec.newVecSide(side);
boolean toAdd = false;
if (!checkedContains(vec, side))
{
checkedAdd(sideVec);
toAdd = true;
}
Block b = sideVec.getBlockIDsafe_noChunkLoad(world);
if (b instanceof BlockAir)
{
if (toAdd && side != sideskip1 && side != sideskip2)
{
nextLayer.add(sideVec);
}
}
else
{
doShine = true;
//Glass blocks go through to the next layer as well
if (side != sideskip1 && side != sideskip2)
{
if (toAdd && b != null && b.getLightOpacity(world, sideVec.toBlockPos()) == 0)
{
nextLayer.add(sideVec);
}
}
}
}
side++;
}
while (side < 6);
if (doShine)
{
airNew.add(vec);
Block id = vec.getBlockIDsafe_noChunkLoad(world);
if (Blocks.air == id)
{
this.brightenAir(world, vec, brightAir);
index = this.checkLightPartA(EnumSkyBlock.BLOCK, vec.toBlockPos(), index);
dirty = true;
}
else if (id == breatheableAirID)
{
this.brightenAir(world, vec, brightBreatheableAir);
index = this.checkLightPartA(EnumSkyBlock.BLOCK, vec.toBlockPos(), index);
dirty = true;
}
}
}
if (nextLayer.size() == 0)
{
break;
}
currentLayer = nextLayer;
nextLayer = new LinkedList<BlockVec3>();
}
if (dirty)
{
this.markDirty();
this.checkLightPartB(EnumSkyBlock.BLOCK, index);
}
//Look for any holdover bright blocks which are no longer lit (e.g. because the Arc Lamp became blocked in a tunnel)
airToRevert.removeAll(airNew);
index = 0;
dirty = false;
for (Object obj : airToRevert)
{
BlockVec3 vec = (BlockVec3) obj;
this.setDarkerAir(vec);
index = this.checkLightPartA(EnumSkyBlock.BLOCK, vec.toBlockPos(), index);
this.airToRestore.remove(vec);
dirty = true;
}
if (dirty)
{
this.markDirty();
this.checkLightPartB(EnumSkyBlock.BLOCK, index);
}
// long time3 = System.nanoTime();
// float total = (time3 - time1) / 1000000.0F;
// GCLog.info(" Total Time taken: " + String.format("%.2f", total) + "ms");
this.usingBuckets.set(false);
this.usingLightList.set(false);
}
@Override
public void readFromNBT(NBTTagCompound nbt)
{
super.readFromNBT(nbt);
this.facing = nbt.getInteger("Facing");
this.airToRestore.clear();
NBTTagList airBlocks = nbt.getTagList("AirBlocks", 10);
if (airBlocks.tagCount() > 0)
{
for (int j = airBlocks.tagCount() - 1; j >= 0; j--)
{
NBTTagCompound tag1 = airBlocks.getCompoundTagAt(j);
if (tag1 != null)
{
this.airToRestore.add(BlockVec3.readFromNBT(tag1));
}
}
}
}
@Override
public void writeToNBT(NBTTagCompound nbt)
{
super.writeToNBT(nbt);
nbt.setInteger("Facing", this.facing);
NBTTagList airBlocks = new NBTTagList();
for (BlockVec3 vec : this.airToRestore)
{
NBTTagCompound tag = new NBTTagCompound();
vec.writeToNBT(tag);
airBlocks.appendTag(tag);
}
nbt.setTag("AirBlocks", airBlocks);
}
public void facingChanged()
{
this.facing -= 2;
if (this.facing < 0)
{
this.facing = 1 - this.facing;
//facing sequence: 0 - 3 - 1 - 2
}
this.updateAllInDimension();
this.thisAABB = null;
this.revertAir();
this.markDirty();
this.ticks = 91;
}
private void brightenAir(World world, BlockVec3 vec, IBlockState newState)
{
BlockPos blockpos = vec.toBlockPos();
Chunk chunk = this.worldObj.getChunkFromBlockCoords(blockpos);
IBlockState oldState = chunk.setBlockState(blockpos, newState);
if (this.worldObj.isRemote) this.worldObj.markAndNotifyBlock(blockpos, chunk, oldState, newState, 2);
//No block update on server - not necessary for changing air to air (also must not trigger a sealer edge check!)
this.airToRestore.add(vec);
}
private void setDarkerAir(BlockVec3 vec)
{
Block b = vec.getBlock(this.worldObj);
IBlockState newState;
if (b == GCBlocks.brightAir)
{
newState = Blocks.air.getDefaultState();
}
else if (b == GCBlocks.brightBreatheableAir)
{
newState = GCBlocks.breatheableAir.getDefaultState();
}
else
{
return;
}
// Roughly similar to: this.worldObj.setBlockState(pos, newState, (this.worldObj.isRemote) ? 2 : 0);
BlockPos blockpos = vec.toBlockPos();
Chunk chunk = this.worldObj.getChunkFromBlockCoords(blockpos);
IBlockState oldState = chunk.setBlockState(blockpos, newState);
if (this.worldObj.isRemote) this.worldObj.markAndNotifyBlock(blockpos, chunk, oldState, newState, 2);
//No block update on server - not necessary for changing air to air (also must not trigger a sealer edge check!)
}
private void revertAir()
{
int size = this.airToRestore.size();
int index = 0;
for (BlockVec3 vec : this.airToRestore)
{
this.setDarkerAir(vec);
}
if (!this.usingLightList.getAndSet(true))
{
for (BlockVec3 vec : this.airToRestore)
{
index = this.checkLightPartA(EnumSkyBlock.BLOCK, vec.toBlockPos(), index);
}
this.checkLightPartB(EnumSkyBlock.BLOCK, index);
this.usingLightList.set(false);
}
this.airToRestore.clear();
this.checkLightFor(EnumSkyBlock.BLOCK, this.pos);
}
public boolean checkLightFor(EnumSkyBlock lightType, BlockPos bp)
{
if (!this.worldObj.isAreaLoaded(bp, 17, false))
{
return false;
}
World world = this.worldObj;
BlockPos blockpos;
int i = 0;
int index = 0;
int savedLight = world.getLightFor(lightType, bp);
int rawLight = this.getRawLight(bp, lightType);
int testx = bp.getX();
int testy = bp.getY();
int testz = bp.getZ();
int x = testx - 32;
int y = testy - 32;
int z = testz - 32;
int value, opacity, arraylight;
int xx, yy, zz, range;
LinkedList<BlockPos> result = new LinkedList<>();
if (rawLight > savedLight) //Light switched on
{
lightUpdateBlockList[index++] = 133152; //32, 32, 32 = the 0 position
}
else if (rawLight < savedLight) //Light switched off ?
{
lightUpdateBlockList[index++] = 133152 | savedLight << 18;
while (i < index) //This becomes CRAZY LARGE
{
value = lightUpdateBlockList[i++];
xx = (value & 63) + x;
yy = (value >> 6 & 63) + y;
zz = (value >> 12 & 63) + z;
arraylight = value >> 18 & 15;
if (arraylight > 0)
{
blockpos = new BlockPos(xx, yy, zz);
if (world.getLightFor(lightType, blockpos) == arraylight) //Only gonna happen once (definitely will happen the first iteration)
{
this.setLightFor_preChecked(lightType, blockpos, 0); //= flagdone
range = MathHelper.abs_int(xx - testx) + MathHelper.abs_int(yy - testy) + MathHelper.abs_int(zz - testz);
if (range < 17)
{
GCCoreUtil.getPositionsAdjoining(xx, yy, zz, result);
for (BlockPos vec : result)
{
savedLight = world.getLightFor(lightType, vec);
if (savedLight == 0) continue; //eliminate backtracking
opacity = world.getBlockState(vec).getBlock().getLightOpacity();
if (opacity <= 0) opacity = 1;
//Tack positions onto the list as long as it looks like lit from here. i.e. saved light is adjacent light - opacity!
//There will be some errors due to coincidence / equality of light levels from 2 sources
if (savedLight == arraylight - opacity && index < SIZELIST)
{
lightUpdateBlockList[index++] = vec.getX() - x + (((savedLight << 6) + vec.getZ() - z << 6) + vec.getY() - y << 6);
}
}
}
}
}
}
i = 0;
}
while (i < index)
{
value = lightUpdateBlockList[i++];
xx = (value & 63) + x;
yy = (value >> 6 & 63) + y;
zz = (value >> 12 & 63) + z;
blockpos = new BlockPos(xx, yy, zz);
savedLight = world.getLightFor(lightType, blockpos);
rawLight = this.getRawLight(blockpos, lightType);
if (rawLight != savedLight)
{
this.setLightFor_preChecked(lightType, blockpos, rawLight); //<-------the light setting
if (rawLight > savedLight)
{
range = MathHelper.abs_int(xx - testx) + MathHelper.abs_int(yy - testy) + MathHelper.abs_int(zz - testz);
if (range < 17 && index < SIZELIST - 6)
{
GCCoreUtil.getPositionsAdjoining(xx, yy, zz, result);
for (BlockPos vec : result)
{
if (world.getLightFor(lightType, vec) < rawLight - 1) //-1 here because opacity can't be less than 1 in getRawLight
{
//Tack even more positions on to the end of the list - this propagates each time we find a light source block... including going back over old positions
lightUpdateBlockList[index++] = vec.getX() - x + ((vec.getZ() - z << 6) + vec.getY() - y << 6);
}
}
}
}
}
}
return true;
}
public int checkLightPartA(EnumSkyBlock lightType, BlockPos bp, int indexIn)
{
if (indexIn >= SIZELIST)
{
return indexIn;
}
World world = this.worldObj;
BlockPos blockpos;
int i = indexIn;
int iNextStart = 0;
int index = indexIn;
int savedLight = world.getLightFor(lightType, bp);
int rawLight = this.getRawLight(bp, lightType);
int x = this.pos.getX() - 64;
int y = this.pos.getY() - 64;
int z = this.pos.getZ() - 64;
int testx = bp.getX();
int testy = bp.getY();
int testz = bp.getZ();
int value, opacity, arraylight;
int xx, yy, zz, range;
LinkedList<BlockPos> neighbours = new LinkedList<>();
if (rawLight > savedLight) //Light switched on
{
lightUpdateBlockList[index++] = ((testz - z << 7) + testy - y << 7) + testx - x;
}
else if (rawLight < savedLight) //Light switched off: savedLight cannot be 0 here
{
lightUpdateBlockList[index++] = ((testz - z << 7) + testy - y << 7) + testx - x | savedLight << 21;
this.setLightFor_preChecked(lightType, bp, 0);
while (i < index)
{
value = lightUpdateBlockList[i++];
xx = (value & 127) + x;
yy = (value >> 7 & 127) + y;
zz = (value >> 14 & 127) + z;
range = MathHelper.abs_int(xx - testx) + MathHelper.abs_int(yy - testy) + MathHelper.abs_int(zz - testz);
if (range < 17)
{
arraylight = value >> 21 & 15;
GCCoreUtil.getPositionsAdjoiningLoaded(xx, yy, zz, neighbours, world);
for (BlockPos vec : neighbours)
{
savedLight = world.getLightFor(lightType, vec);
if (savedLight == 0) continue; //eliminate backtracking
opacity = world.getBlockState(vec).getBlock().getLightOpacity();
if (opacity <= 0) opacity = 1;
//Tack positions onto the list as long as it looks like lit from here. i.e. saved light is adjacent light - opacity!
//There will be some errors due to coincidence / equality of light levels from 2 sources
if (savedLight == arraylight - opacity && index < SIZELIST)
{
lightUpdateBlockList[index++] = vec.getX() - x + (((savedLight << 7) + vec.getZ() - z << 7) + vec.getY() - y << 7);
this.setLightFor_preChecked(lightType, vec, 0); //darken everything ready for re-light
}
}
}
}
i = indexIn;
}
return index;
}
public boolean checkLightPartB(EnumSkyBlock lightType, int index)
{
World world = this.worldObj;
BlockPos blockpos;
int i = 0;
int savedLight, rawLight;
int testx = this.pos.getX();
int testy = this.pos.getY();
int testz = this.pos.getZ();
int x = testx - 64;
int y = testy - 64;
int z = testz - 64;
int value;
int xx, yy, zz, range;
LinkedList<BlockPos> neighbours = new LinkedList<>();
while (i < index)
{
value = lightUpdateBlockList[i++];
xx = (value & 127) + x;
yy = (value >> 7 & 127) + y;
zz = (value >> 14 & 127) + z;
blockpos = new BlockPos(xx, yy, zz);
savedLight = world.getLightFor(lightType, blockpos);
rawLight = this.getRawLight(blockpos, lightType);
if (rawLight != savedLight)
{
this.setLightFor_preChecked(lightType, blockpos, rawLight); //<-------the light setting
if (rawLight > savedLight)
{
range = MathHelper.abs_int(xx - testx) + MathHelper.abs_int(yy - testy) + MathHelper.abs_int(zz - testz);
if (range < 34 && index < SIZELIST - 6)
{
GCCoreUtil.getPositionsAdjoiningLoaded(xx, yy, zz, neighbours, world);
for (BlockPos vec : neighbours)
{
if (world.getLightFor(lightType, vec) < rawLight - 1) //-1 here because opacity can't be less than 1 in getRawLight
{
//Tack even more positions on to the end of the list - this propagates each time we find a light source block... including going back over old positions
lightUpdateBlockList[index++] = vec.getX() - x + ((vec.getZ() - z << 7) + vec.getY() - y << 7);
}
}
}
}
}
}
return true;
}
/**
* From vanilla. This is buggy, gets confused if two low opacity blocks adjacent (e.g. redstone wire, stairs)
* if those blocks are receiving similar light levels from another source
*/
private int getRawLight(BlockPos pos, EnumSkyBlock lightType)
{
Block block = this.worldObj.getBlockState(pos).getBlock();
int blockLight = block.getLightValue(this.worldObj, pos);
int light = lightType == EnumSkyBlock.SKY ? 0 : blockLight;
if (light < 14)
{
int opacity = block.getLightOpacity(this.worldObj, pos);
if (opacity < 1)
{
opacity = 1;
}
else if (opacity >= 15)
{
if (blockLight > 0)
{
opacity = 1;
}
else
{
return 0;
}
}
for (BlockPos blockpos : GCCoreUtil.getPositionsAdjoining(pos))
{
int neighbourLight = this.worldObj.getLightFor(lightType, blockpos) - opacity; //Easily picks up neighbour lighting if opacity is low...
if (neighbourLight > light)
{
if (neighbourLight >= 14)
{
return neighbourLight;
}
light = neighbourLight;
}
}
}
return light;
}
private void setLightFor_preChecked(EnumSkyBlock type, BlockPos pos, int lightValue)
{
int cx = pos.getX() >> 4;
int cz = pos.getZ() >> 4;
if (this.worldObj.getChunkProvider().chunkExists(cx, cz))
{
this.worldObj.getChunkFromChunkCoords(cx, cz).setLightFor(type, pos, lightValue);
if (this.worldObj.isRemote) this.worldObj.notifyLightSet(pos);
}
}
public boolean getEnabled()
{
return !RedstoneUtil.isBlockReceivingRedstone(this.worldObj, this.getPos());
}
@Override
public boolean shouldRefresh(World world, BlockPos pos, IBlockState oldState, IBlockState newSate)
{
return oldState.getBlock() != newSate.getBlock();
}
private void checkedAdd(BlockVec3 vec)
{
int dx = this.pos.getX() - vec.x;
int dz = this.pos.getZ() - vec.z;
if (dx < -8191 || dx > 8192) return;
if (dz < -8191 || dz > 8192) return;
intBucket bucket = buckets[((dx & 15) << 4) + (dz & 15)];
bucket.add(vec.y + ((dx & 0x3FF0) + ((dz & 0x3FF0) << 10) << 4));
}
/**
* Currently unused - the sided implementation is used instead
*/
private boolean checkedContains(BlockVec3 vec)
{
int dx = this.pos.getX() - vec.x;
int dz = this.pos.getZ() - vec.z;
if (dx < -8191 || dx > 8192) return true;
if (dz < -8191 || dz > 8192) return true;
intBucket bucket = buckets[((dx & 15) << 4) + (dz & 15)];
return bucket.contains(vec.y + ((dx & 0x3FF0) + ((dz & 0x3FF0) << 10) << 4));
}
private boolean checkedContains(BlockVec3 vec, int side)
{
int y = vec.y;
int dx = this.pos.getX() - vec.x;
int dz = this.pos.getZ() - vec.z;
switch (side)
{
case 0:
y--;
if (y < 0) return true;
break;
case 1:
y++;
if (y > 255) return true;
break;
case 2:
dz++;
break;
case 3:
dz--;
break;
case 4:
dx++;
break;
case 5:
dx--;
}
if (dx < -8191 || dx > 8192) return true;
if (dz < -8191 || dz > 8192) return true;
intBucket bucket = buckets[((dx & 15) << 4) + (dz & 15)];
return bucket.contains(y + ((dx & 0x3FF0) + ((dz & 0x3FF0) << 10) << 4));
}
private static void checkedInit(intBucket[] buckets)
{
for (int i = 0; i < 256; i++)
{
buckets[i] = new intBucket();
}
}
private void checkedClear()
{
for (int i = 0; i < 256; i++)
{
this.buckets[i].clear();
}
}
public static class intBucket
{
private int maxSize = 12; //default size
private int size = 0;
private int[] table = new int[maxSize];
public void add(int i)
{
if (this.contains(i))
return;
if (size >= maxSize)
{
int[] newTable = new int[maxSize + maxSize];
System.arraycopy(table, 0, newTable, 0, maxSize);
table = newTable;
maxSize += maxSize;
}
table[size] = i;
size++;
}
public boolean contains(int test)
{
for (int i = size - 1; i >= 0; i--)
{
if (table[i] == test)
return true;
}
return false;
}
public void clear()
{
size = 0;
}
public int size()
{
return size;
}
public int[] contents()
{
return table;
}
}
@Override
public void buildDataPacket(int[] data)
{
data[0] = this.facing;
}
@Override
public void updateClient(List<Object> data)
{
this.facing = (Integer) data.get(1);
this.revertAir();
this.thisAABB = null;
this.ticks = 86;
}
@Override
@SideOnly(Side.CLIENT)
public AxisAlignedBB getRenderBoundingBox()
{
if (this.renderAABB == null)
{
this.renderAABB = new AxisAlignedBB(pos, pos.add(1, 1, 1));
}
return this.renderAABB;
}
@Override
@SideOnly(Side.CLIENT)
public double getMaxRenderDistanceSquared()
{
return Constants.RENDERDISTANCE_LONG;
}
@Override
public void getNetworkedData(ArrayList<Object> sendData)
{
for (BlockVec3 vec : this.airToRestore)
{
sendData.add(vec);
}
}
@Override
public void decodePacketdata(ByteBuf buffer)
{
while (buffer.readableBytes() >= 12)
{
int x = buffer.readInt();
int y = buffer.readInt();
int z = buffer.readInt();
this.airToRestore.add(new BlockVec3(x, y, z));
}
}
}