blusunrize.immersiveengineering.api.ApiUtils.java Source code

Java tutorial

Introduction

Here is the source code for blusunrize.immersiveengineering.api.ApiUtils.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.api;

import blusunrize.immersiveengineering.ImmersiveEngineering;
import blusunrize.immersiveengineering.api.crafting.IngredientStack;
import blusunrize.immersiveengineering.api.energy.wires.*;
import blusunrize.immersiveengineering.api.energy.wires.ImmersiveNetHandler.Connection;
import blusunrize.immersiveengineering.common.EventHandler;
import blusunrize.immersiveengineering.common.IESaveData;
import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IGeneralMultiblock;
import blusunrize.immersiveengineering.common.util.ItemNBTHelper;
import blusunrize.immersiveengineering.common.util.Utils;
import blusunrize.immersiveengineering.common.util.chickenbones.Matrix4;
import blusunrize.immersiveengineering.common.util.network.MessageObstructedConnection;
import com.google.common.util.concurrent.AtomicDouble;
import com.google.common.util.concurrent.ListenableFutureTask;
import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.texture.TextureMap;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.client.renderer.vertex.VertexFormatElement;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.Ingredient;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagInt;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.*;
import net.minecraft.util.math.*;
import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.world.World;
import net.minecraftforge.client.model.pipeline.IVertexConsumer;
import net.minecraftforge.client.model.pipeline.UnpackedBakedQuad;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.FluidUtil;
import net.minecraftforge.fml.common.network.NetworkRegistry;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.ItemHandlerHelper;
import net.minecraftforge.oredict.OreDictionary;
import org.apache.commons.lang3.tuple.ImmutableTriple;
import org.apache.commons.lang3.tuple.Triple;
import org.lwjgl.util.vector.Vector3f;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;

import static blusunrize.immersiveengineering.api.energy.wires.ImmersiveNetHandler.Connection.vertices;

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

    public static boolean stackMatchesObject(ItemStack stack, Object o) {
        return stackMatchesObject(stack, o, false);
    }

    public static boolean stackMatchesObject(ItemStack stack, Object o, boolean checkNBT) {
        if (o instanceof ItemStack)
            return OreDictionary.itemMatches((ItemStack) o, stack, false)
                    && (!checkNBT || ((ItemStack) o).getItemDamage() == OreDictionary.WILDCARD_VALUE
                            || Utils.compareItemNBT((ItemStack) o, stack));
        else if (o instanceof Collection) {
            for (Object io : (Collection) o)
                if (io instanceof ItemStack && OreDictionary.itemMatches((ItemStack) io, stack, false)
                        && (!checkNBT || ((ItemStack) io).getItemDamage() == OreDictionary.WILDCARD_VALUE
                                || Utils.compareItemNBT((ItemStack) io, stack)))
                    return true;
        } else if (o instanceof IngredientStack)
            return ((IngredientStack) o).matchesItemStack(stack);
        else if (o instanceof ItemStack[]) {
            for (ItemStack io : (ItemStack[]) o)
                if (OreDictionary.itemMatches(io, stack, false) && (!checkNBT
                        || io.getItemDamage() == OreDictionary.WILDCARD_VALUE || Utils.compareItemNBT(io, stack)))
                    return true;
        } else if (o instanceof FluidStack) {
            FluidStack fs = FluidUtil.getFluidContained(stack);
            return fs != null && fs.containsFluid((FluidStack) o);
        } else if (o instanceof String)
            return compareToOreName(stack, (String) o);
        return false;
    }

    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 boolean stacksMatchIngredientList(List<IngredientStack> list, NonNullList<ItemStack> stacks) {
        ArrayList<ItemStack> queryList = new ArrayList<ItemStack>(stacks.size());
        for (ItemStack s : stacks)
            if (!s.isEmpty())
                queryList.add(s.copy());

        for (IngredientStack ingr : list)
            if (ingr != null) {
                int amount = ingr.inputSize;
                Iterator<ItemStack> it = queryList.iterator();
                while (it.hasNext()) {
                    ItemStack query = it.next();
                    if (!query.isEmpty()) {
                        if (ingr.matchesItemStackIgnoringSize(query)) {
                            if (query.getCount() > amount) {
                                query.shrink(amount);
                                amount = 0;
                            } else {
                                amount -= query.getCount();
                                query.setCount(0);
                            }
                        }
                        if (query.getCount() <= 0)
                            it.remove();
                        if (amount <= 0)
                            break;
                    }
                }
                if (amount > 0)
                    return false;
            }
        return true;
    }

    public static Ingredient createIngredientFromList(List<ItemStack> list) {
        return Ingredient.fromStacks(list.toArray(new ItemStack[list.size()]));
    }

    @Deprecated
    public static ComparableItemStack createComparableItemStack(ItemStack stack) {
        return createComparableItemStack(stack, true);
    }

    public static ComparableItemStack createComparableItemStack(ItemStack stack, boolean copy) {
        return createComparableItemStack(stack, copy, stack.hasTagCompound() && !stack.getTagCompound().isEmpty());
    }

    public static ComparableItemStack createComparableItemStack(ItemStack stack, boolean copy, boolean useNbt) {
        ComparableItemStack comp = new ComparableItemStack(stack, true, copy);
        comp.setUseNBT(useNbt);
        return comp;
    }

    public static boolean isExistingOreName(String name) {
        if (!OreDictionary.doesOreNameExist(name))
            return false;
        else
            return !OreDictionary.getOres(name).isEmpty();
    }

    public static boolean isMetalComponent(ItemStack stack, String componentType) {
        return getMetalComponentType(stack, componentType) != null;
    }

    public static String getMetalComponentType(ItemStack stack, String... componentTypes) {
        int[] ids = OreDictionary.getOreIDs(stack);
        String[] oreNames = OreDictionary.getOreNames();
        for (int id : ids) {
            String oreName = oreNames[id];
            for (String componentType : componentTypes)
                if (oreName.startsWith(componentType))
                    return componentType;
        }
        return null;
    }

    public static String[] getMetalComponentTypeAndMetal(ItemStack stack, String... componentTypes) {
        int[] ids = OreDictionary.getOreIDs(stack);
        String[] oreNames = OreDictionary.getOreNames();
        for (int id : ids) {
            String oreName = oreNames[id];
            for (String componentType : componentTypes) {
                if (oreName.startsWith(componentType))
                    return new String[] { componentType, oreName.substring(componentType.length()) };
            }
        }
        return null;
    }

    public static boolean isIngot(ItemStack stack) {
        return isMetalComponent(stack, "ingot");
    }

    public static boolean isPlate(ItemStack stack) {
        return isMetalComponent(stack, "plate");
    }

    public static int getComponentIngotWorth(ItemStack stack) {
        String[] keys = IEApi.prefixToIngotMap.keySet().toArray(new String[IEApi.prefixToIngotMap.size()]);
        String key = getMetalComponentType(stack, keys);
        if (key != null) {
            Integer[] relation = IEApi.prefixToIngotMap.get(key);
            if (relation != null && relation.length > 1) {
                double val = relation[0] / (double) relation[1];
                return (int) val;
            }
        }
        return 0;
    }

    public static ItemStack breakStackIntoIngots(ItemStack stack) {
        String[] keys = IEApi.prefixToIngotMap.keySet().toArray(new String[IEApi.prefixToIngotMap.size()]);
        String[] type = getMetalComponentTypeAndMetal(stack, keys);
        if (type != null) {
            Integer[] relation = IEApi.prefixToIngotMap.get(type[0]);
            if (relation != null && relation.length > 1) {
                double val = relation[0] / (double) relation[1];
                return copyStackWithAmount(IEApi.getPreferredOreStack("ingot" + type[1]), (int) val);
            }
        }
        return ItemStack.EMPTY;
    }

    public static Object[] breakStackIntoPreciseIngots(ItemStack stack) {
        String[] keys = IEApi.prefixToIngotMap.keySet().toArray(new String[IEApi.prefixToIngotMap.size()]);
        String[] type = getMetalComponentTypeAndMetal(stack, keys);
        if (type != null) {
            Integer[] relation = IEApi.prefixToIngotMap.get(type[0]);
            if (relation != null && relation.length > 1) {
                double val = relation[0] / (double) relation[1];
                return new Object[] { IEApi.getPreferredOreStack("ingot" + type[1]), val };
            }
        }
        return null;
    }

    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 BlockPos toBlockPos(Object object) {
        if (object instanceof BlockPos)
            return (BlockPos) object;
        if (object instanceof TileEntity)
            return ((TileEntity) object).getPos();
        if (object instanceof IICProxy)
            return ((IICProxy) object).getPos();
        return null;
    }

    public static IImmersiveConnectable toIIC(Object object, World world) {
        return toIIC(object, world, true);
    }

    public static IImmersiveConnectable toIIC(Object object, World world, boolean allowProxies) {
        if (object instanceof IImmersiveConnectable)
            return (IImmersiveConnectable) object;
        else if (object instanceof BlockPos) {
            if (world != null && world.isBlockLoaded((BlockPos) object)) {
                TileEntity te = world.getTileEntity((BlockPos) object);
                if (te instanceof IImmersiveConnectable)
                    return (IImmersiveConnectable) te;
            }
            if (allowProxies) {
                DimensionBlockPos pos = new DimensionBlockPos((BlockPos) object, world);
                if (ImmersiveNetHandler.INSTANCE.proxies.containsKey(pos))
                    return ImmersiveNetHandler.INSTANCE.proxies.get(pos);
            }
        }
        return null;
    }

    public static Vec3d getVecForIICAt(World world, BlockPos pos, Connection conn) {
        Vec3d offset = Vec3d.ZERO;
        //Force loading
        IImmersiveConnectable iicPos = toIIC(pos, world, false);
        if (iicPos != null)
            offset = iicPos.getConnectionOffset(conn);
        if (pos.equals(conn.end))
            offset = offset.add(conn.end.getX() - conn.start.getX(), conn.end.getY() - conn.start.getY(),
                    conn.end.getZ() - conn.start.getZ());
        return offset;
    }

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

    public static double acosh(double x) {
        //See http://mathworld.wolfram.com/InverseHyperbolicCosine.html
        return Math.log(x + Math.sqrt(x + 1) * Math.sqrt(x - 1));
    }

    public static Vec3d[] getConnectionCatenary(Connection connection, Vec3d start, Vec3d end) {
        boolean vertical = end.x == start.x && end.z == start.z;
        connection.across = end.subtract(start);

        if (vertical) {
            Vec3d[] ret = new Vec3d[vertices + 1];
            double height = end.y - start.y;
            connection.horizontalLength = Math.abs(height);
            for (int i = 0; i < vertices + 1; i++)
                ret[i] = new Vec3d(start.x, start.y + i * height / vertices, start.z);
            return ret;
        }

        return getConnectionCatenary(start, end, connection.cableType.getSlack(), connection);
    }

    public static Vec3d[] getConnectionCatenary(Vec3d start, Vec3d end, double slack) {
        return getConnectionCatenary(start, end, slack, null);
    }

    public static Vec3d[] getConnectionCatenary(Vec3d start, Vec3d end, double slack, @Nullable Connection c) {
        double dx = (end.x) - (start.x);
        double dy = (end.y) - (start.y);
        double dz = (end.z) - (start.z);
        double dw = Math.sqrt(dx * dx + dz * dz);
        double k = Math.sqrt(dx * dx + dy * dy + dz * dz) * slack;
        double l = 0;
        int limiter = 0;
        while (limiter < 300) {
            limiter++;
            l += 0.01;
            if (Math.sinh(l) / l >= Math.sqrt(k * k - dy * dy) / dw)
                break;
        }
        double a = dw / 2 / l;
        double offsetX = (0 + dw - a * Math.log((k + dy) / (k - dy))) * 0.5;
        double offsetY = (dy + 0 - k * Math.cosh(l) / Math.sinh(l)) * 0.5;
        if (c != null) {
            c.catOffsetX = offsetX;
            c.catOffsetY = offsetY;
            c.catA = a;
            c.horizontalLength = dw;
        }

        Vec3d[] vex = new Vec3d[vertices + 1];

        vex[0] = new Vec3d(start.x, start.y, start.z);
        for (int i = 1; i < vertices; i++) {
            float posRelative = i / (float) vertices;
            double x = 0 + dx * posRelative;
            double z = 0 + dz * posRelative;
            double y = a * Math.cosh((dw * posRelative - offsetX) / a) + offsetY;
            vex[i] = new Vec3d(start.x + x, start.y + y, start.z + z);
        }
        vex[vertices] = new Vec3d(end.x, end.y, end.z);

        return vex;
    }

    public static double getDim(Vec3d vec, int dim) {
        return dim == 0 ? vec.x : (dim == 1 ? vec.y : vec.z);
    }

    public static BlockPos offsetDim(BlockPos p, int dim, int amount) {
        return p.add(dim == 0 ? amount : 0, dim == 1 ? amount : 0, dim == 2 ? amount : 0);
    }

    public static Vec3d offsetDim(Vec3d p, int dim, double amount) {
        return p.add(dim == 0 ? amount : 0, dim == 1 ? amount : 0, dim == 2 ? amount : 0);
    }

    public static boolean raytraceAlongCatenary(Connection conn, World w,
            Predicate<Triple<BlockPos, Vec3d, Vec3d>> shouldStop, Consumer<Triple<BlockPos, Vec3d, Vec3d>> close) {
        Vec3d vStart = getVecForIICAt(w, conn.start, conn);
        Vec3d vEnd = getVecForIICAt(w, conn.end, conn);
        return raytraceAlongCatenaryRelative(conn, shouldStop, close, vStart, vEnd);
    }

    /**
     * Use raytraceAlongCatenaryRelative instead, pass vStart and vEnd relative to the start of the connection
     */
    @Deprecated
    public static boolean raytraceAlongCatenary(Connection conn,
            Predicate<Triple<BlockPos, Vec3d, Vec3d>> shouldStop, Consumer<Triple<BlockPos, Vec3d, Vec3d>> close,
            Vec3d vStart, Vec3d vEnd) {
        return raytraceAlongCatenaryRelative(conn, shouldStop, close,
                vStart.subtract(conn.start.getX(), conn.start.getY(), conn.start.getZ()),
                vEnd.subtract(conn.start.getX(), conn.start.getY(), conn.start.getZ()));
    }

    public static boolean raytraceAlongCatenaryRelative(Connection conn,
            Predicate<Triple<BlockPos, Vec3d, Vec3d>> shouldStop, Consumer<Triple<BlockPos, Vec3d, Vec3d>> close,
            Vec3d vStart, Vec3d vEnd) {
        conn.getSubVertices(vStart, vEnd);
        HashMap<BlockPos, Vec3d> halfScanned = new HashMap<>();
        HashSet<BlockPos> done = new HashSet<>();
        HashSet<Triple<BlockPos, Vec3d, Vec3d>> near = new HashSet<>();
        Vec3d across = vEnd.subtract(vStart);
        across = new Vec3d(across.x, 0, across.z);
        double lengthHor = across.length();
        halfScanned.put(conn.start, vStart);
        halfScanned.put(conn.end, vEnd);
        //Raytrace X&Z
        for (int dim = 0; dim <= 2; dim += 2) {
            int start = (int) Math.ceil(Math.min(getDim(vStart, dim), getDim(vEnd, dim)));
            int end = (int) Math.ceil(Math.max(getDim(vStart, dim), getDim(vEnd, dim)));
            for (int i = start; i < end; i++) {
                double factor = (i - getDim(vStart, dim)) / getDim(across, dim);
                Vec3d pos = conn.getVecAt(factor);

                if (handleVec(pos, pos, 0, halfScanned, done, shouldStop, near, conn.start))
                    return false;
            }
        }
        //Raytrace Y
        boolean vertical = vStart.x == vEnd.x && vStart.z == vEnd.z;
        if (vertical) {
            for (int y = (int) Math.ceil(Math.min(vStart.y, vEnd.y)); y <= Math
                    .floor(Math.max(vStart.y, vEnd.y)); y++) {
                Vec3d pos = new Vec3d(vStart.x, y, vStart.z);
                if (handleVec(pos, pos, 0, halfScanned, done, shouldStop, near, conn.start))
                    return false;
            }
        } else {
            double min = conn.catA + conn.catOffsetY + vStart.y;
            for (int i = 0; i < 2; i++) {
                double factor = i == 0 ? 1 : -1;
                double max = i == 0 ? vEnd.y : vStart.y;
                for (int y = (int) Math.ceil(min); y <= Math.floor(max); y++) {
                    double yReal = y - vStart.y;
                    double posRel;
                    Vec3d pos;
                    posRel = (factor * acosh((yReal - conn.catOffsetY) / conn.catA) * conn.catA + conn.catOffsetX)
                            / lengthHor;
                    pos = new Vec3d(vStart.x + across.x * posRel, y, vStart.z + across.z * posRel);

                    if (posRel >= 0 && posRel <= 1
                            && handleVec(pos, pos, 0, halfScanned, done, shouldStop, near, conn.start))
                        return false;
                }
            }
        }
        for (Triple<BlockPos, Vec3d, Vec3d> p : near)
            close.accept(p);
        for (Map.Entry<BlockPos, Vec3d> p : halfScanned.entrySet())
            if (shouldStop.test(new ImmutableTriple<>(p.getKey(), p.getValue(), p.getValue())))
                return false;
        return true;
    }

    private static boolean handleVec(Vec3d pos, Vec3d origPos, int start, HashMap<BlockPos, Vec3d> halfScanned,
            HashSet<BlockPos> done, Predicate<Triple<BlockPos, Vec3d, Vec3d>> shouldStop,
            HashSet<Triple<BlockPos, Vec3d, Vec3d>> near, BlockPos offset) {
        final double DELTA_HIT = 1e-5;
        final double EPSILON = 1e-5;
        boolean calledOther = false;
        for (int i = start; i < 3; i++) {
            double coord = getDim(pos, i);
            double diff = coord - Math.floor(coord);
            if (diff < DELTA_HIT) {
                if (handleVec(offsetDim(pos, i, -(diff + EPSILON)), origPos, i + 1, halfScanned, done, shouldStop,
                        near, offset))
                    return true;
                calledOther = true;
            }
            diff = Math.ceil(coord) - coord;
            if (diff < DELTA_HIT) {
                if (handleVec(offsetDim(pos, i, diff + EPSILON), origPos, i + 1, halfScanned, done, shouldStop,
                        near, offset))
                    return true;
                calledOther = true;
            }
        }
        if (!calledOther) {
            BlockPos blockPos = new BlockPos(pos);
            return handlePos(origPos.subtract(blockPos.getX(), blockPos.getY(), blockPos.getZ()),
                    blockPos.add(offset), halfScanned, done, shouldStop, near);
        }
        return false;
    }

    private static boolean handlePos(Vec3d pos, BlockPos posB, HashMap<BlockPos, Vec3d> halfScanned,
            HashSet<BlockPos> done, Predicate<Triple<BlockPos, Vec3d, Vec3d>> shouldStop,
            HashSet<Triple<BlockPos, Vec3d, Vec3d>> near) {
        final double DELTA_NEAR = .3;
        if (!done.contains(posB)) {
            if (halfScanned.containsKey(posB) && !pos.equals(halfScanned.get(posB))) {
                Triple<BlockPos, Vec3d, Vec3d> added = new ImmutableTriple<>(posB, halfScanned.get(posB), pos);
                boolean stop = shouldStop.test(added);
                done.add(posB);
                halfScanned.remove(posB);
                near.removeIf((t) -> t.getLeft().equals(posB));
                if (stop)
                    return true;
                for (int i = 0; i < 3; i++) {
                    double coord = getDim(pos, i);
                    double diff = coord - Math.floor(coord);
                    if (diff < DELTA_NEAR)
                        near.add(
                                new ImmutableTriple<>(offsetDim(posB, i, -1), added.getMiddle(), added.getRight()));
                    diff = Math.ceil(coord) - coord;
                    if (diff < DELTA_NEAR)
                        near.add(new ImmutableTriple<>(offsetDim(posB, i, 1), added.getMiddle(), added.getRight()));
                }
            } else {
                halfScanned.put(posB, pos);
            }
        }
        return false;
    }

    public static WireType getWireTypeFromNBT(NBTTagCompound tag, String key) {
        //Legacy code for old save data, where types used to be integers
        if (tag.getTag(key) instanceof NBTTagInt) {
            int i = tag.getInteger(key);
            return i == 1 ? WireType.ELECTRUM
                    : i == 2 ? WireType.STEEL
                            : i == 3 ? WireType.STRUCTURE_ROPE
                                    : i == 4 ? WireType.STRUCTURE_STEEL : WireType.COPPER;
        } else
            return WireType.getValue(tag.getString(key));
    }

    public static EnumActionResult doCoilUse(IWireCoil coil, EntityPlayer player, World world, BlockPos pos,
            EnumHand hand, EnumFacing side, float hitX, float hitY, float hitZ) {
        TileEntity tileEntity = world.getTileEntity(pos);
        if (tileEntity instanceof IImmersiveConnectable && ((IImmersiveConnectable) tileEntity).canConnect()) {
            ItemStack stack = player.getHeldItem(hand);
            TargetingInfo target = new TargetingInfo(side, hitX, hitY, hitZ);
            WireType wire = coil.getWireType(stack);
            BlockPos masterPos = ((IImmersiveConnectable) tileEntity).getConnectionMaster(wire, target);
            Vec3i offset = pos.subtract(masterPos);
            tileEntity = world.getTileEntity(masterPos);
            if (!(tileEntity instanceof IImmersiveConnectable)
                    || !((IImmersiveConnectable) tileEntity).canConnect())
                return EnumActionResult.PASS;

            if (!((IImmersiveConnectable) tileEntity).canConnectCable(wire, target, offset)
                    || !coil.canConnectCable(stack, tileEntity)) {
                if (!world.isRemote)
                    player.sendStatusMessage(new TextComponentTranslation(Lib.CHAT_WARN + "wrongCable"), true);
                return EnumActionResult.FAIL;
            }

            if (!world.isRemote)
                if (!ItemNBTHelper.hasKey(stack, "linkingPos")) {
                    ItemNBTHelper.setIntArray(stack, "linkingPos",
                            new int[] { world.provider.getDimension(), masterPos.getX(), masterPos.getY(),
                                    masterPos.getZ(), offset.getX(), offset.getY(), offset.getZ() });
                    NBTTagCompound targetNbt = new NBTTagCompound();
                    target.writeToNBT(targetNbt);
                    ItemNBTHelper.setTagCompound(stack, "targettingInfo", targetNbt);
                } else {
                    int[] array = ItemNBTHelper.getIntArray(stack, "linkingPos");
                    BlockPos linkPos = new BlockPos(array[1], array[2], array[3]);
                    Vec3i offsetLink = BlockPos.NULL_VECTOR;
                    if (array.length == 7)
                        offsetLink = new Vec3i(array[4], array[5], array[6]);
                    TileEntity tileEntityLinkingPos = world.getTileEntity(linkPos);
                    int distanceSq = (int) Math.ceil(linkPos.distanceSq(masterPos));
                    int maxLengthSq = coil.getMaxLength(stack); //not squared yet
                    maxLengthSq *= maxLengthSq;
                    if (array[0] != world.provider.getDimension())
                        player.sendStatusMessage(new TextComponentTranslation(Lib.CHAT_WARN + "wrongDimension"),
                                true);
                    else if (linkPos.equals(masterPos))
                        player.sendStatusMessage(new TextComponentTranslation(Lib.CHAT_WARN + "sameConnection"),
                                true);
                    else if (distanceSq > maxLengthSq)
                        player.sendStatusMessage(new TextComponentTranslation(Lib.CHAT_WARN + "tooFar"), true);
                    else {
                        TargetingInfo targetLink = TargetingInfo
                                .readFromNBT(ItemNBTHelper.getTagCompound(stack, "targettingInfo"));
                        if (!(tileEntityLinkingPos instanceof IImmersiveConnectable)
                                || !((IImmersiveConnectable) tileEntityLinkingPos).canConnectCable(wire, targetLink,
                                        offsetLink)
                                || !((IImmersiveConnectable) tileEntityLinkingPos)
                                        .getConnectionMaster(wire, targetLink).equals(linkPos)
                                || !coil.canConnectCable(stack, tileEntityLinkingPos))
                            player.sendStatusMessage(new TextComponentTranslation(Lib.CHAT_WARN + "invalidPoint"),
                                    true);
                        else {
                            IImmersiveConnectable nodeHere = (IImmersiveConnectable) tileEntity;
                            IImmersiveConnectable nodeLink = (IImmersiveConnectable) tileEntityLinkingPos;
                            boolean connectionExists = false;
                            Set<Connection> outputs = ImmersiveNetHandler.INSTANCE.getConnections(world,
                                    Utils.toCC(nodeHere));
                            if (outputs != null)
                                for (Connection con : outputs) {
                                    if (con.end.equals(Utils.toCC(nodeLink)))
                                        connectionExists = true;
                                }
                            if (connectionExists)
                                player.sendStatusMessage(
                                        new TextComponentTranslation(Lib.CHAT_WARN + "connectionExists"), true);
                            else {
                                Set<BlockPos> ignore = new HashSet<>();
                                ignore.addAll(nodeHere.getIgnored(nodeLink));
                                ignore.addAll(nodeLink.getIgnored(nodeHere));
                                Connection tmpConn = new Connection(Utils.toCC(nodeHere), Utils.toCC(nodeLink),
                                        wire, (int) Math.sqrt(distanceSq));
                                Vec3d start = nodeHere.getConnectionOffset(tmpConn, target,
                                        pos.subtract(masterPos));
                                Vec3d end = nodeLink.getConnectionOffset(tmpConn, targetLink, offsetLink).add(
                                        linkPos.getX() - masterPos.getX(), linkPos.getY() - masterPos.getY(),
                                        linkPos.getZ() - masterPos.getZ());
                                BlockPos.MutableBlockPos failedReason = new BlockPos.MutableBlockPos();
                                boolean canSee = ApiUtils.raytraceAlongCatenaryRelative(tmpConn, (p) -> {
                                    if (ignore.contains(p.getLeft()))
                                        return false;
                                    IBlockState state = world.getBlockState(p.getLeft());
                                    if (ApiUtils.preventsConnection(world, p.getLeft(), state, p.getMiddle(),
                                            p.getRight())) {
                                        failedReason.setPos(p.getLeft());
                                        return true;
                                    }
                                    return false;
                                }, (p) -> {
                                }, start, end);
                                if (canSee) {
                                    Connection conn = ImmersiveNetHandler.INSTANCE.addAndGetConnection(world,
                                            Utils.toCC(nodeHere), Utils.toCC(nodeLink), (int) Math.sqrt(distanceSq),
                                            wire);

                                    nodeHere.connectCable(wire, target, nodeLink, offset);
                                    nodeLink.connectCable(wire, targetLink, nodeHere, offsetLink);
                                    ImmersiveNetHandler.INSTANCE.addBlockData(world, conn);
                                    IESaveData.setDirty(world.provider.getDimension());
                                    Utils.unlockIEAdvancement(player, "main/connect_wire");

                                    if (!player.capabilities.isCreativeMode)
                                        coil.consumeWire(stack, (int) Math.sqrt(distanceSq));
                                    ((TileEntity) nodeHere).markDirty();
                                    world.addBlockEvent(masterPos, ((TileEntity) nodeHere).getBlockType(), -1, 0);
                                    IBlockState state = world.getBlockState(masterPos);
                                    world.notifyBlockUpdate(masterPos, state, state, 3);
                                    ((TileEntity) nodeLink).markDirty();
                                    world.addBlockEvent(linkPos, ((TileEntity) nodeLink).getBlockType(), -1, 0);
                                    state = world.getBlockState(linkPos);
                                    world.notifyBlockUpdate(linkPos, state, state, 3);
                                } else {
                                    player.sendStatusMessage(
                                            new TextComponentTranslation(Lib.CHAT_WARN + "cantSee"), true);
                                    ImmersiveEngineering.packetHandler.sendToAllAround(
                                            new MessageObstructedConnection(tmpConn, failedReason, player.world),
                                            new NetworkRegistry.TargetPoint(player.world.provider.getDimension(),
                                                    player.posX, player.posY, player.posZ, 64));
                                }
                            }
                        }
                    }
                    ItemNBTHelper.remove(stack, "linkingPos");
                    ItemNBTHelper.remove(stack, "targettingInfo");
                }
            return EnumActionResult.SUCCESS;
        }
        return EnumActionResult.PASS;
    }

    public static Object convertToValidRecipeInput(Object input) {
        if (input instanceof ItemStack)
            return input;
        else if (input instanceof Item)
            return new ItemStack((Item) input);
        else if (input instanceof Block)
            return new ItemStack((Block) input);
        else if (input instanceof List)
            return input;
        else if (input instanceof String) {
            if (!ApiUtils.isExistingOreName((String) input))
                return null;
            List<ItemStack> l = OreDictionary.getOres((String) input);
            if (!l.isEmpty())
                return l;
            else
                return null;
        } else
            throw new RuntimeException(
                    "Recipe Inputs must always be ItemStack, Item, Block or String (OreDictionary name), " + input
                            + " is invalid");
    }

    public static IngredientStack createIngredientStack(Object input, boolean preferWildcard) {
        if (input instanceof IngredientStack)
            return (IngredientStack) input;
        else if (input instanceof ItemStack)
            return new IngredientStack((ItemStack) input);
        else if (input instanceof Item) {
            if (preferWildcard)
                return new IngredientStack(new ItemStack((Item) input, 1, OreDictionary.WILDCARD_VALUE));
            return new IngredientStack(new ItemStack((Item) input));
        } else if (input instanceof Block) {
            if (preferWildcard)
                return new IngredientStack(new ItemStack((Block) input, 1, OreDictionary.WILDCARD_VALUE));
            return new IngredientStack(new ItemStack((Block) input));
        } else if (input instanceof Ingredient)
            return new IngredientStack(Arrays.asList(((Ingredient) input).getMatchingStacks()));
        else if (input instanceof List) {
            if (!((List) input).isEmpty()) {
                if (((List) input).get(0) instanceof ItemStack)
                    return new IngredientStack(((List<ItemStack>) input));
                else if (((List) input).get(0) instanceof String) {
                    ArrayList<ItemStack> itemList = new ArrayList();
                    for (String s : ((List<String>) input))
                        itemList.addAll(OreDictionary.getOres(s));
                    return new IngredientStack(itemList);
                }
            } else
                return new IngredientStack(ItemStack.EMPTY);
        } else if (input instanceof ItemStack[])
            return new IngredientStack(Arrays.asList((ItemStack[]) input));
        else if (input instanceof String[]) {
            ArrayList<ItemStack> itemList = new ArrayList();
            for (String s : ((String[]) input))
                itemList.addAll(OreDictionary.getOres(s));
            return new IngredientStack(itemList);
        } else if (input instanceof String)
            return new IngredientStack((String) input);
        else if (input instanceof FluidStack)
            return new IngredientStack((FluidStack) input);
        throw new RuntimeException(
                "Recipe Ingredients must always be ItemStack, Item, Block, List<ItemStack>, String (OreDictionary name) or FluidStack; "
                        + input + " is invalid");
    }

    public static IngredientStack createIngredientStack(Object input) {
        return createIngredientStack(input, false);
    }

    public static ItemStack getItemStackFromObject(Object o) {
        if (o instanceof ItemStack)
            return (ItemStack) o;
        else if (o instanceof Item)
            return new ItemStack((Item) o);
        else if (o instanceof Block)
            return new ItemStack((Block) o);
        else if (o instanceof List)
            return ((List<ItemStack>) o).get(0);
        else if (o instanceof String) {
            if (!isExistingOreName((String) o))
                return ItemStack.EMPTY;
            List<ItemStack> l = OreDictionary.getOres((String) o);
            if (!l.isEmpty())
                return l.get(0);
            else
                return ItemStack.EMPTY;
        }
        return ItemStack.EMPTY;
    }

    public static boolean hasPlayerIngredient(EntityPlayer player, IngredientStack ingredient) {
        int amount = ingredient.inputSize;
        ItemStack itemstack;
        for (EnumHand hand : EnumHand.values()) {
            itemstack = player.getHeldItem(hand);
            if (ingredient.matchesItemStackIgnoringSize(itemstack)) {
                amount -= itemstack.getCount();
                if (amount <= 0)
                    return true;
            }
        }
        for (int i = 0; i < player.inventory.getSizeInventory(); i++) {
            itemstack = player.inventory.getStackInSlot(i);
            if (ingredient.matchesItemStackIgnoringSize(itemstack)) {
                amount -= itemstack.getCount();
                if (amount <= 0)
                    return true;
            }
        }
        return amount <= 0;
    }

    public static void consumePlayerIngredient(EntityPlayer player, IngredientStack ingredient) {
        int amount = ingredient.inputSize;
        ItemStack itemstack;
        for (EnumHand hand : EnumHand.values()) {
            itemstack = player.getHeldItem(hand);
            if (ingredient.matchesItemStackIgnoringSize(itemstack)) {
                int taken = Math.min(amount, itemstack.getCount());
                amount -= taken;
                itemstack.shrink(taken);
                if (itemstack.getCount() <= 0)
                    player.setHeldItem(hand, ItemStack.EMPTY);
                if (amount <= 0)
                    return;
            }
        }
        for (int i = 0; i < player.inventory.getSizeInventory(); i++) {
            itemstack = player.inventory.getStackInSlot(i);
            if (ingredient.matchesItemStackIgnoringSize(itemstack)) {
                int taken = Math.min(amount, itemstack.getCount());
                amount -= taken;
                itemstack.shrink(taken);
                if (itemstack.getCount() <= 0)
                    player.inventory.setInventorySlotContents(i, ItemStack.EMPTY);
                if (amount <= 0)
                    return;
            }
        }
    }

    public static Map<String, Integer> sortMap(Map<String, Integer> map, boolean inverse) {
        TreeMap<String, Integer> sortedMap = new TreeMap<String, Integer>(new ValueComparator(map, inverse));
        sortedMap.putAll(map);
        return sortedMap;
    }

    public static <T extends TileEntity & IGeneralMultiblock> void checkForNeedlessTicking(T te) {
        if (!te.getWorld().isRemote && te.isLogicDummy())
            EventHandler.REMOVE_FROM_TICKING.add(te);
    }

    public static boolean preventsConnection(World worldIn, BlockPos pos, IBlockState state, Vec3d a, Vec3d b) {
        if (state.getBlock().canCollideCheck(state, false)) {
            List<AxisAlignedBB> aabbs = new ArrayList<>(1);
            state.addCollisionBoxToList(worldIn, pos, Block.FULL_BLOCK_AABB.offset(pos), aabbs, null, false);
            for (AxisAlignedBB aabb : aabbs) {
                aabb = aabb.offset(-pos.getX(), -pos.getY(), -pos.getZ()).grow(1e-5);
                if (aabb.contains(a) || aabb.contains(b))
                    return true;
            }
            RayTraceResult rayResult = state.collisionRayTrace(worldIn, pos,
                    a.add(pos.getX(), pos.getY(), pos.getZ()), b.add(pos.getX(), pos.getY(), pos.getZ()));
            return rayResult != null && rayResult.typeOfHit == RayTraceResult.Type.BLOCK;
        }
        return false;
    }

    //Based on net.minecraft.entity.EntityLivingBase.knockBack
    public static void knockbackNoSource(EntityLivingBase entity, double strength, double xRatio, double zRatio) {
        entity.isAirBorne = true;
        float factor = MathHelper.sqrt(xRatio * xRatio + zRatio * zRatio);
        entity.motionX /= 2;
        entity.motionZ /= 2;
        entity.motionX -= xRatio / (double) factor * strength;
        entity.motionZ -= zRatio / (double) factor * strength;

        if (entity.onGround) {
            entity.motionY /= 2;
            entity.motionY += strength;

            if (entity.motionY > 0.4) {
                entity.motionY = 0.4;
            }
        }
    }

    public static Connection raytraceWires(World world, Vec3d start, Vec3d end, @Nullable Connection ignored) {
        Map<BlockPos, ImmersiveNetHandler.BlockWireInfo> inDim = ImmersiveNetHandler.INSTANCE.blockWireMap
                .lookup(world.provider.getDimension());
        AtomicReference<Connection> ret = new AtomicReference<>();
        AtomicDouble minDistSq = new AtomicDouble(Double.POSITIVE_INFINITY);
        if (inDim != null) {
            Utils.rayTrace(start, end, world, (pos) -> {
                if (inDim.containsKey(pos)) {
                    ImmersiveNetHandler.BlockWireInfo info = inDim.get(pos);
                    for (int i = 0; i < 2; i++) {
                        Set<Triple<Connection, Vec3d, Vec3d>> conns = i == 0 ? info.in : info.near;
                        for (Triple<Connection, Vec3d, Vec3d> conn : conns) {
                            Connection c = conn.getLeft();
                            if (ignored == null || !c.hasSameConnectors(ignored)) {
                                Vec3d startRelative = start.add(-pos.getX(), -pos.getY(), -pos.getZ());
                                Vec3d across = conn.getRight().subtract(conn.getMiddle());
                                double t = Utils.getCoeffForMinDistance(startRelative, conn.getMiddle(), across);
                                t = MathHelper.clamp(0, t, 1);
                                Vec3d closest = conn.getMiddle().add(t * across.x, t * across.y, t * across.z);
                                double distSq = closest.squareDistanceTo(startRelative);
                                if (distSq < minDistSq.get()) {
                                    ret.set(c);
                                    minDistSq.set(distSq);
                                }
                            }
                        }
                    }
                }
            });
        }

        return ret.get();
    }

    public static Connection getConnectionMovedThrough(World world, EntityLivingBase e) {
        Vec3d start = e.getPositionEyes(0);
        Vec3d end = e.getPositionEyes(1);
        return raytraceWires(world, start, end, null);
    }

    public static Connection getTargetConnection(World world, EntityPlayer player, Connection ignored,
            double maxDistance) {
        Vec3d look = player.getLookVec();
        Vec3d start = player.getPositionEyes(1);
        Vec3d end = start.add(look.scale(maxDistance));
        Connection ret = raytraceWires(world, start, end, ignored);
        if (ret != null) {
            Vec3d across = new Vec3d(ret.end).subtract(new Vec3d(ret.start));
            if (across.dotProduct(player.getLookVec()) < 0)
                ret = ImmersiveNetHandler.INSTANCE.getReverseConnection(world.provider.getDimension(), ret);
        }
        return ret;
    }

    public static void addFutureServerTask(World world, Runnable task) {
        if (world.getMinecraftServer() != null)
            synchronized (world.getMinecraftServer().futureTaskQueue) {
                world.getMinecraftServer().futureTaskQueue.add(ListenableFutureTask.create(task, null));
            }
    }

    public static void callFromOtherThread(Consumer<Runnable> cons, Runnable r) {
        new Thread(() -> cons.accept(r)).start();
    }

    public static void moveConnectionEnd(Connection conn, BlockPos newEnd, World world) {
        IImmersiveConnectable otherSide = ApiUtils.toIIC(conn.start, world);
        Vec3d start = ApiUtils.getVecForIICAt(world, conn.start, conn);
        Vec3d end = ApiUtils.getVecForIICAt(world, conn.end, conn);
        if (otherSide == null || otherSide.moveConnectionTo(conn, newEnd)) {
            ImmersiveNetHandler.INSTANCE.removeConnection(world, conn, start, end);
            Connection newConn = new Connection(conn.start, newEnd, conn.cableType, conn.length);
            ImmersiveNetHandler.INSTANCE.addConnection(world, conn.start, newConn);
            ImmersiveNetHandler.INSTANCE.addConnection(world, newEnd,
                    new Connection(newEnd, conn.start, conn.cableType, conn.length));
            ImmersiveNetHandler.INSTANCE.addBlockData(world, newConn);
        }
    }

    public static class ValueComparator implements java.util.Comparator<String> {
        Map<String, Integer> base;
        boolean inverse;

        public ValueComparator(Map<String, Integer> base, boolean inverse) {
            this.base = base;
            this.inverse = inverse;
        }

        @Override
        public int compare(String s0, String s1)//Cant return equal to keys separate
        {
            if (inverse) {
                if (base.get(s0) <= base.get(s1))
                    return -1;
                else
                    return 1;
            } else {
                if (base.get(s0) >= base.get(s1))
                    return -1;
                else
                    return 1;
            }
        }

        @Override
        public boolean equals(Object obj) {
            if (!(obj instanceof ValueComparator))
                return false;
            ValueComparator other = (ValueComparator) obj;
            return other.base == base && other.inverse == inverse;
        }
    }

    @SideOnly(Side.CLIENT)
    public static TextureAtlasSprite getRegisterSprite(TextureMap map, String path) {
        TextureAtlasSprite sprite = map.getTextureExtry(path);
        if (sprite == null) {
            map.registerSprite(new ResourceLocation(path));
            sprite = map.getTextureExtry(path);
        }
        return sprite;
    }

    @SideOnly(Side.CLIENT)
    public static TextureAtlasSprite getRegisterSprite(TextureMap map, ResourceLocation path) {
        TextureAtlasSprite sprite = map.getTextureExtry(path.toString());
        if (sprite == null) {
            map.registerSprite(path);
            sprite = map.getTextureExtry(path.toString());
        }
        return sprite;
    }

    @SideOnly(Side.CLIENT)
    public static Function<BakedQuad, BakedQuad> transformQuad(Matrix4 mat, @Nullable VertexFormat ignored,
            Function<Integer, Integer> colorMultiplier) {
        return new QuadTransformer(mat, colorMultiplier);
    }

    // Full class names to work around some sort of compiler bug (Only happens when building with gradle)
    @net.minecraftforge.fml.relauncher.SideOnly(Side.CLIENT)
    private static class QuadTransformer implements
            java.util.function.Function<net.minecraft.client.renderer.block.model.BakedQuad, net.minecraft.client.renderer.block.model.BakedQuad> {
        private final Matrix4 transform;
        private final Matrix4 normalTransform;
        @Nullable
        private final Function<Integer, Integer> colorTransform;
        private UnpackedBakedQuad.Builder currentQuadBuilder;
        private final Map<VertexFormat, IVertexConsumer> consumers = new HashMap<>();

        private QuadTransformer(Matrix4 transform, @Nullable Function<Integer, Integer> colorTransform) {
            this.transform = transform;
            this.colorTransform = colorTransform;
            this.normalTransform = transform.copy();
            normalTransform.transpose().invert();
        }

        @Override
        public BakedQuad apply(BakedQuad q) {
            IVertexConsumer transformer = consumers.computeIfAbsent(q.getFormat(), this::createConsumer);
            assert transformer != null;
            currentQuadBuilder = new UnpackedBakedQuad.Builder(q.getFormat());
            q.pipe(transformer);
            return currentQuadBuilder.build();
        }

        private IVertexConsumer createConsumer(VertexFormat f) {
            int posPos = -1;
            int normPos = -1;
            int colorPos = -1;
            for (int i = 0; i < f.getElements().size(); i++)
                if (f.getElement(i).getUsage() == VertexFormatElement.EnumUsage.POSITION)
                    posPos = i;
                else if (f.getElement(i).getUsage() == VertexFormatElement.EnumUsage.NORMAL)
                    normPos = i;
                else if (f.getElement(i).getUsage() == VertexFormatElement.EnumUsage.COLOR)
                    colorPos = i;
            if (posPos == -1)
                return null;
            final int posPosFinal = posPos;
            final int normPosFinal = normPos;
            final int colorPosFinal = colorPos;
            return new IVertexConsumer() {
                int tintIndex = -1;

                @Nonnull
                @Override
                public VertexFormat getVertexFormat() {
                    return f;
                }

                @Override
                public void setQuadTint(int tint) {
                    currentQuadBuilder.setQuadTint(tint);
                    tintIndex = tint;
                }

                @Override
                public void setQuadOrientation(@Nonnull EnumFacing orientation) {
                    Vec3d newFront = normalTransform.apply(new Vec3d(orientation.getDirectionVec()));
                    EnumFacing newOrientation = EnumFacing.getFacingFromVector((float) newFront.x,
                            (float) newFront.y, (float) newFront.z);
                    currentQuadBuilder.setQuadOrientation(newOrientation);
                }

                @Override
                public void setApplyDiffuseLighting(boolean diffuse) {
                    currentQuadBuilder.setApplyDiffuseLighting(diffuse);
                }

                @Override
                public void setTexture(@Nonnull TextureAtlasSprite texture) {
                    currentQuadBuilder.setTexture(texture);
                }

                @Override
                public void put(int element, @Nonnull float... data) {
                    if (element == posPosFinal && transform != null) {
                        Vector3f newPos = transform.apply(new Vector3f(data[0], data[1], data[2]));
                        data = new float[3];
                        data[0] = newPos.x;
                        data[1] = newPos.y;
                        data[2] = newPos.z;
                    } else if (element == normPosFinal && normalTransform != null) {
                        Vector3f newNormal = normalTransform.apply(new Vector3f(data[0], data[1], data[2]));
                        data = new float[3];
                        data[0] = newNormal.x;
                        data[1] = newNormal.y;
                        data[2] = newNormal.z;
                    } else if (element == colorPosFinal) {
                        if (tintIndex != -1 && colorTransform != null) {
                            int multiplier = colorTransform.apply(tintIndex);
                            if (multiplier != 0) {
                                float r = (float) (multiplier >> 16 & 255) / 255.0F;
                                float g = (float) (multiplier >> 8 & 255) / 255.0F;
                                float b = (float) (multiplier & 255) / 255.0F;
                                float[] oldData = data;
                                data = new float[4];
                                data[0] = oldData[0] * r;
                                data[1] = oldData[1] * g;
                                data[2] = oldData[2] * b;
                                data[3] = oldData[3];
                            }
                        }
                    }
                    currentQuadBuilder.put(element, data);
                }
            };
        }
    }
}