package advancedsystemsmanager.tileentities.manager; import advancedsystemsmanager.api.ISystemType; import advancedsystemsmanager.api.tileentities.ITileFactory; import advancedsystemsmanager.api.gui.IManagerButton; import advancedsystemsmanager.api.network.IPacketReader; import advancedsystemsmanager.api.tileentities.*; import advancedsystemsmanager.api.tileentities.IBUDListener; import advancedsystemsmanager.api.tileentities.ITileElement; import advancedsystemsmanager.compatibility.rf.RFCompat; import advancedsystemsmanager.flow.Connection; import advancedsystemsmanager.flow.FlowComponent; import advancedsystemsmanager.flow.elements.Variable; import advancedsystemsmanager.flow.execution.Executor; import advancedsystemsmanager.flow.execution.TriggerHelper; import advancedsystemsmanager.flow.execution.TriggerHelperBUD; import advancedsystemsmanager.flow.execution.TriggerHelperRedstone; import advancedsystemsmanager.flow.menus.*; import advancedsystemsmanager.containers.ContainerManager; import advancedsystemsmanager.client.gui.GuiManager; import advancedsystemsmanager.client.gui.IInterfaceRenderer; import advancedsystemsmanager.client.gui.ManagerButtonList; import advancedsystemsmanager.helpers.BlockHelper; import advancedsystemsmanager.network.ASMPacket; import advancedsystemsmanager.network.PacketHandler; import advancedsystemsmanager.reference.Mods; import advancedsystemsmanager.registry.*; import advancedsystemsmanager.tileentities.TileEntityBUD; import advancedsystemsmanager.tileentities.TileEntityCluster; import advancedsystemsmanager.tileentities.TileEntityElementBase; import advancedsystemsmanager.tileentities.TileEntityQuantumCable; import advancedsystemsmanager.util.SystemCoord; import cofh.api.energy.IEnergyReceiver; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import cpw.mods.fml.common.Optional; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import gnu.trove.map.hash.TIntObjectHashMap; import gnu.trove.map.hash.TLongObjectHashMap; import net.minecraft.client.gui.GuiScreen; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.Container; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.IIcon; import net.minecraftforge.common.util.ForgeDirection; import java.util.*; import static advancedsystemsmanager.api.execution.ICommand.CommandType; @Optional.Interface(iface = "cofh.api.energy.IEnergyReceiver", modid = Mods.COFH_ENERGY) public class TileEntityManager extends TileEntityElementBase implements ITileInterfaceProvider, ITriggerNode, ISystemTypeFilter, IEnergyReceiver, IBUDListener { public static final TriggerHelperRedstone redstoneTrigger = new TriggerHelperRedstone(3, 4); public static final TriggerHelperRedstone redstoneCondition = new TriggerHelperRedstone(1, 2); public static final TriggerHelperBUD budTrigger = new TriggerHelperBUD(); public static final int BUTTON_SRC_X = 0; public static final int BUTTON_SRC_Y = 234; public static final int MAX_CABLE_LENGTH = 128; public static final int MAX_COMPONENT_AMOUNT = 511; public static final int MAX_CONNECTED_INVENTORIES = 1023; public static final String NBT_COMPONENTS = "Components"; private static final String NBT_MAX_ID = "maxID"; private static final String NBT_TIMER = "Timer"; private static final String NBT_VARIABLES = "Variables"; private static final String NBT_SIDES = "Sides"; public static boolean energyCostActive; private List<FlowComponent> triggers; private List<FlowComponent> quickTriggers; public ManagerButtonList buttons; public boolean serverPacket; public boolean variableUpdate; public TIntObjectHashMap<FlowComponent> components; public TIntObjectHashMap<Variable> variables; public TLongObjectHashMap<SystemCoord> network; public FlowComponent selectedGroup; @SideOnly(Side.CLIENT) public IInterfaceRenderer specialRenderer; private Connection currentlyConnecting; private List<FlowComponent> zLevelRenderingList; private int maxID; private int triggerOffset; private boolean firstInventoryUpdate = true; private boolean firstCommandExecution = true; private int timer = 0; private int[] oldPowered = new int[ForgeDirection.VALID_DIRECTIONS.length]; private int[] isPowered = new int[ForgeDirection.VALID_DIRECTIONS.length]; private boolean usingUnlimitedInventories; public TileEntityManager() { zLevelRenderingList = new ArrayList<FlowComponent>(); buttons = ManagerButtonRegistry.getButtons(this); components = new TIntObjectHashMap<FlowComponent>(); triggers = new ArrayList<FlowComponent>(); quickTriggers = new ArrayList<FlowComponent>(); network = new TLongObjectHashMap<SystemCoord>(); variables = Variable.getDefaultVariables(); this.triggerOffset = (((173 + xCoord) << 8 + yCoord) << 8 + zCoord) % 20; } @Override public IIcon getIcon(int side) { return getTileFactory().getIcon(side, 0); } public void removeFlowComponent(int idToRemove, boolean connected) { List<FlowComponent> removed; if (connected) { removed = new ArrayList<FlowComponent>(); List<FlowComponent> toRemove = new ArrayList<FlowComponent>(); FlowComponent.findCluster(toRemove, getFlowItem(idToRemove), null); for (FlowComponent flowComponent : toRemove) { removed.addAll(removeFlowComponents(flowComponent.getId(), true)); } } else { removed = removeFlowComponents(idToRemove, false); } if (!worldObj.isRemote) { triggers.removeAll(removed); } else { zLevelRenderingList.removeAll(removed); } updateVariables(); } public FlowComponent getFlowItem(int i) { return components.get(i); } public List<FlowComponent> removeFlowComponents(int idToRemove, boolean connected) { List<FlowComponent> result = new ArrayList<FlowComponent>(); Queue<Integer> remove = new PriorityQueue<Integer>(); Multimap<FlowComponent, FlowComponent> parents = getParentHierarchy(); remove.add(idToRemove); if (!connected) getFlowItem(idToRemove).deleteConnections(); while (!remove.isEmpty()) { FlowComponent removed = removeComponent(remove.poll()); result.add(removed); for (FlowComponent child : parents.get(removed)) { remove.add(child.getId()); } } return result; } public void updateVariables() { for (Variable variable : variables.valueCollection()) { variable.setDeclaration(null); } for (FlowComponent item : getFlowItems()) { if (item.getType() == CommandRegistry.VARIABLE && item.getConnectionSet() == ConnectionSet.EMPTY) { int selectedVariable = ((MenuVariable)item.getMenus().get(0)).getSelectedVariable(); Variable variable = variables.get(selectedVariable); if (variable != null && !variable.isValid()) { variable.setDeclaration(item); } } } } public Multimap<FlowComponent, FlowComponent> getParentHierarchy() { Multimap<FlowComponent, FlowComponent> result = HashMultimap.create(); for (FlowComponent component : getFlowItems()) { if (component.getParent() != null) result.put(component.getParent(), component); } return result; } private FlowComponent removeComponent(int idToRemove) { FlowComponent removed = components.get(idToRemove); components.remove(idToRemove); if (selectedGroup == removed) { selectedGroup = null; } return removed; } public Collection<FlowComponent> getFlowItems() { return components.valueCollection(); } public TLongObjectHashMap<SystemCoord> getNetwork() { return network; } public List<SystemCoord> getConnectedInventories() { List<SystemCoord> result = new ArrayList<SystemCoord>(network.valueCollection()); Collections.sort(result); return result; } public Connection getCurrentlyConnecting() { return currentlyConnecting; } public void setCurrentlyConnecting(Connection currentlyConnecting) { this.currentlyConnecting = currentlyConnecting; } public void updateFirst() { if (firstCommandExecution) { updateInventories(); updateRedstone(); updateVariables(); firstCommandExecution = false; } } public void activateTrigger(FlowComponent component, EnumSet<ConnectionOption> validTriggerOutputs) { updateFirst(); for (SystemCoord inventory : network.valueCollection()) { if (inventory.getTileEntity().isInvalid()) { updateInventories(); break; } } new Executor(this).executeTriggerCommand(component, validTriggerOutputs); } @Override public Container getContainer(EntityPlayer player) { return new ContainerManager(this, player.inventory); } @SideOnly(Side.CLIENT) @Override public GuiScreen getGui(EntityPlayer player) { return new GuiManager(this, player.inventory); } @Override public boolean readData(ASMPacket packet, EntityPlayer player) { boolean result = false; switch (packet.readByte()) { case PacketHandler.SYNC_ALL: if (worldObj.isRemote) { updateInventories(); int flowControlCount = packet.readVarIntFromBuffer(); components.clear(); getZLevelRenderingList().clear(); for (int i = 0; i < flowControlCount; i++) { NBTTagCompound tagCompound = packet.readNBTTagCompoundFromBuffer(); FlowComponent component = FlowComponent.readFromNBT(this, tagCompound, false); if (component != null) addNewComponent(component); } variables.clear(); int variableCount = packet.readVarIntFromBuffer(); for (int i = 0; i < variableCount; i++) { int colour = packet.readUnsignedMedium(); String name = packet.readStringFromBuffer(); addVariable(new Variable(colour, name)); } for (FlowComponent item : getFlowItems()) { item.linkAfterLoad(); } if (Settings.isAutoCloseGroup()) { selectedGroup = null; } else { while (selectedGroup != null && !findNewSelectedComponent(selectedGroup.getId())) { selectedGroup = selectedGroup.getParent(); } } variableUpdate = true; } break; case PacketHandler.SETTING_MESSAGE: if (!worldObj.isRemote) { boolean val = packet.readBoolean(); if ((val || !isUsingUnlimitedStuff()) && player.capabilities.isCreativeMode) { Settings.setLimitless(this, val); } } break; case PacketHandler.SYNC_COMPONENT: IPacketReader nr = getFlowItem(packet.readVarIntFromBuffer()); result = nr != null && nr.readData(packet); break; case 3: updateInventories(); break; case PacketHandler.NEW_VARIABLE: addNewVariable(new Variable(packet.readMedium())); variableUpdate = true; break; case PacketHandler.BUTTON_CLICK: int buttonId = packet.readByte(); if (buttonId >= 0 && buttonId < buttons.size()) { IManagerButton button = buttons.get(buttonId); result = button.readData(packet); } break; case PacketHandler.SPECIAL_DATA: if (worldObj.isRemote && specialRenderer instanceof IPacketReader) { ((IPacketReader)specialRenderer).readData(packet); } break; } if (!this.worldObj.isRemote) this.markDirty(); return result; } public void updateInventories() { usingUnlimitedInventories = false; SystemCoord[] oldCoordinates = network.values(new SystemCoord[network.size()]); List<SystemCoord> visited = new ArrayList<SystemCoord>(); network.clear(); Queue<SystemCoord> queue = new PriorityQueue<SystemCoord>(); SystemCoord start = new SystemCoord(xCoord, yCoord, zCoord, worldObj, 0); queue.add(start); visited.add(start); addInventory(start); while (!queue.isEmpty()) { SystemCoord element = queue.poll(); for (ForgeDirection direction : ForgeDirection.VALID_DIRECTIONS) { SystemCoord target = new SystemCoord(element, direction); if (!visited.contains(target) && (Settings.isLimitless(this) || network.size() < MAX_CONNECTED_INVENTORIES)) { visited.add(target); TileEntity te = target.getWorldTE(); if ((Settings.isLimitless(this) || element.getDepth() < MAX_CABLE_LENGTH) && BlockHelper.isCable(target.getBlock(), target.getWorld(), target.getX(), target.getY(), target.getZ())) { queue.add(target); if (te instanceof TileEntityQuantumCable) { TileEntityQuantumCable pair = TileEntityQuantumCable.getPairedCable((TileEntityQuantumCable) te); if (pair != null) { SystemCoord paired = new SystemCoord(pair.xCoord, pair.yCoord, pair.zCoord, pair.getWorldObj(), target.getDepth() + 1); queue.add(paired); visited.add(paired); } } } if (te == null) continue; if (te instanceof TileEntityCluster) { for (Map.Entry<ITileFactory, ITileElement> entry : ((TileEntityCluster) te).getPairs()) { ((TileEntityCluster)te).setWorld(entry.getValue()); SystemCoord coord = target.copy(); coord.setClusterType(entry.getKey()); addInventory(coord); } } else { addInventory(target); } } } } if (!firstInventoryUpdate) { for (SystemCoord oldCoordinate : oldCoordinates) { if (oldCoordinate.isValid() && oldCoordinate.getTileEntity() instanceof ISystemListener) { if (!network.containsKey(oldCoordinate.getId())) { ((ISystemListener)oldCoordinate.getTileEntity()).removed(this); for (FlowComponent component : components.valueCollection()) { for (Menu menu : component.getMenus()) { if (menu instanceof MenuContainer) { ((MenuContainer) menu).getSelectedInventories().remove(oldCoordinate.getId()); } } } } } } } firstInventoryUpdate = false; } public List<FlowComponent> getZLevelRenderingList() { return zLevelRenderingList; } public boolean addNewComponent(FlowComponent component) { boolean result = components.put(component.getId(), component) != null; if (component.getType().getCommandType() == CommandType.TRIGGER) triggers.add(component); if (worldObj != null && worldObj.isRemote) zLevelRenderingList.add(0, component); return result; } private void addVariable(Variable variable) { variables.put(variable.colour, variable); } private boolean findNewSelectedComponent(int id) { if (components.containsKey(id)) { selectedGroup = components.get(id); return true; } return false; } public boolean isUsingUnlimitedStuff() { return components.size() > MAX_COMPONENT_AMOUNT || usingUnlimitedInventories; } public void addNewVariable(Variable variable) { variables.put(variable.colour, variable); } private void addInventory(SystemCoord target) { boolean isValidConnection = false; TileEntity te = target.getTileEntity(); target.resetTypes(); for (ISystemType connectionBlockType : SystemTypeRegistry.getTypes()) { if (connectionBlockType.isInstance(this, te)) { if (te instanceof ISystemTypeFilter && !((ISystemTypeFilter)te).isOfType(connectionBlockType)) continue; isValidConnection = true; target.addType(connectionBlockType); } } if (isValidConnection) { if (target.getDepth() >= MAX_CABLE_LENGTH || network.size() >= MAX_CONNECTED_INVENTORIES) { usingUnlimitedInventories = true; } network.put(target.getId(), target); if (target.getTileEntity() instanceof ISystemListener) { ((ISystemListener)target.getTileEntity()).added(this); } } } public void removeVariableDeclaration(int colour, FlowComponent component) { Variable variable = variables.get(colour); if (variable != null && variable.getDeclaration() == component) variable.setDeclaration(null); } public void updateDeclaration(FlowComponent component, int colour) { if (variables.containsKey(colour)) { Variable variable = variables.get(colour); if (!variable.isValid()) variable.setDeclaration(component); } } public int getNextFreeID() { while (components.containsKey(++maxID) || maxID < 0) if (maxID < 0) maxID = 0; return maxID; } public void triggerBUD(TileEntityBUD tileEntityBUD) { for (FlowComponent item : triggers) { if (item.getConnectionSet() == ConnectionSet.BUD) { budTrigger.triggerBUD(item, tileEntityBUD); } } } public FlowComponent getSelectedGroup() { return selectedGroup; } public void setSelectedGroup(FlowComponent selectedGroup) { this.selectedGroup = selectedGroup; } @Override public void readFromTileNBT(NBTTagCompound tag) { super.readFromTileNBT(tag); timer = tag.getByte(NBT_TIMER); byte[] sides = tag.getByteArray(NBT_SIDES); int[] powered = new int[ForgeDirection.VALID_DIRECTIONS.length]; for (int i = 0; i < sides.length; i++) { powered[i] = sides[i]; } oldPowered = isPowered = powered; } @Override public void writeToTileNBT(NBTTagCompound tag) { super.writeToTileNBT(tag); tag.setByte(NBT_TIMER, (byte) (timer % 20)); byte[] sides = new byte[isPowered.length]; for (int i = 0; i < sides.length; i++) { sides[i] = (byte)isPowered[i]; } tag.setByteArray(NBT_SIDES, sides); } @Override public void updateEntity() { serverPacket = false; if (!worldObj.isRemote) { updateFirst(); quickTickTriggers(); if (timer++ % 20 == triggerOffset) { for (FlowComponent item : triggers) { MenuInterval componentMenuInterval = (MenuInterval)item.getMenus().get(TriggerHelper.TRIGGER_INTERVAL_ID); int interval = componentMenuInterval.getInterval(); if (interval == 0) { continue; } item.setCurrentInterval(item.getCurrentInterval() + 1); if (item.getCurrentInterval() >= interval) { item.setCurrentInterval(0); EnumSet<ConnectionOption> valid = EnumSet.of(ConnectionOption.INTERVAL); if (item.getConnectionSet() == ConnectionSet.REDSTONE) { redstoneTrigger.onTrigger(item, valid); } else if (item.getConnectionSet() == ConnectionSet.BUD) { budTrigger.onTrigger(item, valid); } activateTrigger(item, valid); } } } } } public void quickTickTriggers() { for (Iterator<FlowComponent> itr = quickTriggers.iterator(); itr.hasNext(); ) { MenuTriggered toTrigger = (MenuTriggered)itr.next().getMenus().get(6); if (toTrigger.isVisible()) { toTrigger.tick(); if (toTrigger.remove()) itr.remove(); } else { itr.remove(); } } } public void addQuickTrigger(FlowComponent component) { quickTriggers.add(component); } @Override public void writeItemNBT(NBTTagCompound tag) { super.writeItemNBT(tag); tag.setInteger(NBT_MAX_ID, maxID); NBTTagList components = new NBTTagList(); for (FlowComponent item : getFlowItems()) { NBTTagCompound component = new NBTTagCompound(); item.writeToNBT(component, false); components.appendTag(component); } tag.setTag(NBT_COMPONENTS, components); NBTTagList variablesTag = new NBTTagList(); for (Variable variable : variables.valueCollection()) { NBTTagCompound variableTag = new NBTTagCompound(); variable.writeToNBT(variableTag); variablesTag.appendTag(variableTag); } tag.setTag(NBT_VARIABLES, variablesTag); } @Override public void readItemNBT(NBTTagCompound tag) { super.readItemNBT(tag); maxID = tag.getInteger(NBT_MAX_ID); NBTTagList components = tag.getTagList(NBT_COMPONENTS, 10); for (int i = 0; i < components.tagCount(); i++) { NBTTagCompound component = components.getCompoundTagAt(i); FlowComponent flowComponent = FlowComponent.readFromNBT(this, component, false); if (flowComponent != null) addNewComponent(flowComponent); } for (FlowComponent item : getFlowItems()) { item.linkAfterLoad(); } NBTTagList variablesTag = tag.getTagList(NBT_VARIABLES, 10); for (int i = 0; i < variablesTag.tagCount(); i++) { NBTTagCompound variableTag = variablesTag.getCompoundTagAt(i); addVariable(new Variable(variableTag)); } } public int getNextColour(int colour, int direction) { List<Variable> variables = new ArrayList<Variable>(getVariables()); Collections.sort(variables); for (int i = 0; i < variables.size(); i++) { if (variables.get(i).colour == colour) { int returnVar = (i + direction) % variables.size(); if (returnVar < 0) returnVar += variables.size(); return variables.get(returnVar).colour; } } return variables.get(0).colour; } public Collection<Variable> getVariables() { return variables.valueCollection(); } public String getUniqueComponentName(FlowComponent component) { String name = getComponentName(component); int modifier = 0; for (int i : components.keys()) { FlowComponent other = components.get(i); if (getComponentName(other).equals(name)) modifier++; } if (modifier > 0) name += " [" + modifier + "]"; return name; } public String getComponentName(FlowComponent component) { String name = component.getComponentName(); if (name == null) name = component.getType().getName(); return name; } public boolean hasInventory(long key) { return network.containsKey(key) && network.get(key).isValid(); } public SystemCoord getInventory(long selected) { return network.get(selected); } @Override public boolean writeData(ASMPacket packet) { packet.writeByte(PacketHandler.SYNC_ALL); packet.writeVarIntToBuffer(components.size()); for (FlowComponent flowComponent : components.valueCollection()) { NBTTagCompound tagCompound = new NBTTagCompound(); flowComponent.writeToNBT(tagCompound, false); packet.writeNBTTagCompoundToBuffer(tagCompound); } packet.writeVarIntToBuffer(variables.size()); for (Variable variable : variables.valueCollection()) { packet.writeMedium(variable.colour); packet.writeStringToBuffer(variable.getNameFromColor()); } return true; } public Variable getVariable(int selectedVariable) { return variables.get(selectedVariable); } public void updateRedstone() { isPowered = new int[isPowered.length]; for (int i = 0; i < isPowered.length; i++) { ForgeDirection direction = ForgeDirection.VALID_DIRECTIONS[i]; isPowered[i] = worldObj.getIndirectPowerLevelTo(direction.offsetX + this.xCoord, direction.offsetY + this.yCoord, direction.offsetZ + this.zCoord, i); } triggerRedstone(this); oldPowered = isPowered; } public void triggerRedstone(ITriggerNode inputTrigger) { for (FlowComponent item : triggers) { if (item.getConnectionSet() == ConnectionSet.REDSTONE) { redstoneTrigger.onRedstoneTrigger(item, inputTrigger); } } } @Override public int[] getData() { return isPowered; } @Override public int[] getOldData() { return oldPowered; } @Override @Optional.Method(modid = Mods.COFH_ENERGY) public int receiveEnergy(ForgeDirection forgeDirection, int amount, boolean simulate) { return 0; } @Override @Optional.Method(modid = Mods.COFH_ENERGY) public int getEnergyStored(ForgeDirection forgeDirection) { return 0; } @Override @Optional.Method(modid = Mods.COFH_ENERGY) public int getMaxEnergyStored(ForgeDirection forgeDirection) { return 0; } @Override @Optional.Method(modid = Mods.COFH_ENERGY) public boolean canConnectEnergy(ForgeDirection forgeDirection) { return requiresPower(); } public boolean requiresPower() { return energyCostActive && !Settings.isLimitless(this); } @Override public boolean isOfType(ISystemType type) { return type != RFCompat.RF_RECEIVER || requiresPower(); } @Override public void validate() { super.validate(); firstInventoryUpdate = true; firstCommandExecution = true; } @Override public void onNeighborBlockChange() { updateRedstone(); updateInventories(); } @Override public void invalidate() { super.invalidate(); } @Override public void onChunkUnload() { super.onChunkUnload(); } }