/** * Copyright (c) 2011-2014, SpaceToad and the BuildCraft Team * http://www.mod-buildcraft.com * * BuildCraft is distributed under the terms of the Minecraft Mod Public * License 1.0, or MMPL. Please check the contents of the license located in * http://www.mod-buildcraft.com/MMPL-1.0.txt */ package buildcraft.core.utils; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Random; import net.minecraft.block.Block; import net.minecraft.entity.EntityLivingBase; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; import net.minecraft.nbt.CompressedStreamTools; import net.minecraft.nbt.NBTSizeTracker; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.MathHelper; import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; import net.minecraftforge.common.util.ForgeDirection; import buildcraft.BuildCraftCore; import buildcraft.api.core.IAreaProvider; import buildcraft.api.core.LaserKind; import buildcraft.api.core.Position; import buildcraft.api.transport.IPipeTile; import buildcraft.api.transport.IPipeTile.PipeType; import buildcraft.core.BlockIndex; import buildcraft.core.DefaultProps; import buildcraft.core.EntityBlock; import buildcraft.core.IDropControlInventory; import buildcraft.core.IFramePipeConnection; import buildcraft.core.TileBuildCraft; import buildcraft.core.inventory.ITransactor; import buildcraft.core.inventory.InvUtils; import buildcraft.core.inventory.Transactor; import buildcraft.core.network.BuildCraftPacket; import buildcraft.core.network.ISynchronizedTile; import buildcraft.core.network.PacketUpdate; import buildcraft.core.proxy.CoreProxy; import buildcraft.energy.TileEngine; import cpw.mods.fml.common.network.internal.FMLProxyPacket; public class Utils { public static final Random RANDOM = new Random(); private static final List directions = new ArrayList(Arrays.asList(ForgeDirection.VALID_DIRECTIONS)); public enum NBTTag_Types { NBTTagEnd, NBTTagByte, NBTTagShort, NBTTagInt, NBTTagLong, NBTTagFloat, NBTTagDouble, NBTTagByteArray, NBTTagString, NBTTagList, NBTTagCompound, NBTTagIntArray } /* IINVENTORY HELPERS */ /** * Tries to add the passed stack to any valid inventories around the given * coordinates. * * @param stack * @param world * @param x * @param y * @param z * @return amount used */ public static int addToRandomInventoryAround(World world, int x, int y, int z, ItemStack stack) { Collections.shuffle(directions); for (ForgeDirection orientation : directions) { Position pos = new Position(x, y, z, orientation); pos.moveForwards(1.0); TileEntity tileInventory = world.getTileEntity((int) pos.x, (int) pos.y, (int) pos.z); ITransactor transactor = Transactor.getTransactorFor(tileInventory); if (transactor != null && !(tileInventory instanceof TileEngine) && transactor.add(stack, orientation.getOpposite(), false).stackSize > 0) { return transactor.add(stack, orientation.getOpposite(), true).stackSize; } } return 0; } /** * Returns the cardinal direction of the entity depending on its * rotationYaw */ public static ForgeDirection get2dOrientation(EntityLivingBase entityliving) { ForgeDirection[] orientationTable = { ForgeDirection.SOUTH, ForgeDirection.WEST, ForgeDirection.NORTH, ForgeDirection.EAST }; int orientationIndex = MathHelper .floor_double((entityliving.rotationYaw + 45.0) / 90.0) & 3; return orientationTable[orientationIndex]; } /* * FIXME This is only kept here for the purpose of get3dOrientation, which * should probably be removed following the same principles */ @Deprecated private static ForgeDirection get2dOrientation(Position pos1, Position pos2) { double Dx = pos1.x - pos2.x; double Dz = pos1.z - pos2.z; double angle = Math.atan2(Dz, Dx) / Math.PI * 180 + 180; if (angle < 45 || angle > 315) { return ForgeDirection.EAST; } else if (angle < 135) { return ForgeDirection.SOUTH; } else if (angle < 225) { return ForgeDirection.WEST; } else { return ForgeDirection.NORTH; } } public static ForgeDirection get3dOrientation(Position pos1, Position pos2) { double Dx = pos1.x - pos2.x; double Dy = pos1.y - pos2.y; double angle = Math.atan2(Dy, Dx) / Math.PI * 180 + 180; if (angle > 45 && angle < 135) { return ForgeDirection.UP; } else if (angle > 225 && angle < 315) { return ForgeDirection.DOWN; } else { return get2dOrientation(pos1, pos2); } } /** * Look around the tile given in parameter in all 6 position, tries to add * the items to a random pipe entry around. Will make sure that the location * from which the items are coming from (identified by the from parameter) * isn't used again so that entities doesn't go backwards. Returns true if * successful, false otherwise. */ public static int addToRandomPipeAround(World world, int x, int y, int z, ForgeDirection from, ItemStack stack) { List possiblePipes = new ArrayList(); List pipeDirections = new ArrayList(); for (ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { if (from.getOpposite() == side) continue; Position pos = new Position(x, y, z, side); pos.moveForwards(1.0); TileEntity tile = world.getTileEntity((int) pos.x, (int) pos.y, (int) pos.z); if (tile instanceof IPipeTile) { IPipeTile pipe = (IPipeTile) tile; if (pipe.getPipeType() != PipeType.ITEM) continue; if (!pipe.isPipeConnected(side.getOpposite())) continue; possiblePipes.add(pipe); pipeDirections.add(side.getOpposite()); } } if (possiblePipes.size() > 0) { int choice = RANDOM.nextInt(possiblePipes.size()); IPipeTile pipeEntry = possiblePipes.get(choice); return pipeEntry.injectItem(stack, true, pipeDirections.get(choice)); } return 0; } public static TileEntity getTile(World world, Position pos, ForgeDirection step) { Position tmp = new Position(pos); tmp.orientation = step; tmp.moveForwards(1.0); return world.getTileEntity((int) tmp.x, (int) tmp.y, (int) tmp.z); } public static IAreaProvider getNearbyAreaProvider(World world, int i, int j, int k) { TileEntity a1 = world.getTileEntity(i + 1, j, k); TileEntity a2 = world.getTileEntity(i - 1, j, k); TileEntity a3 = world.getTileEntity(i, j, k + 1); TileEntity a4 = world.getTileEntity(i, j, k - 1); TileEntity a5 = world.getTileEntity(i, j + 1, k); TileEntity a6 = world.getTileEntity(i, j - 1, k); if (a1 instanceof IAreaProvider) { return (IAreaProvider) a1; } if (a2 instanceof IAreaProvider) { return (IAreaProvider) a2; } if (a3 instanceof IAreaProvider) { return (IAreaProvider) a3; } if (a4 instanceof IAreaProvider) { return (IAreaProvider) a4; } if (a5 instanceof IAreaProvider) { return (IAreaProvider) a5; } if (a6 instanceof IAreaProvider) { return (IAreaProvider) a6; } return null; } public static EntityBlock createLaser(World world, Position p1, Position p2, LaserKind kind) { if (p1.equals(p2)) { return null; } double iSize = p2.x - p1.x; double jSize = p2.y - p1.y; double kSize = p2.z - p1.z; double i = p1.x; double j = p1.y; double k = p1.z; if (iSize != 0) { i += 0.5; j += 0.45; k += 0.45; jSize = 0.10; kSize = 0.10; } else if (jSize != 0) { i += 0.45; j += 0.5; k += 0.45; iSize = 0.10; kSize = 0.10; } else if (kSize != 0) { i += 0.45; j += 0.45; k += 0.5; iSize = 0.10; jSize = 0.10; } EntityBlock block = CoreProxy.proxy.newEntityBlock(world, i, j, k, iSize, jSize, kSize, kind); block.setBrightness(210); world.spawnEntityInWorld(block); return block; } public static EntityBlock[] createLaserBox(World world, double xMin, double yMin, double zMin, double xMax, double yMax, double zMax, LaserKind kind) { EntityBlock lasers[] = new EntityBlock[12]; Position[] p = new Position[8]; p[0] = new Position(xMin, yMin, zMin); p[1] = new Position(xMax, yMin, zMin); p[2] = new Position(xMin, yMax, zMin); p[3] = new Position(xMax, yMax, zMin); p[4] = new Position(xMin, yMin, zMax); p[5] = new Position(xMax, yMin, zMax); p[6] = new Position(xMin, yMax, zMax); p[7] = new Position(xMax, yMax, zMax); lasers[0] = Utils.createLaser(world, p[0], p[1], kind); lasers[1] = Utils.createLaser(world, p[0], p[2], kind); lasers[2] = Utils.createLaser(world, p[2], p[3], kind); lasers[3] = Utils.createLaser(world, p[1], p[3], kind); lasers[4] = Utils.createLaser(world, p[4], p[5], kind); lasers[5] = Utils.createLaser(world, p[4], p[6], kind); lasers[6] = Utils.createLaser(world, p[5], p[7], kind); lasers[7] = Utils.createLaser(world, p[6], p[7], kind); lasers[8] = Utils.createLaser(world, p[0], p[4], kind); lasers[9] = Utils.createLaser(world, p[1], p[5], kind); lasers[10] = Utils.createLaser(world, p[2], p[6], kind); lasers[11] = Utils.createLaser(world, p[3], p[7], kind); return lasers; } public static void handleBufferedDescription(ISynchronizedTile tileSynch) { TileEntity tile = (TileEntity) tileSynch; BlockIndex index = new BlockIndex(tile.xCoord, tile.yCoord, tile.zCoord); if (BuildCraftCore.bufferedDescriptions.containsKey(index)) { PacketUpdate payload = BuildCraftCore.bufferedDescriptions.get(index); BuildCraftCore.bufferedDescriptions.remove(index); try { tileSynch.handleDescriptionPacket(payload); } catch (IOException ex) { ex.printStackTrace(); } tileSynch.postPacketHandling(payload); } } public static void preDestroyBlock(World world, int i, int j, int k) { TileEntity tile = world.getTileEntity(i, j, k); if (tile instanceof IInventory && !world.isRemote) { if (!(tile instanceof IDropControlInventory) || ((IDropControlInventory) tile).doDrop()) { InvUtils.dropItems(world, (IInventory) tile, i, j, k); InvUtils.wipeInventory((IInventory) tile); } } if (tile instanceof TileBuildCraft) { ((TileBuildCraft) tile).destroy(); } } public static boolean checkPipesConnections(TileEntity tile1, TileEntity tile2) { if (tile1 == null || tile2 == null) return false; if (!(tile1 instanceof IPipeTile) && !(tile2 instanceof IPipeTile)) return false; ForgeDirection o = ForgeDirection.UNKNOWN; if (tile1.xCoord - 1 == tile2.xCoord) o = ForgeDirection.WEST; else if (tile1.xCoord + 1 == tile2.xCoord) o = ForgeDirection.EAST; else if (tile1.yCoord - 1 == tile2.yCoord) o = ForgeDirection.DOWN; else if (tile1.yCoord + 1 == tile2.yCoord) o = ForgeDirection.UP; else if (tile1.zCoord - 1 == tile2.zCoord) o = ForgeDirection.NORTH; else if (tile1.zCoord + 1 == tile2.zCoord) o = ForgeDirection.SOUTH; if (tile1 instanceof IPipeTile && !((IPipeTile) tile1).isPipeConnected(o)) return false; if (tile2 instanceof IPipeTile && !((IPipeTile) tile2).isPipeConnected(o.getOpposite())) return false; return true; } public static boolean checkLegacyPipesConnections(IBlockAccess blockAccess, int x1, int y1, int z1, int x2, int y2, int z2) { Block b1 = blockAccess.getBlock(x1, y1, z1); Block b2 = blockAccess.getBlock(x2, y2, z2); if (!(b1 instanceof IFramePipeConnection) && !(b2 instanceof IFramePipeConnection)) { return false; } if (b1 instanceof IFramePipeConnection && !((IFramePipeConnection) b1).isPipeConnected(blockAccess, x1, y1, z1, x2, y2, z2)) { return false; } if (b2 instanceof IFramePipeConnection && !((IFramePipeConnection) b2).isPipeConnected(blockAccess, x2, y2, z2, x1, y1, z1)) { return false; } return true; } public static T[] concat(T[] first, T[] second) { T[] result = Arrays.copyOf(first, first.length + second.length); System.arraycopy(second, 0, result, first.length, second.length); return result; } public static int[] concat(int[] first, int[] second) { int[] result = Arrays.copyOf(first, first.length + second.length); System.arraycopy(second, 0, result, first.length, second.length); return result; } public static float[] concat(float[] first, float[] second) { float[] result = Arrays.copyOf(first, first.length + second.length); System.arraycopy(second, 0, result, first.length, second.length); return result; } public static int[] createSlotArray(int first, int count) { int[] slots = new int[count]; for (int k = first; k < first + count; k++) { slots[k - first] = k; } return slots; } public static void writeUTF (ByteBuf data, String str) { try { byte [] b = str.getBytes("UTF-8"); data.writeInt (b.length); data.writeBytes(b); } catch (UnsupportedEncodingException e) { e.printStackTrace(); data.writeInt (0); } } public static String readUTF (ByteBuf data) { try { int len = data.readInt(); byte [] b = new byte [len]; data.readBytes(b); return new String (b, "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); return null; } } public static void writeNBT (ByteBuf data, NBTTagCompound nbt) { try { byte[] compressed = CompressedStreamTools.compress(nbt); data.writeShort(compressed.length); data.writeBytes(compressed); } catch (IOException e) { e.printStackTrace(); } } public static NBTTagCompound readNBT(ByteBuf data) { try { short length = data.readShort(); byte[] compressed = new byte[length]; data.readBytes(compressed); return CompressedStreamTools.func_152457_a(compressed, NBTSizeTracker.field_152451_a); } catch (IOException e) { e.printStackTrace(); return null; } } public static void writeStack (ByteBuf data, ItemStack stack) { if (stack == null) { data.writeBoolean(false); } else { data.writeBoolean(true); NBTTagCompound nbt = new NBTTagCompound(); stack.writeToNBT(nbt); Utils.writeNBT(data, nbt); } } public static ItemStack readStack(ByteBuf data) { if (!data.readBoolean()) { return null; } else { NBTTagCompound nbt = readNBT(data); return ItemStack.loadItemStackFromNBT(nbt); } } /** * This subprogram transforms a packet into a FML packet to be send in the * minecraft default packet mechanism. This always use BC-CORE as a * channel, and as a result, should use discriminators declared there. * * WARNING! The implementation of this subprogram relies on the internal * behavior of #FMLIndexedMessageToMessageCodec (in particular the encode * member). It is probably opening a maintenance issue and should be * replaced eventually by some more solid mechanism. */ public static FMLProxyPacket toPacket (BuildCraftPacket packet, int discriminator) { ByteBuf buf = Unpooled.buffer(); buf.writeByte((byte) discriminator); packet.writeData(buf); return new FMLProxyPacket(buf, DefaultProps.NET_CHANNEL_NAME + "-CORE"); } }