blusunrize.immersiveengineering.common.util.Utils.java Source code

Java tutorial

Introduction

Here is the source code for blusunrize.immersiveengineering.common.util.Utils.java

Source

/*
 * BluSunrize
 * Copyright (c) 2017
 *
 * This code is licensed under "Blu's License of Common Sense"
 * Details can be found in the license file in the root folder of this project
 */

package blusunrize.immersiveengineering.common.util;

import blusunrize.immersiveengineering.ImmersiveEngineering;
import blusunrize.immersiveengineering.api.ApiUtils;
import blusunrize.immersiveengineering.api.DirectionalBlockPos;
import blusunrize.immersiveengineering.api.Lib;
import blusunrize.immersiveengineering.api.crafting.IngredientStack;
import blusunrize.immersiveengineering.common.util.inventory.IIEInventory;
import com.google.common.base.Charsets;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.io.Resources;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonParseException;
import net.minecraft.advancements.Advancement;
import net.minecraft.advancements.AdvancementManager;
import net.minecraft.advancements.PlayerAdvancements;
import net.minecraft.block.*;
import net.minecraft.block.material.Material;
import net.minecraft.block.properties.IProperty;
import net.minecraft.block.state.BlockFaceShape;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.resources.I18n;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.monster.EntityMob;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.init.Biomes;
import net.minecraft.init.Blocks;
import net.minecraft.init.Items;
import net.minecraft.inventory.Container;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.InventoryCrafting;
import net.minecraft.item.Item;
import net.minecraft.item.ItemDye;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.CraftingManager;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.*;
import net.minecraft.util.EnumFacing.Axis;
import net.minecraft.util.math.*;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraft.world.WorldServer;
import net.minecraft.world.WorldType;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.storage.loot.*;
import net.minecraft.world.storage.loot.conditions.LootCondition;
import net.minecraft.world.storage.loot.conditions.LootConditionManager;
import net.minecraft.world.storage.loot.functions.LootFunction;
import net.minecraft.world.storage.loot.functions.LootFunctionManager;
import net.minecraftforge.common.property.IExtendedBlockState;
import net.minecraftforge.common.property.IUnlistedProperty;
import net.minecraftforge.common.util.Constants;
import net.minecraftforge.fluids.*;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandlerItem;
import net.minecraftforge.fluids.capability.IFluidTankProperties;
import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.ModContainer;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.ItemHandlerHelper;
import net.minecraftforge.oredict.OreDictionary;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.text.DecimalFormat;
import java.util.*;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Predicate;

import static java.lang.Math.min;

public class Utils {
    public static final Random RAND = new Random();

    public static boolean compareToOreName(ItemStack stack, String oreName) {
        if (!ApiUtils.isExistingOreName(oreName))
            return false;
        ItemStack comp = copyStackWithAmount(stack, 1);
        List<ItemStack> s = OreDictionary.getOres(oreName);
        for (ItemStack st : s)
            if (OreDictionary.itemMatches(st, comp, false))
                return true;
        return false;
    }

    public static boolean compareItemNBT(ItemStack stack1, ItemStack stack2) {
        if ((stack1.isEmpty()) != (stack2.isEmpty()))
            return false;
        boolean empty1 = (stack1.getTagCompound() == null || stack1.getTagCompound().isEmpty());
        boolean empty2 = (stack2.getTagCompound() == null || stack2.getTagCompound().isEmpty());
        if (empty1 != empty2)
            return false;
        if (!empty1 && !stack1.getTagCompound().equals(stack2.getTagCompound()))
            return false;
        return stack1.areCapsCompatible(stack2);
    }

    public static boolean canCombineArrays(ItemStack[] stacks, ItemStack[] target) {
        HashSet<IngredientStack> inputSet = new HashSet();
        for (ItemStack s : stacks)
            inputSet.add(new IngredientStack(s));
        for (ItemStack t : target) {
            int size = t.getCount();
            Iterator<IngredientStack> it = inputSet.iterator();
            while (it.hasNext()) {
                IngredientStack in = it.next();
                if (in.matchesItemStackIgnoringSize(t)) {
                    int taken = Math.min(size, in.inputSize);
                    size -= taken;
                    in.inputSize -= taken;
                    if (in.inputSize <= 0)
                        it.remove();
                    if (size <= 0)
                        break;
                }
            }
            if (size > 0)
                return false;
        }
        return true;
    }

    public static ItemStack copyStackWithAmount(ItemStack stack, int amount) {
        if (stack.isEmpty())
            return ItemStack.EMPTY;
        ItemStack s2 = stack.copy();
        s2.setCount(amount);
        return s2;
    }

    public static String[] dyeNames = { "Black", "Red", "Green", "Brown", "Blue", "Purple", "Cyan", "LightGray",
            "Gray", "Pink", "Lime", "Yellow", "LightBlue", "Magenta", "Orange", "White" };

    public static int getDye(ItemStack stack) {
        if (stack.isEmpty())
            return -1;
        if (stack.getItem().equals(Items.DYE))
            return stack.getItemDamage();
        for (int dye = 0; dye < dyeNames.length; dye++)
            if (compareToOreName(stack, "dye" + dyeNames[dye]))
                return dye;
        return -1;
    }

    public static boolean isDye(ItemStack stack) {
        if (stack.isEmpty())
            return false;
        if (stack.getItem().equals(Items.DYE))
            return true;
        for (int dye = 0; dye < dyeNames.length; dye++)
            if (compareToOreName(stack, "dye" + dyeNames[dye]))
                return true;
        return false;
    }

    public static FluidStack copyFluidStackWithAmount(FluidStack stack, int amount, boolean stripPressure) {
        if (stack == null)
            return null;
        FluidStack fs = new FluidStack(stack, amount);
        if (stripPressure && fs.tag != null && fs.tag.hasKey("pressurized")) {
            fs.tag.removeTag("pressurized");
            if (fs.tag.isEmpty())
                fs.tag = null;
        }
        return fs;
    }

    static long UUIDBase = 109406000905L;
    static long UUIDAdd = 01L;

    public static UUID generateNewUUID() {
        UUID uuid = new UUID(UUIDBase, UUIDAdd);
        UUIDAdd++;
        return uuid;
    }

    public static BlockPos toCC(Object object) {
        return ApiUtils.toBlockPos(object);
    }

    public static DirectionalBlockPos toDirCC(Object object, EnumFacing direction) {
        if (object instanceof BlockPos)
            return new DirectionalBlockPos((BlockPos) object, direction);
        if (object instanceof TileEntity)
            return new DirectionalBlockPos(((TileEntity) object).getPos(), direction);
        return null;
    }

    public static boolean isBlockAt(World world, BlockPos pos, Block b, int meta) {
        return blockstateMatches(world.getBlockState(pos), b, meta);
    }

    public static boolean blockstateMatches(IBlockState state, Block b, int meta) {
        if (state.getBlock().equals(b))
            return meta < 0 || meta == OreDictionary.WILDCARD_VALUE
                    || state.getBlock().getMetaFromState(state) == meta;
        return false;
    }

    public static boolean isOreBlockAt(World world, BlockPos pos, String oreName) {
        IBlockState state = world.getBlockState(pos);
        ItemStack stack = new ItemStack(state.getBlock(), 1, state.getBlock().getMetaFromState(state));
        return compareToOreName(stack, oreName);
    }

    public static boolean canFenceConnectTo(IBlockAccess world, BlockPos pos, EnumFacing facing,
            Material material) {
        BlockPos other = pos.offset(facing);
        IBlockState state = world.getBlockState(other);
        Block block = world.getBlockState(other).getBlock();
        if (block.canBeConnectedTo(world, other, facing.getOpposite()))
            return true;
        BlockFaceShape blockfaceshape = state.getBlockFaceShape(world, other, facing.getOpposite());
        boolean flag = blockfaceshape == BlockFaceShape.MIDDLE_POLE
                && (state.getMaterial() == material || block instanceof BlockFenceGate);
        return !isExceptBlockForAttachWithFence(block) && blockfaceshape == BlockFaceShape.SOLID || flag;
    }

    private static boolean isExceptionBlockForAttaching(Block block) {
        return block instanceof BlockShulkerBox || block instanceof BlockLeaves || block instanceof BlockTrapDoor
                || block == Blocks.BEACON || block == Blocks.CAULDRON || block == Blocks.GLASS
                || block == Blocks.GLOWSTONE || block == Blocks.ICE || block == Blocks.SEA_LANTERN
                || block == Blocks.STAINED_GLASS;
    }

    private static boolean isExceptBlockForAttachWithPiston(Block block) {
        return isExceptionBlockForAttaching(block) || block == Blocks.PISTON || block == Blocks.STICKY_PISTON
                || block == Blocks.PISTON_HEAD;
    }

    private static boolean isExceptBlockForAttachWithFence(Block block) {
        return isExceptBlockForAttachWithPiston(block) || block == Blocks.BARRIER || block == Blocks.MELON_BLOCK
                || block == Blocks.PUMPKIN || block == Blocks.LIT_PUMPKIN;
    }

    public static String formatDouble(double d, String s) {
        DecimalFormat df = new DecimalFormat(s);
        return df.format(d);
    }

    public static String toScientificNotation(int value, String decimalPrecision, int useKilo) {
        float formatted = value >= 1000000000 ? value / 1000000000f
                : value >= 1000000 ? value / 1000000f : value >= useKilo ? value / 1000f : value;
        String notation = value >= 1000000000 ? "G" : value >= 1000000 ? "M" : value >= useKilo ? "K" : "";
        return formatDouble(formatted, "0." + decimalPrecision) + notation;
    }

    public static String toCamelCase(String s) {
        return s.substring(0, 1).toUpperCase(Locale.ENGLISH) + s.substring(1).toLowerCase(Locale.ENGLISH);
    }

    static Method m_getHarvestLevel = null;

    public static String getHarvestLevelName(int lvl) {
        if (Loader.isModLoaded("TConstruct")) {
            try {
                if (m_getHarvestLevel == null) {
                    Class clazz = Class.forName("tconstruct.library.util");
                    if (clazz != null)
                        m_getHarvestLevel = clazz.getDeclaredMethod("getHarvestLevelName", int.class);
                }
                if (m_getHarvestLevel != null)
                    return (String) m_getHarvestLevel.invoke(null, lvl);
            } catch (Exception e) {
            }
        }
        return I18n.format(Lib.DESC_INFO + "mininglvl." + Math.max(-1, Math.min(lvl, 6)));
    }

    public static String getModVersion(String modid) {
        for (ModContainer container : Loader.instance().getActiveModList())
            if (container.getModId().equalsIgnoreCase(modid))
                return container.getVersion();
        return "";
    }

    private static final HashMap<String, String> MODNAME_LOOKUP = new HashMap<>();

    public static String getModName(String modid) {
        if (MODNAME_LOOKUP.containsKey(modid))
            return MODNAME_LOOKUP.get(modid);
        else {
            ModContainer modContainer = Loader.instance().getIndexedModList().get(modid);
            if (modContainer != null) {
                MODNAME_LOOKUP.put(modid, modContainer.getName());
                return modContainer.getName();
            }
            return "";
        }
    }

    public static <T> int findSequenceInList(List<T> list, T[] sequence, BiPredicate<T, T> predicate) {
        if (list.size() <= 0 || list.size() < sequence.length)
            return -1;

        for (int i = 0; i < list.size(); i++)
            if (predicate.test(sequence[0], list.get(i))) {
                boolean found = true;
                for (int j = 1; j < sequence.length; j++)
                    if (!(found = predicate.test(sequence[j], list.get(i + j))))
                        break;
                if (found)
                    return i;
            }
        return -1;
    }

    public static boolean tilePositionMatch(TileEntity tile0, TileEntity tile1) {
        return tile0.getPos().equals(tile1.getPos());
    }

    public static EnumFacing rotateFacingTowardsDir(EnumFacing f, EnumFacing dir) {
        if (dir == EnumFacing.NORTH)
            return f;
        else if (dir == EnumFacing.SOUTH && f.getAxis() != Axis.Y)
            return f.rotateY().rotateY();
        else if (dir == EnumFacing.WEST && f.getAxis() != Axis.Y)
            return f.rotateYCCW();
        else if (dir == EnumFacing.EAST && f.getAxis() != Axis.Y)
            return f.rotateY();
        else if (dir == EnumFacing.DOWN && f.getAxis() != Axis.Y)
            return f.rotateAround(Axis.X);
        else if (dir == EnumFacing.UP && f.getAxis() != Axis.X)
            return f.rotateAround(Axis.X).getOpposite();
        return f;
    }

    public static RayTraceResult getMovingObjectPositionFromPlayer(World world, EntityLivingBase living,
            boolean bool) {
        float f = 1.0F;
        float f1 = living.prevRotationPitch + (living.rotationPitch - living.prevRotationPitch) * f;
        float f2 = living.prevRotationYaw + (living.rotationYaw - living.prevRotationYaw) * f;
        double d0 = living.prevPosX + (living.posX - living.prevPosX) * (double) f;
        double d1 = living.prevPosY + (living.posY - living.prevPosY) * (double) f
                + (double) (world.isRemote ? living.getEyeHeight()
                        - (living instanceof EntityPlayer ? ((EntityPlayer) living).getDefaultEyeHeight() : 0)
                        : living.getEyeHeight()); // isRemote check to revert changes to ray trace position due to adding the eye height clientside and player yOffset differences
        double d2 = living.prevPosZ + (living.posZ - living.prevPosZ) * (double) f;
        Vec3d vec3 = new Vec3d(d0, d1, d2);
        float f3 = MathHelper.cos(-f2 * 0.017453292F - (float) Math.PI);
        float f4 = MathHelper.sin(-f2 * 0.017453292F - (float) Math.PI);
        float f5 = -MathHelper.cos(-f1 * 0.017453292F);
        float f6 = MathHelper.sin(-f1 * 0.017453292F);
        float f7 = f4 * f5;
        float f8 = f3 * f5;
        double d3 = 5.0D;
        if (living instanceof EntityPlayerMP)
            d3 = ((EntityPlayerMP) living).interactionManager.getBlockReachDistance();

        Vec3d vec31 = vec3.add((double) f7 * d3, (double) f6 * d3, (double) f8 * d3);
        return world.rayTraceBlocks(vec3, vec31, bool, !bool, false);
    }

    public static boolean canBlocksSeeOther(World world, BlockPos cc0, BlockPos cc1, Vec3d pos0, Vec3d pos1) {
        HashSet<BlockPos> inter = rayTrace(pos0, pos1, world);
        Iterator<BlockPos> it = inter.iterator();
        while (it.hasNext()) {
            BlockPos cc = it.next();
            if (!cc.equals(cc0) && !cc.equals(cc1))
                return false;
        }
        return true;
    }

    public static Vec3d getLivingFrontPos(EntityLivingBase entity, double offset, double height, EnumHandSide hand,
            boolean useSteppedYaw, float partialTicks) {
        double offsetX = hand == EnumHandSide.LEFT ? -.3125 : hand == EnumHandSide.RIGHT ? .3125 : 0;

        float yaw = entity.prevRotationYaw + (entity.rotationYaw - entity.prevRotationYaw) * partialTicks;
        if (useSteppedYaw)
            yaw = entity.prevRenderYawOffset + (entity.renderYawOffset - entity.prevRenderYawOffset) * partialTicks;
        float pitch = entity.prevRotationPitch + (entity.rotationPitch - entity.prevRotationPitch) * partialTicks;

        float yawCos = MathHelper.cos(-yaw * 0.017453292F - (float) Math.PI);
        float yawSin = MathHelper.sin(-yaw * 0.017453292F - (float) Math.PI);
        float pitchCos = -MathHelper.cos(-pitch * 0.017453292F);
        float pitchSin = MathHelper.sin(-pitch * 0.017453292F);

        return new Vec3d(entity.posX + offsetX * yawCos + offset * pitchCos * yawSin,
                entity.posY + offset * pitchSin + height,
                entity.posZ + offset * pitchCos * yawCos - offsetX * yawSin);
    }

    public static List<EntityLivingBase> getTargetsInCone(World world, Vec3d start, Vec3d dir, float spreadAngle,
            float truncationLength) {
        double length = dir.length();
        Vec3d dirNorm = dir.normalize();
        double radius = Math.tan(spreadAngle / 2) * length;

        Vec3d endLow = start.add(dir).subtract(radius, radius, radius);
        Vec3d endHigh = start.add(dir).add(radius, radius, radius);

        AxisAlignedBB box = new AxisAlignedBB(minInArray(start.x, endLow.x, endHigh.x),
                minInArray(start.y, endLow.y, endHigh.y), minInArray(start.z, endLow.z, endHigh.z),
                maxInArray(start.x, endLow.x, endHigh.x), maxInArray(start.y, endLow.y, endHigh.y),
                maxInArray(start.z, endLow.z, endHigh.z));

        List<EntityLivingBase> list = world.getEntitiesWithinAABB(EntityLivingBase.class, box);
        Iterator<EntityLivingBase> iterator = list.iterator();
        while (iterator.hasNext()) {
            EntityLivingBase e = iterator.next();
            if (!isPointInCone(start, dirNorm, radius, length, truncationLength,
                    e.getPositionVector().subtract(start)))
                iterator.remove();
        }
        return list;
    }

    public static boolean isPointInConeByAngle(Vec3d start, Vec3d normDirection, double aperture, double length,
            Vec3d relativePoint) {
        return isPointInCone(start, normDirection, Math.tan(aperture / 2) * length, length, 0, relativePoint);
    }

    public static boolean isPointInCone(Vec3d start, Vec3d normDirection, double radius, double length,
            Vec3d relativePoint) {
        return isPointInCone(start, normDirection, radius, length, 0, relativePoint);
    }

    public static boolean isPointInConeByAngle(Vec3d start, Vec3d normDirection, float aperture, double length,
            float truncationLength, Vec3d relativePoint) {
        return isPointInCone(start, normDirection, Math.tan(aperture / 2) * length, length, truncationLength,
                relativePoint);
    }

    /**
     * Checks if  point is contained within a cone in 3D space
     *
     * @param start            tip of the cone
     * @param normDirection    normalized (length==1) vector, direction of cone
     * @param radius           radius at the end of the cone
     * @param length           length of the cone
     * @param truncationLength optional lenght at which the cone is truncated (flat tip)
     * @param relativePoint    point to be checked, relative to {@code start}
     */
    public static boolean isPointInCone(Vec3d start, Vec3d normDirection, double radius, double length,
            float truncationLength, Vec3d relativePoint) {
        double projectedDist = relativePoint.dotProduct(normDirection); //Orthogonal projection, establishing point's distance on cone direction vector
        if (projectedDist < truncationLength || projectedDist > length) //If projected distance is before truncation or beyond length, point not contained
            return false;

        double radiusAtDist = projectedDist / length * radius; //Radius of the cone at the projected distance
        Vec3d orthVec = relativePoint.subtract(normDirection.scale(projectedDist)); //Orthogonal vector between point and cone direction

        return orthVec.lengthSquared() < (radiusAtDist * radiusAtDist); //Check if Vector's length is shorter than radius -> point in cone
    }

    public static boolean isPointInTriangle(Vec3d tA, Vec3d tB, Vec3d tC, Vec3d point) {
        //Distance vectors to A (focuspoint of triangle)
        Vec3d v0 = tC.subtract(tA);
        Vec3d v1 = tB.subtract(tA);
        Vec3d v2 = point.subtract(tA);

        return isPointInTriangle(v0, v1, v2);
    }

    private static boolean isPointInTriangle(Vec3d leg0, Vec3d leg1, Vec3d targetVec) {
        //Dot products
        double dot00 = leg0.dotProduct(leg0);
        double dot01 = leg0.dotProduct(leg1);
        double dot02 = leg0.dotProduct(targetVec);
        double dot11 = leg1.dotProduct(leg1);
        double dot12 = leg1.dotProduct(targetVec);

        //Barycentric coordinates
        double invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
        double u = (dot11 * dot02 - dot01 * dot12) * invDenom;
        double v = (dot00 * dot12 - dot01 * dot02) * invDenom;

        return (u >= 0) && (v >= 0) && (u + v < 1);
    }

    private static Vec3d getVectorForRotation(float pitch, float yaw) {
        float f = MathHelper.cos(-yaw * 0.017453292F - (float) Math.PI);
        float f1 = MathHelper.sin(-yaw * 0.017453292F - (float) Math.PI);
        float f2 = -MathHelper.cos(-pitch * 0.017453292F);
        float f3 = MathHelper.sin(-pitch * 0.017453292F);
        return new Vec3d((double) (f1 * f2), (double) f3, (double) (f * f2));
    }

    public static void attractEnemies(EntityLivingBase target, float radius) {
        attractEnemies(target, radius, null);
    }

    public static void attractEnemies(EntityLivingBase target, float radius, Predicate<EntityMob> predicate) {
        AxisAlignedBB aabb = new AxisAlignedBB(target.posX - radius, target.posY - radius, target.posZ - radius,
                target.posX + radius, target.posY + radius, target.posZ + radius);

        List<EntityMob> list = target.getEntityWorld().getEntitiesWithinAABB(EntityMob.class, aabb);
        for (EntityMob mob : list)
            if (predicate == null || predicate.test(mob)) {
                mob.setAttackTarget(target);
                mob.faceEntity(target, 180, 0);
            }
    }

    public static boolean isHammer(ItemStack stack) {
        if (stack.isEmpty())
            return false;
        return stack.getItem().getToolClasses(stack).contains(Lib.TOOL_HAMMER);
    }

    public static boolean isWirecutter(ItemStack stack) {
        if (stack.isEmpty())
            return false;
        return stack.getItem().getToolClasses(stack).contains(Lib.TOOL_WIRECUTTER);
    }

    public static boolean canBlockDamageSource(EntityLivingBase entity, DamageSource damageSourceIn) {
        if (!damageSourceIn.isUnblockable() && entity.isActiveItemStackBlocking()) {
            Vec3d vec3d = damageSourceIn.getDamageLocation();
            if (vec3d != null) {
                Vec3d vec3d1 = entity.getLook(1.0F);
                Vec3d vec3d2 = vec3d.subtractReverse(entity.getPositionVector()).normalize();
                vec3d2 = new Vec3d(vec3d2.x, 0.0D, vec3d2.z);
                return vec3d2.dotProduct(vec3d1) < 0;
            }
        }
        return false;
    }

    public static Vec3d getFlowVector(World world, BlockPos pos) {
        IBlockState state = world.getBlockState(pos);
        if (state.getBlock() instanceof BlockFluidBase)
            return ((BlockFluidBase) state.getBlock()).getFlowVector(world, pos);
        else if (!(state.getBlock() instanceof BlockLiquid))
            return new Vec3d(0, 0, 0);

        BlockLiquid block = (BlockLiquid) state.getBlock();
        Vec3d vec3 = new Vec3d(0.0D, 0.0D, 0.0D);
        Material mat = state.getMaterial();
        int i = getEffectiveFlowDecay(world, pos, mat);

        for (EnumFacing enumfacing : EnumFacing.Plane.HORIZONTAL) {
            BlockPos blockpos = pos.offset(enumfacing);
            int j = getEffectiveFlowDecay(world, blockpos, mat);
            if (j < 0) {
                if (!world.getBlockState(blockpos).getMaterial().blocksMovement()) {
                    j = getEffectiveFlowDecay(world, blockpos.down(), mat);
                    if (j >= 0) {
                        int k = j - (i - 8);
                        vec3 = vec3.add((double) ((blockpos.getX() - pos.getX()) * k),
                                (double) ((blockpos.getY() - pos.getY()) * k),
                                (double) ((blockpos.getZ() - pos.getZ()) * k));
                    }
                }
            } else if (j >= 0) {
                int l = j - i;
                vec3 = vec3.add((double) ((blockpos.getX() - pos.getX()) * l),
                        (double) ((blockpos.getY() - pos.getY()) * l),
                        (double) ((blockpos.getZ() - pos.getZ()) * l));
            }
        }

        if (state.getValue(BlockLiquid.LEVEL).intValue() >= 8) {
            for (EnumFacing enumfacing1 : EnumFacing.Plane.HORIZONTAL) {
                BlockPos blockpos1 = pos.offset(enumfacing1);
                if (block.causesDownwardCurrent(world, blockpos1, enumfacing1)
                        || block.causesDownwardCurrent(world, blockpos1.up(), enumfacing1)) {
                    vec3 = vec3.normalize().add(0.0D, -6.0D, 0.0D);
                    break;
                }
            }
        }
        return vec3.normalize();
    }

    static int getEffectiveFlowDecay(IBlockAccess world, BlockPos pos, Material mat) {
        IBlockState state = world.getBlockState(pos);
        if (state.getMaterial() != mat)
            return -1;
        int l = state.getBlock().getMetaFromState(state);
        if (l >= 8)
            l = 0;
        return l;
    }

    public static Vec3d addVectors(Vec3d vec0, Vec3d vec1) {
        return vec0.add(vec1.x, vec1.y, vec1.z);
    }

    public static double minInArray(double... f) {
        if (f.length < 1)
            return 0;
        double min = f[0];
        for (int i = 1; i < f.length; i++)
            min = Math.min(min, f[i]);
        return min;
    }

    public static double maxInArray(double... f) {
        if (f.length < 1)
            return 0;
        double max = f[0];
        for (int i = 1; i < f.length; i++)
            max = Math.max(max, f[i]);
        return max;
    }

    public static boolean isVecInEntityHead(EntityLivingBase entity, Vec3d vec) {
        if (entity.height / entity.width < 2)//Crude check to see if the entity is bipedal or at least upright (this should work for blazes)
            return false;
        double d = vec.y - (entity.posY + entity.getEyeHeight());
        return Math.abs(d) < .25;
    }

    public static void unlockIEAdvancement(EntityPlayer player, String name) {
        if (player instanceof EntityPlayerMP) {
            PlayerAdvancements advancements = ((EntityPlayerMP) player).getAdvancements();
            AdvancementManager manager = ((WorldServer) player.getEntityWorld()).getAdvancementManager();
            Advancement advancement = manager
                    .getAdvancement(new ResourceLocation(ImmersiveEngineering.MODID, name));
            if (advancement != null)
                advancements.grantCriterion(advancement, "code_trigger");
        }
    }

    public static NBTTagCompound getRandomFireworkExplosion(Random rand, int preType) {
        NBTTagCompound tag = new NBTTagCompound();
        NBTTagCompound expl = new NBTTagCompound();
        expl.setBoolean("Flicker", true);
        expl.setBoolean("Trail", true);
        int[] colors = new int[rand.nextInt(8) + 1];
        for (int i = 0; i < colors.length; i++) {
            int j = rand.nextInt(11) + 1;
            if (j > 2)
                j++;
            if (j > 6)
                j += 2;
            //no black, brown, light grey, grey or white
            colors[i] = ItemDye.DYE_COLORS[j];
        }
        expl.setIntArray("Colors", colors);
        int type = preType >= 0 ? preType : rand.nextInt(4);
        if (preType < 0 && type == 3)
            type = 4;
        expl.setByte("Type", (byte) type);
        NBTTagList list = new NBTTagList();
        list.appendTag(expl);
        tag.setTag("Explosions", list);

        return tag;
    }

    public static FluidStack drainFluidBlock(World world, BlockPos pos, boolean doDrain) {
        Block b = world.getBlockState(pos).getBlock();
        Fluid f = FluidRegistry.lookupFluidForBlock(b);

        if (f != null) {
            if (b instanceof IFluidBlock) {
                if (((IFluidBlock) b).canDrain(world, pos))
                    return ((IFluidBlock) b).drain(world, pos, doDrain);
                else
                    return null;
            } else {
                if (b.getMetaFromState(world.getBlockState(pos)) == 0) {
                    if (doDrain)
                        world.setBlockToAir(pos);
                    return new FluidStack(f, 1000);
                }
                return null;
            }
        }
        return null;
    }

    public static Fluid getRelatedFluid(World w, BlockPos pos) {
        Block b = w.getBlockState(pos).getBlock();
        return FluidRegistry.lookupFluidForBlock(b);
    }

    public static boolean placeFluidBlock(World world, BlockPos pos, FluidStack fluid) {
        if (fluid == null || fluid.getFluid() == null)
            return false;
        IBlockState state = world.getBlockState(pos);
        Block b = state.getBlock();
        Block fluidBlock = fluid.getFluid().getBlock();

        if (Blocks.WATER.equals(fluidBlock))
            fluidBlock = Blocks.FLOWING_WATER;
        else if (Blocks.LAVA.equals(fluidBlock))
            fluidBlock = Blocks.FLOWING_LAVA;

        boolean canPlace = b == null || b.isAir(state, world, pos) || b.isReplaceable(world, pos);

        if (fluidBlock != null && canPlace && fluid.amount >= 1000) {
            boolean placed = false;
            if ((fluidBlock instanceof BlockFluidBase)) {
                BlockFluidBase blockFluid = (BlockFluidBase) fluidBlock;
                placed = world.setBlockState(pos, fluidBlock.getStateFromMeta(blockFluid.getMaxRenderHeightMeta()));
            } else
                placed = world.setBlockState(pos, fluidBlock.getDefaultState());
            if (placed)
                fluid.amount -= 1000;
            return placed;
        }
        return false;
    }

    //   public static Collection<ItemStack> getContainersFilledWith(FluidStack fluidStack)
    //   {
    //      List<ItemStack> containers = new ArrayList();
    //      for (FluidContainerRegistry.FluidContainerData data : FluidContainerRegistry.getRegisteredFluidContainerData())
    //         if(data.fluid.containsFluid(fluidStack))
    //            containers.add(data.filledContainer);
    //      return containers;
    //   }

    //   public static String nameFromStack(ItemStack stack)
    //   {
    //      if(stack==null)
    //         return "";
    //      try
    //      {
    //         return GameData.getItemRegistry().getNameForObject(stack.getItem());
    //      }
    //      catch (NullPointerException e) {}
    //      return "";
    //   }

    public static IBlockState getStateFromItemStack(ItemStack stack) {
        if (stack.isEmpty())
            return null;
        Block block = getBlockFromItem(stack.getItem());
        if (block != null)
            return block.getStateFromMeta(stack.getItemDamage());
        return null;
    }

    public static Block getBlockFromItem(Item item) {
        if (item == Items.CAULDRON)
            return Blocks.CAULDRON;
        return Block.getBlockFromItem(item);
    }

    public static boolean canInsertStackIntoInventory(TileEntity inventory, ItemStack stack, EnumFacing side) {
        if (!stack.isEmpty() && inventory != null
                && inventory.hasCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, side)) {
            IItemHandler handler = inventory.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, side);
            ItemStack temp = ItemHandlerHelper.insertItem(handler, stack.copy(), true);
            return temp.isEmpty() || temp.getCount() < stack.getCount();
        }
        return false;
    }

    public static ItemStack insertStackIntoInventory(TileEntity inventory, ItemStack stack, EnumFacing side) {
        if (!stack.isEmpty() && inventory != null
                && inventory.hasCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, side)) {
            IItemHandler handler = inventory.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, side);
            ItemStack temp = ItemHandlerHelper.insertItem(handler, stack.copy(), true);
            if (temp.isEmpty() || temp.getCount() < stack.getCount())
                return ItemHandlerHelper.insertItem(handler, stack, false);
        }
        return stack;
    }

    public static ItemStack insertStackIntoInventory(TileEntity inventory, ItemStack stack, EnumFacing side,
            boolean simulate) {
        if (inventory != null && !stack.isEmpty()
                && inventory.hasCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, side)) {
            IItemHandler handler = inventory.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, side);
            return ItemHandlerHelper.insertItem(handler, stack.copy(), simulate);
        }
        return stack;
    }

    public static void dropStackAtPos(World world, BlockPos pos, ItemStack stack, EnumFacing facing) {
        if (!stack.isEmpty()) {
            EntityItem ei = new EntityItem(world, pos.getX() + .5, pos.getY() + .5, pos.getZ() + .5, stack.copy());
            ei.motionY = 0.025000000372529D;
            if (facing != null) {
                ei.motionX = (0.075F * facing.getXOffset());
                ei.motionZ = (0.075F * facing.getZOffset());
            }
            world.spawnEntity(ei);
        }
    }

    public static void dropStackAtPos(World world, BlockPos pos, ItemStack stack) {
        dropStackAtPos(world, pos, stack, null);
    }
    //   public static ItemStack insertStackIntoInventory(IInventory inventory, ItemStack stack, EnumFacing side)
    //   {
    //      if (stack == null || inventory == null)
    //         return null;
    //      int stackSize = stack.stackSize;
    //      if (inventory instanceof ISidedInventory)
    //      {
    //         ISidedInventory sidedInv = (ISidedInventory) inventory;
    //         int slots[] = sidedInv.getSlotsForFace(side);
    //         if (slots == null)
    //            return stack;
    //         for (int i=0; i<slots.length && stack!=null; i++)
    //         {
    //            if (sidedInv.canInsertItem(slots[i], stack, side))
    //            {
    //               ItemStack existingStack = inventory.getStackInSlot(slots[i]);
    //               if(OreDictionary.itemMatches(existingStack, stack, true)&&Utils.compareItemNBT(stack, existingStack))
    //                  stack = addToOccupiedSlot(sidedInv, slots[i], stack, existingStack);
    //            }
    //         }
    //         for (int i=0; i<slots.length && stack!=null; i++)
    //            if (inventory.getStackInSlot(slots[i]) == null && sidedInv.canInsertItem(slots[i], stack, side))
    //               stack = addToEmptyInventorySlot(sidedInv, slots[i], stack);
    //      }
    //      else
    //      {
    //         int invSize = inventory.getSizeInventory();
    //         for (int i=0; i<invSize && stack!=null; i++)
    //         {
    //            ItemStack existingStack = inventory.getStackInSlot(i);
    //            if (OreDictionary.itemMatches(existingStack, stack, true)&&Utils.compareItemNBT(stack, existingStack))
    //               stack = addToOccupiedSlot(inventory, i, stack, existingStack);
    //         }
    //         for (int i=0; i<invSize && stack!=null; i++)
    //            if (inventory.getStackInSlot(i) == null)
    //               stack = addToEmptyInventorySlot(inventory, i, stack);
    //      }
    //      if (stack == null || stack.stackSize != stackSize)
    //         inventory.markDirty();
    //      return stack;
    //   }

    public static ItemStack addToEmptyInventorySlot(IInventory inventory, int slot, ItemStack stack) {
        if (!inventory.isItemValidForSlot(slot, stack)) {
            return stack;
        }
        int stackLimit = inventory.getInventoryStackLimit();
        inventory.setInventorySlotContents(slot,
                copyStackWithAmount(stack, Math.min(stack.getCount(), stackLimit)));
        return stackLimit >= stack.getCount() ? ItemStack.EMPTY : stack.splitStack(stack.getCount() - stackLimit);
    }

    public static ItemStack addToOccupiedSlot(IInventory inventory, int slot, ItemStack stack,
            ItemStack existingStack) {
        int stackLimit = Math.min(inventory.getInventoryStackLimit(), stack.getMaxStackSize());
        if (stack.getCount() + existingStack.getCount() > stackLimit) {
            int stackDiff = stackLimit - existingStack.getCount();
            existingStack.setCount(stackLimit);
            stack.shrink(stackDiff);
            inventory.setInventorySlotContents(slot, existingStack);
            return stack;
        }
        existingStack.grow(min(stack.getCount(), stackLimit));
        inventory.setInventorySlotContents(slot, existingStack);
        return stackLimit >= stack.getCount() ? ItemStack.EMPTY : stack.splitStack(stack.getCount() - stackLimit);
    }

    //   public static boolean canInsertStackIntoInventory(IInventory inventory, ItemStack stack, EnumFacing side)
    //   {
    //      if(stack == null || inventory == null)
    //         return false;
    //      if(inventory instanceof ISidedInventory)
    //      {
    //         ISidedInventory sidedInv = (ISidedInventory) inventory;
    //         int slots[] = sidedInv.getSlotsForFace(side);
    //         if(slots == null)
    //            return false;
    //         for(int i=0; i<slots.length && stack!=null; i++)
    //         {
    //            if(sidedInv.canInsertItem(slots[i], stack, side) && sidedInv.isItemValidForSlot(slots[i], stack))
    //            {
    //               ItemStack existingStack = inventory.getStackInSlot(slots[i]);
    //               if(existingStack==null)
    //                  return true;
    //               else
    //                  if(OreDictionary.itemMatches(existingStack, stack, true)&&Utils.compareItemNBT(stack, existingStack))
    //                     if(existingStack.stackSize+stack.stackSize<inventory.getInventoryStackLimit() && existingStack.stackSize+stack.stackSize<existingStack.getMaxStackSize())
    //                        return true;
    //            }
    //         }
    //      }
    //      else
    //      {
    //         int invSize = inventory.getSizeInventory();
    //         for(int i=0; i<invSize && stack!=null; i++)
    //            if(inventory.isItemValidForSlot(i, stack))
    //            {
    //               ItemStack existingStack = inventory.getStackInSlot(i);
    //               if(existingStack==null)
    //                  return true;
    //               else
    //                  if(OreDictionary.itemMatches(existingStack, stack, true)&&Utils.compareItemNBT(stack, existingStack))
    //                     if(existingStack.stackSize+stack.stackSize<inventory.getInventoryStackLimit() && existingStack.stackSize+stack.stackSize<existingStack.getMaxStackSize())
    //                        return true;
    //            }
    //      }
    //      return false;
    //   }

    public static ItemStack fillFluidContainer(IFluidHandler handler, ItemStack containerIn, ItemStack containerOut,
            @Nullable EntityPlayer player) {
        if (containerIn == null || containerIn.isEmpty())
            return ItemStack.EMPTY;
        if (containerIn.hasTagCompound() && containerIn.getTagCompound().isEmpty())
            containerIn.setTagCompound(null);

        FluidActionResult result = FluidUtil.tryFillContainer(containerIn, handler, Integer.MAX_VALUE, player,
                false);
        if (result.isSuccess()) {
            final ItemStack full = result.getResult();
            if ((containerOut.isEmpty() || OreDictionary.itemMatches(containerOut, full, true))) {
                if (!containerOut.isEmpty()
                        && containerOut.getCount() + full.getCount() > containerOut.getMaxStackSize())
                    return ItemStack.EMPTY;
                result = FluidUtil.tryFillContainer(containerIn, handler, Integer.MAX_VALUE, player, true);
                if (result.isSuccess()) {
                    return result.getResult();
                }
            }
        }
        return ItemStack.EMPTY;
    }

    public static ItemStack drainFluidContainer(IFluidHandler handler, ItemStack containerIn,
            ItemStack containerOut, @Nullable EntityPlayer player) {
        if (containerIn == null || containerIn.isEmpty())
            return ItemStack.EMPTY;

        if (containerIn.hasTagCompound() && containerIn.getTagCompound().isEmpty())
            containerIn.setTagCompound(null);

        FluidActionResult result = FluidUtil.tryEmptyContainer(containerIn, handler, Integer.MAX_VALUE, player,
                false);
        if (result.isSuccess()) {
            ItemStack empty = result.getResult();
            if ((containerOut.isEmpty() || OreDictionary.itemMatches(containerOut, empty, true))) {
                if (!containerOut.isEmpty()
                        && containerOut.getCount() + empty.getCount() > containerOut.getMaxStackSize())
                    return ItemStack.EMPTY;
                result = FluidUtil.tryEmptyContainer(containerIn, handler, Integer.MAX_VALUE, player, true);
                if (result.isSuccess()) {
                    return result.getResult();
                }
            }
        }
        return ItemStack.EMPTY;

    }

    public static boolean isFluidContainerFull(ItemStack stack) {
        if (stack.isEmpty())
            return false;
        IFluidHandlerItem handler = FluidUtil.getFluidHandler(stack);
        if (handler == null)
            return false;
        IFluidTankProperties[] tank = handler.getTankProperties();
        for (IFluidTankProperties prop : tank)
            if (prop.getContents() == null || prop.getContents().amount < prop.getCapacity())
                return false;
        return true;
    }

    //   public static FluidStack getFluidFromItemStack(ItemStack stack)
    //   {
    //      if(stack==null)
    //         return null;
    //      FluidStack fluid = FluidContainerRegistry.getFluidForFilledItem(stack);
    //      if(fluid != null)
    //         return fluid;
    //      else if(stack.getItem() instanceof IFluidContainerItem)
    //         return ((IFluidContainerItem)stack.getItem()).getFluid(stack);
    //      return null;
    //   }

    public static boolean isFluidRelatedItemStack(ItemStack stack) {
        if (stack.isEmpty())
            return false;
        return stack.hasCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY, null);
    }

    public static IRecipe findRecipe(InventoryCrafting crafting, World world) {
        return CraftingManager.findMatchingRecipe(crafting, world);
    }

    public static NonNullList<ItemStack> createNonNullItemStackListFromArray(ItemStack[] stacks) {
        NonNullList<ItemStack> list = NonNullList.withSize(stacks.length, ItemStack.EMPTY);
        for (int i = 0; i < stacks.length; i++) {
            list.set(i, stacks[i]);
        }
        return list;
    }

    public static NonNullList<ItemStack> createNonNullItemStackListFromItemStack(ItemStack stack) {
        NonNullList<ItemStack> list = NonNullList.withSize(1, ItemStack.EMPTY);
        list.set(0, stack);
        return list;
    }

    public static float[] rotateToFacing(float[] in, EnumFacing facing) {
        for (int i = 0; i < in.length; i++)
            in[i] -= .5F;
        float[] ret = new float[in.length];
        for (int i = 0; i < in.length; i += 3)
            for (int j = 0; j < 3; j++) {
                if (j == 0)
                    ret[i + j] = in[i + 0] * facing.getZOffset() + in[i + 1] * facing.getXOffset()
                            + in[i + 2] * facing.getYOffset();
                else if (j == 1)
                    ret[i + j] = in[i + 0] * facing.getXOffset() + in[i + 1] * facing.getYOffset()
                            + in[i + 2] * facing.getZOffset();
                else
                    ret[i + j] = in[i + 0] * facing.getYOffset() + in[i + 1] * facing.getZOffset()
                            + in[i + 2] * facing.getXOffset();
            }
        for (int i = 0; i < in.length; i++)
            ret[i] += .5;
        return ret;
    }

    public static int hashBlockstate(IBlockState state, Set<Object> ignoredProperties, boolean includeExtended) {
        int val = 0;
        final int prime = 31;
        for (IProperty<?> n : state.getPropertyKeys())
            if (!ignoredProperties.contains(n)) {
                Object o = state.getValue(n);
                val = prime * val + (o == null ? 0 : o.hashCode());
            }
        if (includeExtended && state instanceof IExtendedBlockState) {
            IExtendedBlockState ext = (IExtendedBlockState) state;
            for (IUnlistedProperty<?> n : ext.getUnlistedNames())
                if (!ignoredProperties.contains(n)) {
                    Object o = ext.getValue(n);
                    val = prime * val + (o == null ? 0 : o.hashCode());
                }
        }
        return val;
    }

    public static boolean areStatesEqual(IBlockState state, IBlockState other, Set<Object> ignoredProperties,
            boolean includeExtended) {
        for (IProperty<?> i : state.getPropertyKeys()) {
            if (!other.getProperties().containsKey(i))
                return false;
            if (ignoredProperties.contains(i))
                continue;
            Object valThis = state.getValue(i);
            Object valOther = other.getValue(i);
            if (valThis == null && valOther == null)
                continue;
            else if (valOther == null || !valOther.equals(state.getValue(i)))
                return false;
        }
        if (includeExtended) {
            if (state instanceof IExtendedBlockState ^ other instanceof IExtendedBlockState)
                return false;
            if (state instanceof IExtendedBlockState) {
                IExtendedBlockState extState = (IExtendedBlockState) state;
                IExtendedBlockState extOther = (IExtendedBlockState) other;
                for (IUnlistedProperty<?> i : extState.getUnlistedNames()) {
                    if (!extOther.getUnlistedProperties().containsKey(i))
                        return false;
                    if (ignoredProperties.contains(i))
                        continue;
                    Object valThis = extState.getValue(i);
                    Object valOther = extOther.getValue(i);
                    if (valThis == null && valOther == null)
                        continue;
                    else if (valOther == null || !valOther.equals(valThis))
                        return false;
                }
            }
        }
        return true;
    }

    public static boolean areArraysEqualIncludingBlockstates(Object[] a, Object[] a2) {
        if (a == a2)
            return true;
        if (a == null || a2 == null)
            return false;

        int length = a.length;
        if (a2.length != length)
            return false;

        for (int i = 0; i < length; i++) {
            Object o1 = a[i];
            Object o2 = a2[i];
            if (o1 instanceof IBlockState && o2 instanceof IBlockState) {
                if (!areStatesEqual((IBlockState) o1, (IBlockState) o2, ImmutableSet.of(), false))
                    return false;
            } else if (!(o1 == null ? o2 == null : o1.equals(o2)))
                return false;
        }
        return true;
    }

    /* Reasoning for the formula for pos (below): pos should be the point on the catenary (horizontally) closest to the player position
    A conn start, B conn across, C player pos
    P:=A+tB are the points on the line, t in [0, 1]
    C-A=:D
    E:=|C-P| (Distance from the player to a point on the line)
    E**2=(Cx-Ax-tBx)**2+(Cy-Ay-tBy)**2+(Cz-Az-tBz)**2
    =(Dx-tBx)**2+(Dy-tBy)**2+(Dz-tBz)**2
    =Dx**2-2tDxBx+t**2Bx**2+Dy**2-2tDyBy+t**2By**2+Dz**2-2tDzBz+t**2Bz**2
    =t**2(Bx**2+By**2+Bz**2)-(2DxBx+2DyBy+2DzBz)t+Dz**2+Dy**2+Dx**2
        
    E**2'=(2Bx**2+2*By**2+2Bz**2)*t-2DxBx-2DyBy-2DzBz=0
    t=(DxBx+DyBy+DzBz)/(Bx**2+By**2+Bz**2)
     =D*B/|B|**2
     */
    public static double getCoeffForMinDistance(Vec3d point, Vec3d line, Vec3d across) {
        if (across.x == 0 && across.z == 0) {
            return (point.y - line.y) / across.y;
        } else {
            Vec3d delta = point.subtract(line);
            return delta.dotProduct(across) / across.lengthSquared();
        }
    }

    public static boolean isVecInBlock(Vec3d vec3d, BlockPos pos, BlockPos offset) {
        return vec3d.x >= pos.getX() - offset.getX() && vec3d.x <= pos.getX() - offset.getX() + 1
                && vec3d.y >= pos.getY() - offset.getY() && vec3d.y <= pos.getY() - offset.getY() + 1
                && vec3d.z >= pos.getZ() - offset.getZ() && vec3d.z <= pos.getZ() - offset.getZ() + 1;
    }

    public static class InventoryCraftingFalse extends InventoryCrafting {
        private static final Container nullContainer = new Container() {
            @Override
            public void onCraftMatrixChanged(IInventory paramIInventory) {
            }

            @Override
            public boolean canInteractWith(@Nonnull EntityPlayer p_75145_1_) {
                return false;
            }
        };

        public InventoryCraftingFalse(int w, int h) {
            super(nullContainer, w, h);
        }

        public static InventoryCrafting createFilledCraftingInventory(int w, int h, NonNullList<ItemStack> stacks) {
            InventoryCrafting invC = new Utils.InventoryCraftingFalse(w, h);
            for (int j = 0; j < w * h; j++)
                if (!stacks.get(j).isEmpty())
                    invC.setInventorySlotContents(j, stacks.get(j).copy());
            return invC;
        }
    }

    public static HashSet<BlockPos> rayTrace(Vec3d start, Vec3d end, World world) {
        return rayTrace(start, end, world, (p) -> {
        });
    }

    public static HashSet<BlockPos> rayTrace(Vec3d start, Vec3d end, World world, Consumer<BlockPos> out) {
        HashSet<BlockPos> ret = new HashSet<BlockPos>();
        HashSet<BlockPos> checked = new HashSet<BlockPos>();
        // x
        if (start.x > end.x) {
            Vec3d tmp = start;
            start = end;
            end = tmp;
        }
        double min = start.x;
        double dif = end.x - min;
        double lengthAdd = Math.ceil(min) - start.x;
        Vec3d mov = start.subtract(end);
        if (mov.x != 0) {
            mov = scalarProd(mov, 1 / mov.x);
            ray(dif, mov, start, lengthAdd, ret, world, checked, out);
        }
        // y
        if (mov.y != 0) {
            if (start.y > end.y) {
                Vec3d tmp = start;
                start = end;
                end = tmp;
            }
            min = start.y;
            dif = end.y - min;
            lengthAdd = Math.ceil(min) - start.y;
            mov = start.subtract(end);
            mov = scalarProd(mov, 1 / mov.y);

            ray(dif, mov, start, lengthAdd, ret, world, checked, out);
        }

        // z
        if (mov.z != 0) {
            if (start.z > end.z) {
                Vec3d tmp = start;
                start = end;
                end = tmp;
            }
            min = start.z;
            dif = end.z - min;
            lengthAdd = Math.ceil(min) - start.z;
            mov = start.subtract(end);
            mov = scalarProd(mov, 1 / mov.z);

            ray(dif, mov, start, lengthAdd, ret, world, checked, out);
        }
        if (checked.isEmpty()) {
            BlockPos pos = new BlockPos(start);
            IBlockState state = world.getBlockState(pos);
            Block b = state.getBlock();
            if (b.canCollideCheck(state, false) && state.collisionRayTrace(world, pos, start, end) != null)
                ret.add(pos);
            checked.add(pos);
            out.accept(pos);
        }
        return ret;
    }

    private static void ray(double dif, Vec3d mov, Vec3d start, double lengthAdd, HashSet<BlockPos> ret,
            World world, HashSet<BlockPos> checked, Consumer<BlockPos> out) {
        //Do NOT set this to true unless for debugging. Causes blocks to be placed along the traced ray
        boolean place = false;
        double standartOff = .0625;
        for (int i = 0; i < dif; i++) {
            Vec3d pos = addVectors(start, scalarProd(mov, i + lengthAdd + standartOff));
            Vec3d posNext = addVectors(start, scalarProd(mov, i + 1 + lengthAdd + standartOff));
            Vec3d posPrev = addVectors(start, scalarProd(mov, i + lengthAdd - standartOff));
            Vec3d posVeryPrev = addVectors(start, scalarProd(mov, i - 1 + lengthAdd - standartOff));

            BlockPos blockPos = new BlockPos((int) Math.floor(pos.x), (int) Math.floor(pos.y),
                    (int) Math.floor(pos.z));
            Block b;
            IBlockState state;
            if (!checked.contains(blockPos) && i + lengthAdd + standartOff < dif) {
                state = world.getBlockState(blockPos);
                b = state.getBlock();
                if (b.canCollideCheck(state, false)
                        && state.collisionRayTrace(world, blockPos, pos, posNext) != null)
                    ret.add(blockPos);
                //            if (place)
                //               world.setBlockState(blockPos, tmp);
                checked.add(blockPos);
                out.accept(blockPos);
            }
            blockPos = new BlockPos((int) Math.floor(posPrev.x), (int) Math.floor(posPrev.y),
                    (int) Math.floor(posPrev.z));
            if (!checked.contains(blockPos) && i + lengthAdd - standartOff < dif) {
                state = world.getBlockState(blockPos);
                b = state.getBlock();
                if (b.canCollideCheck(state, false)
                        && state.collisionRayTrace(world, blockPos, posVeryPrev, posPrev) != null)
                    ret.add(blockPos);
                //            if (place)
                //               world.setBlock(blockPos.posX, blockPos.posY, blockPos.posZ, tmp);
                checked.add(blockPos);
                out.accept(blockPos);
            }
        }
    }

    public static Vec3d scalarProd(Vec3d v, double s) {
        return new Vec3d(v.x * s, v.y * s, v.z * s);
    }

    public static BlockPos rayTraceForFirst(Vec3d start, Vec3d end, World w, Set<BlockPos> ignore) {
        HashSet<BlockPos> trace = rayTrace(start, end, w);
        for (BlockPos cc : ignore)
            trace.remove(cc);
        if (start.x != end.x)
            trace = findMinOrMax(trace, start.x > end.x, 0);
        if (start.y != end.y)
            trace = findMinOrMax(trace, start.y > end.y, 0);
        if (start.z != end.z)
            trace = findMinOrMax(trace, start.z > end.z, 0);
        if (trace.size() > 0) {
            BlockPos ret = trace.iterator().next();
            return ret;
        }
        return null;
    }

    public static HashSet<BlockPos> findMinOrMax(HashSet<BlockPos> in, boolean max, int coord) {
        HashSet<BlockPos> ret = new HashSet<BlockPos>();
        int currMinMax = max ? Integer.MIN_VALUE : Integer.MAX_VALUE;
        //find minimum
        for (BlockPos cc : in) {
            int curr = (coord == 0 ? cc.getX() : (coord == 1 ? cc.getY() : cc.getY()));
            if (max ^ (curr < currMinMax))
                currMinMax = curr;
        }
        //fill ret set
        for (BlockPos cc : in) {
            int curr = (coord == 0 ? cc.getX() : (coord == 1 ? cc.getY() : cc.getZ()));
            if (curr == currMinMax)
                ret.add(cc);
        }
        return ret;
    }

    /**
     * get tile entity without loading currently unloaded chunks
     *
     * @return return value of {@link IBlockAccess#getTileEntity(BlockPos)} or always null if chunk is not loaded
     */
    public static TileEntity getExistingTileEntity(World world, BlockPos pos) {
        if (world.isBlockLoaded(pos))
            return world.getTileEntity(pos);
        return null;
    }

    public static NonNullList<ItemStack> readInventory(NBTTagList nbt, int size) {
        NonNullList<ItemStack> inv = NonNullList.withSize(size, ItemStack.EMPTY);
        int max = nbt.tagCount();
        for (int i = 0; i < max; i++) {
            NBTTagCompound itemTag = nbt.getCompoundTagAt(i);
            int slot = itemTag.getByte("Slot") & 255;
            if (slot >= 0 && slot < size)
                inv.set(slot, new ItemStack(itemTag));
        }
        return inv;
    }

    public static NBTTagList writeInventory(ItemStack[] inv) {
        NBTTagList invList = new NBTTagList();
        for (int i = 0; i < inv.length; i++)
            if (!inv[i].isEmpty()) {
                NBTTagCompound itemTag = new NBTTagCompound();
                itemTag.setByte("Slot", (byte) i);
                inv[i].writeToNBT(itemTag);
                invList.appendTag(itemTag);
            }
        return invList;
    }

    public static NBTTagList writeInventory(Collection<ItemStack> inv) {
        NBTTagList invList = new NBTTagList();
        byte slot = 0;
        for (ItemStack s : inv) {
            if (!s.isEmpty()) {
                NBTTagCompound itemTag = new NBTTagCompound();
                itemTag.setByte("Slot", slot);
                s.writeToNBT(itemTag);
                invList.appendTag(itemTag);
            }
            slot++;
        }
        return invList;
    }

    public static NonNullList<ItemStack> loadItemStacksFromNBT(NBTBase nbt) {
        NonNullList<ItemStack> itemStacks = NonNullList.create();
        if (nbt instanceof NBTTagCompound) {
            ItemStack stack = new ItemStack((NBTTagCompound) nbt);
            itemStacks.add(stack);
            return itemStacks;
        } else if (nbt instanceof NBTTagList) {
            NBTTagList list = (NBTTagList) nbt;
            return readInventory(list, list.tagCount());
        }
        return itemStacks;
    }

    public static void modifyInvStackSize(NonNullList<ItemStack> inv, int slot, int amount) {
        if (slot >= 0 && slot < inv.size() && !inv.get(slot).isEmpty()) {
            inv.get(slot).grow(amount);
            if (inv.get(slot).getCount() <= 0)
                inv.set(slot, ItemStack.EMPTY);
        }
    }

    public static void shuffleLootItems(List<ItemStack> stacks, int slotAmount, Random rand) {
        List<ItemStack> list = Lists.newArrayList();
        Iterator<ItemStack> iterator = stacks.iterator();
        while (iterator.hasNext()) {
            ItemStack itemstack = iterator.next();
            if (itemstack.getCount() <= 0)
                iterator.remove();
            else if (itemstack.getCount() > 1) {
                list.add(itemstack);
                iterator.remove();
            }
        }
        slotAmount = slotAmount - stacks.size();
        while (slotAmount > 0 && list.size() > 0) {
            ItemStack itemstack2 = list.remove(MathHelper.getInt(rand, 0, list.size() - 1));
            int i = MathHelper.getInt(rand, 1, itemstack2.getCount() / 2);
            itemstack2.shrink(i);
            ItemStack itemstack1 = itemstack2.copy();
            itemstack1.setCount(i);

            if (itemstack2.getCount() > 1 && rand.nextBoolean())
                list.add(itemstack2);
            else
                stacks.add(itemstack2);

            if (itemstack1.getCount() > 1 && rand.nextBoolean())
                list.add(itemstack1);
            else
                stacks.add(itemstack1);
        }
        stacks.addAll(list);
        Collections.shuffle(stacks, rand);
    }

    private static final Gson GSON_INSTANCE = (new GsonBuilder())
            .registerTypeAdapter(RandomValueRange.class, new RandomValueRange.Serializer())
            .registerTypeAdapter(LootPool.class, new LootPool.Serializer())
            .registerTypeAdapter(LootTable.class, new LootTable.Serializer())
            .registerTypeHierarchyAdapter(LootEntry.class, new LootEntry.Serializer())
            .registerTypeHierarchyAdapter(LootFunction.class, new LootFunctionManager.Serializer())
            .registerTypeHierarchyAdapter(LootCondition.class, new LootConditionManager.Serializer())
            .registerTypeHierarchyAdapter(LootContext.EntityTarget.class, new LootContext.EntityTarget.Serializer())
            .create();

    public static LootTable loadBuiltinLootTable(ResourceLocation resource, LootTableManager lootTableManager) {
        URL url = Utils.class
                .getResource("/assets/" + resource.getNamespace() + "/loot_tables/" + resource.getPath() + ".json");
        if (url == null)
            return LootTable.EMPTY_LOOT_TABLE;
        else {
            String s;
            try {
                s = Resources.toString(url, Charsets.UTF_8);
            } catch (IOException ioexception) {
                //            IELogger.warn(("Failed to load loot table " + resource.toString() + " from " + url.toString()));
                ioexception.printStackTrace();
                return LootTable.EMPTY_LOOT_TABLE;
            }

            try {
                return net.minecraftforge.common.ForgeHooks.loadLootTable(GSON_INSTANCE, resource, s, false,
                        lootTableManager);
            } catch (JsonParseException jsonparseexception) {
                //            IELogger.error(("Failed to load loot table " + resource.toString() + " from " + url.toString()));
                jsonparseexception.printStackTrace();
                return LootTable.EMPTY_LOOT_TABLE;
            }
        }
    }

    public static int calcRedstoneFromInventory(IIEInventory inv) {
        if (inv == null)
            return 0;
        else {
            int max = inv.getComparatedSize();
            int i = 0;
            float f = 0.0F;
            for (int j = 0; j < max; ++j) {
                ItemStack itemstack = inv.getInventory().get(j);
                if (!itemstack.isEmpty()) {
                    f += (float) itemstack.getCount()
                            / (float) Math.min(inv.getSlotLimit(j), itemstack.getMaxStackSize());
                    ++i;
                }
            }
            f = f / (float) max;
            return MathHelper.floor(f * 14.0F) + (i > 0 ? 1 : 0);
        }
    }

    public static Map<String, Object> saveStack(ItemStack stack) {
        HashMap<String, Object> ret = new HashMap<>();
        if (!stack.isEmpty()) {
            ret.put("size", stack.getCount());
            ret.put("name", Item.REGISTRY.getNameForObject(stack.getItem()));
            ret.put("nameUnlocalized", stack.getTranslationKey());
            ret.put("label", stack.getDisplayName());
            ret.put("damage", stack.getItemDamage());
            ret.put("maxDamage", stack.getMaxDamage());
            ret.put("maxSize", stack.getMaxStackSize());
            ret.put("hasTag", stack.hasTagCompound());
        }
        return ret;
    }

    public static Map<String, Object> saveFluidTank(FluidTank tank) {
        HashMap<String, Object> ret = new HashMap<>();
        if (tank != null && tank.getFluid() != null) {
            ret.put("name", tank.getFluid().getFluid().getUnlocalizedName());
            ret.put("amount", tank.getFluidAmount());
            ret.put("capacity", tank.getCapacity());
            ret.put("hasTag", tank.getFluid().tag != null);
        }
        return ret;
    }

    public static Map<String, Object> saveFluidStack(FluidStack tank) {
        HashMap<String, Object> ret = new HashMap<>();
        if (tank != null && tank.getFluid() != null) {
            ret.put("name", tank.getFluid().getUnlocalizedName());
            ret.put("amount", tank.amount);
            ret.put("hasTag", tank.tag != null);
        }
        return ret;
    }

    public static void stateToNBT(NBTTagCompound out, IBlockState state) {
        out.setString("block", state.getBlock().getRegistryName().toString());
        for (IProperty<?> prop : state.getPropertyKeys())
            saveProp(state, prop, out);
    }

    public static IBlockState stateFromNBT(NBTTagCompound in) {
        Block b = Block.getBlockFromName(in.getString("block"));
        if (b == null)
            return Blocks.BOOKSHELF.getDefaultState();
        IBlockState ret = b.getDefaultState();
        for (IProperty<?> prop : ret.getPropertyKeys()) {
            String name = prop.getName();
            if (in.hasKey(name, Constants.NBT.TAG_STRING))
                ret = setProp(ret, prop, in.getString(name));
        }
        return ret;
    }

    public static NonNullList<ItemStack> getDrops(IBlockState state) {
        IBlockAccess w = getSingleBlockWorldAccess(state);
        NonNullList<ItemStack> ret = NonNullList.create();
        state.getBlock().getDrops(ret, w, BlockPos.ORIGIN, state, 0);
        return ret;
    }

    private static <T extends Comparable<T>> void saveProp(IBlockState state, IProperty<T> prop,
            NBTTagCompound out) {
        out.setString(prop.getName(), prop.getName(state.getValue(prop)));
    }

    private static <T extends Comparable<T>> IBlockState setProp(IBlockState state, IProperty<T> prop,
            String value) {
        Optional<T> valueParsed = prop.parseValue(value);
        if (valueParsed.isPresent())
            return state.withProperty(prop, valueParsed.get());
        return state;
    }

    public static AxisAlignedBB transformAABB(AxisAlignedBB original, EnumFacing facing) {
        double minX = 0, minZ = 0, maxX = 0, maxZ = 0;
        EnumFacing right = facing.rotateY();
        switch (facing) {
        case NORTH:
            minZ = original.minZ;
            maxZ = original.maxZ;
            break;
        case SOUTH:
            minZ = 1 - original.minZ;
            maxZ = 1 - original.maxZ;
            break;
        case WEST:
            minX = original.minZ;
            maxX = original.maxZ;
            break;
        case EAST:
            minX = 1 - original.minZ;
            maxX = 1 - original.maxZ;
            break;
        }
        switch (right) {
        case EAST:
            minX = original.minX;
            maxX = original.maxX;
            break;
        case WEST:
            minX = 1 - original.minX;
            maxX = 1 - original.maxX;
            break;
        case SOUTH:
            minZ = 1 - original.minX;
            maxZ = 1 - original.maxX;
            break;
        case NORTH:
            minZ = original.minX;
            maxZ = original.maxX;
            break;
        }
        return new AxisAlignedBB(minX, original.minY, minZ, maxX, original.maxY, maxZ);
    }

    public static IBlockAccess getSingleBlockWorldAccess(IBlockState state) {
        return new SingleBlockAcess(state);
    }

    private static class SingleBlockAcess implements IBlockAccess {
        IBlockState state;

        public SingleBlockAcess(IBlockState state) {
            this.state = state;
        }

        @Nullable
        @Override
        public TileEntity getTileEntity(@Nonnull BlockPos pos) {
            return null;
        }

        @Override
        public int getCombinedLight(@Nonnull BlockPos pos, int lightValue) {
            return 0;
        }

        @Nonnull
        @Override
        public IBlockState getBlockState(@Nonnull BlockPos pos) {
            return pos.equals(BlockPos.ORIGIN) ? state : Blocks.AIR.getDefaultState();
        }

        @Override
        public boolean isAirBlock(@Nonnull BlockPos pos) {
            return !pos.equals(BlockPos.ORIGIN);
        }

        @Nonnull
        @Override
        public Biome getBiome(@Nonnull BlockPos pos) {
            return Biomes.MUSHROOM_ISLAND;
        }

        @Override
        public int getStrongPower(@Nonnull BlockPos pos, @Nonnull EnumFacing direction) {
            return 0;
        }

        @Nonnull
        @Override
        public WorldType getWorldType() {
            return WorldType.DEFAULT;
        }

        @Override
        public boolean isSideSolid(@Nonnull BlockPos pos, @Nonnull EnumFacing side, boolean _default) {
            return pos.equals(BlockPos.ORIGIN) && state.isSideSolid(this, BlockPos.ORIGIN, side);
        }
    }
}