package zmaster587.advancedRocketry.util; import net.minecraft.block.Block; import net.minecraft.entity.item.EntityItem; import net.minecraft.init.Blocks; import net.minecraft.item.ItemStack; import net.minecraft.world.World; import net.minecraftforge.common.util.ForgeDirection; import zmaster587.advancedRocketry.AdvancedRocketry; import zmaster587.advancedRocketry.api.AdvancedRocketryBlocks; import zmaster587.advancedRocketry.api.AreaBlob; import zmaster587.advancedRocketry.api.Configuration; import zmaster587.advancedRocketry.api.util.IBlobHandler; import zmaster587.advancedRocketry.atmosphere.AtmosphereHandler; import zmaster587.libVulpes.util.BlockPosition; import java.util.Collection; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Stack; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class AtmosphereBlob extends AreaBlob implements Runnable { static ThreadPoolExecutor pool = (Configuration.atmosphereHandleBitMask & 1) == 1 ? new ThreadPoolExecutor(3, 16, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(2)) : null; boolean executing; BlockPosition blockPos; List<AreaBlob> nearbyBlobs; public AtmosphereBlob(IBlobHandler blobHandler) { super(blobHandler); executing = false; } @Override public void removeBlock(int x, int y, int z) { BlockPosition blockPos = new BlockPosition(x, y, z); graph.remove(blockPos); graph.contains(blockPos); for(ForgeDirection direction : ForgeDirection.VALID_DIRECTIONS) { BlockPosition newBlock = blockPos.getPositionAtOffset(direction.offsetX, direction.offsetY, direction.offsetZ); if(graph.contains(newBlock) && !graph.doesPathExist(newBlock, blobHandler.getRootPosition())) runEffectOnWorldBlocks(blobHandler.getWorld(), graph.removeAllNodesConnectedTo(newBlock)); } } @Override public boolean isPositionAllowed(World world, BlockPosition pos, List<AreaBlob> otherBlobs) { for(AreaBlob blob : otherBlobs) { if(blob.contains(pos) && blob != this) return false; } return !SealableBlockHandler.INSTANCE.isBlockSealed(world, pos); } @Override public void addBlock(BlockPosition blockPos, List<AreaBlob> otherBlobs) { if(blobHandler.canFormBlob()) { if(!this.contains(blockPos) && (this.graph.size() == 0 || contains(blockPos.getPositionAtOffset(0, 1, 0)) || contains(blockPos.getPositionAtOffset(0, -1, 0)) || contains(blockPos.getPositionAtOffset(1, 0, 0)) || contains(blockPos.getPositionAtOffset(-1, 0, 0)) || contains(blockPos.getPositionAtOffset(0, 0, 1)) || contains(blockPos.getPositionAtOffset(0, 0, -1)))) { if(!executing) { this.nearbyBlobs = otherBlobs; this.blockPos = blockPos; executing = true; if((Configuration.atmosphereHandleBitMask & 1) == 1) try { pool.execute(this); } catch (RejectedExecutionException e) { AdvancedRocketry.logger.warn("Atmosphere calculation at " + this.getRootPosition() + " aborted due to oversize queue!"); } else this.run(); } } } } @Override public void run() { Stack<BlockPosition> stack = new Stack<BlockPosition>(); stack.push(blockPos); final int maxSize = (Configuration.atmosphereHandleBitMask & 2) != 0 ? (int)(Math.pow(this.getBlobMaxRadius(), 3)*((4f/3f)*Math.PI)) : this.getBlobMaxRadius(); final HashSet<BlockPosition> addableBlocks = new HashSet<BlockPosition>(); //Breadth first search; non recursive while(!stack.isEmpty()) { BlockPosition stackElement = stack.pop(); addableBlocks.add(stackElement); for(ForgeDirection dir2 : ForgeDirection.VALID_DIRECTIONS) { BlockPosition searchNextPosition = stackElement.getPositionAtOffset(dir2.offsetX, dir2.offsetY, dir2.offsetZ); //Don't path areas we have already scanned if(!graph.contains(searchNextPosition) && !addableBlocks.contains(searchNextPosition)) { boolean sealed; try { sealed = !isPositionAllowed(blobHandler.getWorld(), searchNextPosition, nearbyBlobs);//SealableBlockHandler.INSTANCE.isBlockSealed(blobHandler.getWorld(), searchNextPosition); if(!sealed) { if(((Configuration.atmosphereHandleBitMask & 2) == 0 && searchNextPosition.getDistance(this.getRootPosition()) <= maxSize) || ((Configuration.atmosphereHandleBitMask & 2) != 0 && addableBlocks.size() <= maxSize)) { stack.push(searchNextPosition); addableBlocks.add(searchNextPosition); } else { //Failed to seal, void clearBlob(); executing = false; return; } } } catch (Exception e){ //Catches errors with additional information AdvancedRocketry.logger.info("Error: AtmosphereBlob has failed to form correctly due to an error. \nCurrentBlock: " + stackElement + "\tNextPos: " + searchNextPosition + "\tDir: " + dir2 + "\tStackSize: " + stack.size()); e.printStackTrace(); //Failed to seal, void clearBlob(); executing = false; return; } } } } //only one instance can editing this at a time because this will not run again b/c "worker" is not null synchronized(graph) { for(BlockPosition blockPos2 : addableBlocks) { super.addBlock(blockPos2, nearbyBlobs); } } executing = false; } /** * @param world * @param blocks Collection containing affected locations */ protected void runEffectOnWorldBlocks(World world, Collection<BlockPosition> blocks) { if(!AtmosphereHandler.getOxygenHandler(world.provider.dimensionId).getDefaultAtmosphereType().allowsCombustion()) { List<BlockPosition> list; synchronized (graph) { list = new LinkedList<BlockPosition>(blocks); } for(BlockPosition pos : list) { Block block = world.getBlock(pos.x, pos.y, pos.z); if(block== Blocks.torch) { world.setBlock(pos.x, pos.y, pos.z, AdvancedRocketryBlocks.blockUnlitTorch); } else if(Configuration.torchBlocks.contains(block)) { EntityItem item = new EntityItem(world, pos.x, pos.y, pos.z, new ItemStack(block)); world.setBlockToAir(pos.x, pos.y, pos.z); world.spawnEntityInWorld(item); } } } } @Override public void clearBlob() { World world = blobHandler.getWorld(); runEffectOnWorldBlocks(world, getLocations()); super.clearBlob(); } public int getPressure() { return 100; } }