package crazypants.enderio.conduit.oc; import java.util.ArrayList; import java.util.Collection; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import appeng.api.networking.IGridNode; import com.enderio.core.client.render.BoundingBox; import com.enderio.core.client.render.IconUtil; import com.enderio.core.common.util.BlockCoord; import com.enderio.core.common.util.DyeColor; import com.enderio.core.common.util.Log; import cpw.mods.fml.common.Optional.Method; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import li.cil.oc.api.Network; import li.cil.oc.api.network.Environment; import li.cil.oc.api.network.Message; import li.cil.oc.api.network.Node; import li.cil.oc.api.network.SidedEnvironment; import li.cil.oc.api.network.Visibility; import net.minecraft.client.renderer.texture.IIconRegister; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.server.MinecraftServer; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.ChatComponentText; import net.minecraft.util.EnumChatFormatting; import net.minecraft.util.IIcon; import net.minecraft.world.World; import net.minecraftforge.common.util.ForgeDirection; import net.minecraftforge.oredict.OreDictionary; import crazypants.enderio.EnderIO; import crazypants.enderio.conduit.AbstractConduit; import crazypants.enderio.conduit.AbstractConduitNetwork; import crazypants.enderio.conduit.ConduitUtil; import crazypants.enderio.conduit.ConnectionMode; import crazypants.enderio.conduit.IConduit; import crazypants.enderio.conduit.IConduitBundle; import crazypants.enderio.conduit.RaytraceResult; import crazypants.enderio.conduit.TileConduitBundle; import crazypants.enderio.conduit.geom.CollidableComponent; import crazypants.enderio.conduit.geom.ConduitGeometryUtil; import crazypants.enderio.conduit.geom.CollidableCache.CacheKey; import crazypants.enderio.conduit.me.IMEConduit; import crazypants.enderio.conduit.me.MEConduitNetwork; import crazypants.enderio.conduit.redstone.IRedstoneConduit; import crazypants.enderio.conduit.redstone.Signal; import crazypants.enderio.config.Config; import crazypants.enderio.item.PacketConduitProbe; import crazypants.enderio.tool.ToolUtil; public class OCConduit extends AbstractConduit implements IOCConduit { protected OCConduitNetwork network; private Map<ForgeDirection, DyeColor> signalColors = new HashMap<ForgeDirection, DyeColor>(); public static IIcon[] coreTextures; public static IIcon[] longTextures; public OCConduit() { super(); } public OCConduit(int meta) { super(); } public static void initIcons() { IconUtil.addIconProvider(new IconUtil.IIconProvider() { @Override public void registerIcons(IIconRegister register) { coreTextures = new IIcon[2]; longTextures = new IIcon[2]; coreTextures[0] = register.registerIcon(EnderIO.DOMAIN + ":ocConduitCore"); coreTextures[1] = register.registerIcon(EnderIO.DOMAIN + ":ocConduitCoreAnim"); longTextures[0] = register.registerIcon(EnderIO.DOMAIN + ":ocConduit"); longTextures[1] = register.registerIcon(EnderIO.DOMAIN + ":ocConduitAnim"); } @Override public int getTextureType() { return 0; } }); } @Override protected void readTypeSettings(ForgeDirection dir, NBTTagCompound dataRoot) { setSignalColor(dir, DyeColor.values()[dataRoot.getShort("signalColor")]); } @Override protected void writeTypeSettingsToNbt(ForgeDirection dir, NBTTagCompound dataRoot) { dataRoot.setShort("signalColor", (short) getSignalColor(dir).ordinal()); } @Override public DyeColor getSignalColor(ForgeDirection dir) { DyeColor res = signalColors.get(dir); if (res == null) { return DyeColor.SILVER; } return res; } @Override public Collection<CollidableComponent> createCollidables(CacheKey key) { Collection<CollidableComponent> baseCollidables = super.createCollidables(key); if (key.dir == ForgeDirection.UNKNOWN) { return baseCollidables; } BoundingBox bb = ConduitGeometryUtil.instance.createBoundsForConnectionController(key.dir, key.offset); CollidableComponent cc = new CollidableComponent(IOCConduit.class, bb, key.dir, COLOR_CONTROLLER_ID); List<CollidableComponent> result = new ArrayList<CollidableComponent>(); result.addAll(baseCollidables); result.add(cc); return result; } @Override public void writeToNBT(NBTTagCompound nbtRoot) { super.writeToNBT(nbtRoot); if (signalColors.size() >= 0) { byte[] modes = new byte[6]; int i = 0; for (ForgeDirection dir : ForgeDirection.VALID_DIRECTIONS) { DyeColor col = signalColors.get(dir); if (col != null) { modes[i] = (byte) col.ordinal(); } else { modes[i] = -1; } i++; } nbtRoot.setByteArray("signalColors", modes); } } @Override public void readFromNBT(NBTTagCompound nbtRoot, short nbtVersion) { super.readFromNBT(nbtRoot, nbtVersion); signalColors.clear(); byte[] cols = nbtRoot.getByteArray("signalColors"); if (cols != null && cols.length == 6) { int i = 0; for (ForgeDirection dir : ForgeDirection.VALID_DIRECTIONS) { if (cols[i] >= 0) { signalColors.put(dir, DyeColor.values()[cols[i]]); } i++; } } } @Override public void setSignalColor(ForgeDirection dir, DyeColor col) { if (signalColors.get(dir) == col) { return; } disconnectNode(dir); signalColors.put(dir, col); addMissingNodeConnections(); setClientStateDirty(); } @Override public Class<? extends IConduit> getBaseConduitType() { return IOCConduit.class; } @Override public ItemStack createItem() { return new ItemStack(EnderIO.itemOCConduit, 1, 0); } @Override public AbstractConduitNetwork<?, ?> getNetwork() { return network; } @Override public boolean setNetwork(AbstractConduitNetwork<?, ?> network) { if (network == null) { for (ForgeDirection dir : getExternalConnections()) { disconnectNode(dir); } } this.network = (OCConduitNetwork) network; addMissingNodeConnections(); return true; } @Override public IIcon getTextureForState(CollidableComponent component) { int state = Config.enableOCConduitsAnimatedTexture ? 1 : 0; if (component.dir == ForgeDirection.UNKNOWN) { return coreTextures[state]; } else { return longTextures[state]; } } @Override public boolean shouldMirrorTexture() { return !Config.enableOCConduitsAnimatedTexture; } @Override public IIcon getTransmitionTextureForState(CollidableComponent component) { return null; } private static String prettyNode(Node o) { String at = ""; Environment host = o.host(); if (host instanceof TileEntity) { BlockCoord bc = new BlockCoord((TileEntity) host); at = " at " + bc.x + "/" + bc.y + "/" + bc.z; } return host.getClass().getName().replaceFirst("^.*\\.", "") + at; } private static EnumChatFormatting dye2chat(DyeColor dyeColor) { switch (dyeColor) { case BLACK: return EnumChatFormatting.BLACK; case BLUE: return EnumChatFormatting.DARK_BLUE; case BROWN: return EnumChatFormatting.DARK_RED; case CYAN: return EnumChatFormatting.DARK_AQUA; // return EnumChatFormatting.AQUA; case GRAY: return EnumChatFormatting.DARK_GRAY; case GREEN: return EnumChatFormatting.DARK_GREEN; case LIGHT_BLUE: return EnumChatFormatting.BLUE; case LIME: return EnumChatFormatting.GREEN; case MAGENTA: return EnumChatFormatting.LIGHT_PURPLE; case ORANGE: return EnumChatFormatting.GOLD; case PINK: return EnumChatFormatting.LIGHT_PURPLE; case PURPLE: return EnumChatFormatting.DARK_PURPLE; case RED: return EnumChatFormatting.RED; case SILVER: return EnumChatFormatting.GRAY; case WHITE: return EnumChatFormatting.WHITE; case YELLOW: return EnumChatFormatting.YELLOW; default: return null; } } @Override public boolean onBlockActivated(EntityPlayer player, RaytraceResult res, List<RaytraceResult> all) { DyeColor col = DyeColor.getColorFromDye(player.getCurrentEquippedItem()); if (col != null && res.component != null) { setSignalColor(res.component.dir, col); return true; } else if (ConduitUtil.isProbeEquipped(player)) { if (!player.worldObj.isRemote) { BlockCoord bc = getLocation(); if (network != null) { boolean noconnections = true; for (DyeColor color : DyeColor.values()) { if (node(color).neighbors().iterator().hasNext()) { noconnections = false; ChatComponentText coltxt = new ChatComponentText(color.getLocalisedName()); coltxt.getChatStyle().setColor(dye2chat(color)); ChatComponentText chantxt = new ChatComponentText("Channel "); chantxt.appendSibling(coltxt); chantxt.appendText(" at " + bc.x + "/" + bc.y + "/" + bc.z); player.addChatMessage(chantxt); for (Node other : node(color).neighbors()) { player.addChatMessage(new ChatComponentText(" Connected to: " + prettyNode(other))); } } } if (noconnections) { player.addChatMessage(new ChatComponentText("No connections at " + bc.x + "/" + bc.y + "/" + bc.z)); } } else { player.addChatMessage(new ChatComponentText("No network at " + bc.x + "/" + bc.y + "/" + bc.z)); } } return true; } else if (ToolUtil.isToolEquipped(player)) { if (!getBundle().getEntity().getWorldObj().isRemote) { if (res != null && res.component != null) { ForgeDirection connDir = res.component.dir; ForgeDirection faceHit = ForgeDirection.getOrientation(res.movingObjectPosition.sideHit); if (all != null && containsExternalConnection(connDir)) { for (RaytraceResult rtr : all) { if (rtr != null && rtr.component != null && COLOR_CONTROLLER_ID.equals(rtr.component.data)) { setSignalColor(connDir, DyeColor.getNext(getSignalColor(connDir))); return true; } } } if (connDir == ForgeDirection.UNKNOWN || connDir == faceHit) { if (getConnectionMode(faceHit) == ConnectionMode.DISABLED) { setConnectionMode(faceHit, ConnectionMode.IN_OUT); return true; } return ConduitUtil.joinConduits(this, faceHit); } else if (externalConnections.contains(connDir)) { setConnectionMode(connDir, getNextConnectionMode(connDir)); return true; } else if (containsConduitConnection(connDir)) { ConduitUtil.disconectConduits(this, connDir); addMissingNodeConnections(); return true; } } } } return false; } @Override public void setConnectionMode(ForgeDirection dir, ConnectionMode mode) { if (mode == ConnectionMode.DISABLED) { disconnectNode(dir); } super.setConnectionMode(dir, mode); } @Override public void connectionsChanged() { super.connectionsChanged(); addMissingNodeConnections(); } private void addMissingNodeConnections() { BlockCoord loc = getLocation(); if (loc != null && network != null) { World world = getBundle().getWorld(); EnumSet<ForgeDirection> conns = getConnections(); for (DyeColor color : DyeColor.values()) { Set<Node> should = new HashSet<Node>(); for (ForgeDirection direction : conns) { if (getSignalColor(direction) == color) { TileEntity te = getLocation().getLocation(direction).getTileEntity(world); Node other = null; if (te instanceof SidedEnvironment) { other = ((SidedEnvironment) te).sidedNode(direction.getOpposite()); } else if (te instanceof Environment) { other = ((Environment) te).node(); } else { // We have a connection to something we cannot connect to. Should // not happen. Poke debugger in >here< if it does! } if (other != null && other != node(color)) { should.add(other); } } } for (Node other : should) { if (!node(color).isNeighborOf(other)) { node(color).connect(other); } } } } } private void disconnectNode(ForgeDirection direction) { World world = getBundle().getWorld(); TileEntity te = getLocation().getLocation(direction).getTileEntity(world); Node other = null; if (te instanceof SidedEnvironment) { other = ((SidedEnvironment) te).sidedNode(direction.getOpposite()); } else if (te instanceof Environment) { other = ((Environment) te).node(); } if (other != null) { disconnectNode(other, getSignalColor(direction)); } } /** * This will disconnect a node from our network unless it has another * connection to our network. This only works if all the node's blocks are * adjacent to us. Connecting 2 ManagedEnvironments at different locations * won't work well. * * @param other * The node to disconnect from us */ private void disconnectNode(Node other, DyeColor color) { // Um. No. if (other == node(color)) { return; } // Two conduit networks never connect to each other. They join instead. Environment otherHost = other.host(); if (otherHost instanceof OCConduitNetwork && otherHost != network) { node(color).disconnect(other); return; } World world = getBundle().getWorld(); EnumSet<ForgeDirection> conns = getConnections(); // we need to check if that node has another way of connecting to our // network. First find out which of our neighbor(s) it belongs to. May // be just one, may be many. List<TileEntity> toCheck = new ArrayList<TileEntity>(); if (otherHost instanceof TileEntity) { TileEntity otherTe = (TileEntity) otherHost; toCheck.add(otherTe); } else { for (ForgeDirection direction : conns) { if (getSignalColor(direction) == color) { TileEntity te = getLocation().getLocation(direction).getTileEntity(world); Node other2 = null; if (te instanceof SidedEnvironment) { other2 = ((SidedEnvironment) te).sidedNode(direction.getOpposite()); } else if (te instanceof Environment) { other2 = ((Environment) te).node(); } if (other2 == other) { toCheck.add(te); } } } } // Then see if it still has a connection to our node other than through us. boolean stayConnected = false; for (TileEntity otherTe : toCheck) { for (ForgeDirection direction : ForgeDirection.VALID_DIRECTIONS) { if (!stayConnected) { boolean checkThisSide = true; if (otherHost instanceof SidedEnvironment) { checkThisSide = ((SidedEnvironment) otherHost).sidedNode(direction) != null; } if (checkThisSide) { BlockCoord otherPos = new BlockCoord(otherTe); BlockCoord connTo = otherPos.getLocation(direction); if (!connTo.equals(getLocation())) { TileEntity connToTe = connTo.getTileEntity(world); if (connToTe instanceof SidedEnvironment) { stayConnected = ((SidedEnvironment) connToTe).sidedNode(direction.getOpposite()) == node(color); } else if (connToTe instanceof Environment) { stayConnected = ((Environment) connToTe).node() == node(color); } } } } } } if (!stayConnected) { node(color).disconnect(other); } } public EnumSet<ForgeDirection> getConnections() { EnumSet<ForgeDirection> cons = EnumSet.noneOf(ForgeDirection.class); cons.addAll(getConduitConnections()); for (ForgeDirection dir : getExternalConnections()) { if (getConnectionMode(dir) != ConnectionMode.DISABLED) { cons.add(dir); } } return cons; } @Override public ConnectionMode getNextConnectionMode(ForgeDirection dir) { ConnectionMode mode = getConnectionMode(dir); mode = mode == ConnectionMode.IN_OUT ? ConnectionMode.DISABLED : ConnectionMode.IN_OUT; return mode; } @Override public ConnectionMode getPreviousConnectionMode(ForgeDirection dir) { return getNextConnectionMode(dir); } @Override public boolean canConnectToExternal(ForgeDirection direction, boolean ignoreConnectionMode) { TileEntity te = getLocation().getLocation(direction).getTileEntity(getBundle().getWorld()); if (te instanceof SidedEnvironment) { if (getBundle().getWorld().isRemote) { return ((SidedEnvironment) te).canConnect(direction.getOpposite()); } else { return ((SidedEnvironment) te).sidedNode(direction.getOpposite()) != null; } } else if (te instanceof Environment) { return true; } return false; } @Override @Method(modid = "OpenComputersAPI|Network") public Node node() { return network != null ? network.node(DyeColor.SILVER) : null; } @Method(modid = "OpenComputersAPI|Network") public Node node(DyeColor subnet) { return network != null ? network.node(subnet) : null; } @Override @Method(modid = "OpenComputersAPI|Network") public void onConnect(Node node) { } @Override @Method(modid = "OpenComputersAPI|Network") public void onDisconnect(Node node) { } @Override @Method(modid = "OpenComputersAPI|Network") public void onMessage(Message message) { } @Override @Method(modid = "OpenComputersAPI|Network") public Node sidedNode(ForgeDirection side) { return getConnections().contains(side) ? node(getSignalColor(side)) : null; } @Override @SideOnly(Side.CLIENT) @Method(modid = "OpenComputersAPI|Network") public boolean canConnect(ForgeDirection side) { return getConnections().contains(side); } @Override public void invalidate() { } }