diff options
Diffstat (limited to 'src/main/java/gregtech/api/graphs')
15 files changed, 1008 insertions, 0 deletions
diff --git a/src/main/java/gregtech/api/graphs/GenerateNodeMap.java b/src/main/java/gregtech/api/graphs/GenerateNodeMap.java new file mode 100644 index 0000000000..7289f0faad --- /dev/null +++ b/src/main/java/gregtech/api/graphs/GenerateNodeMap.java @@ -0,0 +1,202 @@ +package gregtech.api.graphs; + +import static gregtech.api.enums.GT_Values.ALL_VALID_SIDES; + +import java.util.ArrayList; +import java.util.HashSet; + +import net.minecraft.tileentity.TileEntity; +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.graphs.consumers.ConsumerNode; +import gregtech.api.graphs.paths.NodePath; +import gregtech.api.metatileentity.BaseMetaPipeEntity; +import gregtech.api.metatileentity.MetaPipeEntity; + +// generates the node map +public abstract class GenerateNodeMap { + + // clearing the node map to make sure it is gone on reset + public static void clearNodeMap(Node aNode, int aReturnNodeValue) { + if (aNode.mTileEntity instanceof BaseMetaPipeEntity tPipe) { + tPipe.setNode(null); + tPipe.setNodePath(null); + if (aNode.mSelfPath != null) { + aNode.mSelfPath.clearPath(); + aNode.mSelfPath = null; + } + } + for (byte side : ALL_VALID_SIDES) { + final NodePath tPath = aNode.mNodePaths[side]; + if (tPath != null) { + tPath.clearPath(); + aNode.mNodePaths[side] = null; + } + final Node tNextNode = aNode.mNeighbourNodes[side]; + if (tNextNode == null) continue; + if (tNextNode.mNodeValue != aReturnNodeValue) clearNodeMap(tNextNode, aNode.mNodeValue); + aNode.mNeighbourNodes[side] = null; + } + } + + // get how many connections the pipe have + private static int getNumberOfConnections(MetaPipeEntity aPipe) { + int tCons = 0; + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + if (aPipe.isConnectedAtSide(side)) tCons++; + } + return tCons; + } + + // gets the next node + protected void generateNextNode(BaseMetaPipeEntity aPipe, Node aPipeNode, ForgeDirection aInvalidSide, + int aNextNodeValue, ArrayList<ConsumerNode> tConsumers, HashSet<Node> tNodeMap) { + final MetaPipeEntity tMetaPipe = (MetaPipeEntity) aPipe.getMetaTileEntity(); + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + if (side == aInvalidSide) { + continue; + } + final TileEntity tNextTileEntity = aPipe.getTileEntityAtSide(side); + if (tNextTileEntity == null || (tMetaPipe != null && !tMetaPipe.isConnectedAtSide(side))) continue; + final ArrayList<MetaPipeEntity> tNewPipes = new ArrayList<>(); + final Pair nextTileEntity = getNextValidTileEntity(tNextTileEntity, tNewPipes, side, tNodeMap); + if (nextTileEntity != null) { + final Node tNextNode = generateNode( + nextTileEntity.mTileEntity, + aPipeNode, + aNextNodeValue + 1, + tNewPipes, + nextTileEntity.mSide, + tConsumers, + tNodeMap); + if (tNextNode != null) { + final int i = side.ordinal(); + aNextNodeValue = tNextNode.mHighestNodeValue; + aPipeNode.mHighestNodeValue = tNextNode.mHighestNodeValue; + aPipeNode.mNeighbourNodes[i] = tNextNode; + aPipeNode.mNodePaths[i] = aPipeNode.returnValues.mReturnPath; + aPipeNode.locks[i] = aPipeNode.returnValues.returnLock; + aPipeNode.mNodePaths[i].reloadLocks(); + } + } + } + aPipe.reloadLocks(); + } + + // on a valid tile entity create a new node + protected Node generateNode(TileEntity aTileEntity, Node aPreviousNode, int aNextNodeValue, + ArrayList<MetaPipeEntity> aPipes, ForgeDirection side, ArrayList<ConsumerNode> aConsumers, + HashSet<Node> aNodeMap) { + if (aTileEntity.isInvalid()) return null; + final ForgeDirection oppositeSide = side.getOpposite(); + final ForgeDirection tInvalidSide = aPreviousNode == null ? ForgeDirection.UNKNOWN : oppositeSide; + Node tThisNode = null; + if (isPipe(aTileEntity)) { + final BaseMetaPipeEntity tPipe = (BaseMetaPipeEntity) aTileEntity; + final MetaPipeEntity tMetaPipe = (MetaPipeEntity) tPipe.getMetaTileEntity(); + final int tConnections = getNumberOfConnections(tMetaPipe); + final Node tPipeNode; + if (tConnections == 1) { + tPipeNode = getEmptyNode(aNextNodeValue, oppositeSide, aTileEntity, aConsumers); + if (tPipeNode == null) return null; + } else { + tPipeNode = getPipeNode(aNextNodeValue, oppositeSide, aTileEntity, aConsumers); + } + tPipe.setNode(tPipeNode); + aNodeMap.add(tPipeNode); + tPipeNode.mSelfPath = getNewPath(new MetaPipeEntity[] { tMetaPipe }); + tThisNode = tPipeNode; + if (tInvalidSide != ForgeDirection.UNKNOWN) { + final int iInvalid = tInvalidSide.ordinal(); + tPipeNode.mNeighbourNodes[iInvalid] = aPreviousNode; + tPipeNode.mNodePaths[iInvalid] = getNewPath(aPipes.toArray(new MetaPipeEntity[0])); + final Lock lock = new Lock(); + tPipeNode.mNodePaths[oppositeSide.ordinal()].lock = lock; + tPipeNode.locks[iInvalid] = lock; + aPreviousNode.returnValues.mReturnPath = tPipeNode.mNodePaths[iInvalid]; + aPreviousNode.returnValues.returnLock = lock; + } + if (tConnections > 1) + generateNextNode(tPipe, tPipeNode, tInvalidSide, aNextNodeValue, aConsumers, aNodeMap); + } else if (addConsumer(aTileEntity, oppositeSide, aNextNodeValue, aConsumers)) { + final int oppositeSideOrdinal = oppositeSide.ordinal(); + final ConsumerNode tConsumeNode = aConsumers.get(aConsumers.size() - 1); + tConsumeNode.mNeighbourNodes[oppositeSideOrdinal] = aPreviousNode; + tConsumeNode.mNodePaths[oppositeSideOrdinal] = getNewPath(aPipes.toArray(new MetaPipeEntity[0])); + final Lock lock = new Lock(); + tConsumeNode.mNodePaths[oppositeSideOrdinal].lock = lock; + aPreviousNode.returnValues.mReturnPath = tConsumeNode.mNodePaths[oppositeSideOrdinal]; + aPreviousNode.returnValues.returnLock = lock; + tThisNode = tConsumeNode; + } + return tThisNode; + } + + // go over the pipes until we see a valid tile entity that needs a node + protected Pair getNextValidTileEntity(TileEntity aTileEntity, ArrayList<MetaPipeEntity> aPipes, ForgeDirection side, + HashSet<Node> aNodeMap) { + if (isPipe(aTileEntity)) { + final BaseMetaPipeEntity tPipe = (BaseMetaPipeEntity) aTileEntity; + final MetaPipeEntity tMetaPipe = (MetaPipeEntity) tPipe.getMetaTileEntity(); + final Node tNode = tPipe.getNode(); + if (tNode != null) { + if (aNodeMap.contains(tNode)) return null; + } + final int tConnections = getNumberOfConnections(tMetaPipe); + if (tConnections == 2) { + final ForgeDirection tSideOp = side.getOpposite(); + for (final ForgeDirection s : ForgeDirection.VALID_DIRECTIONS) { + if (s == tSideOp || !(tMetaPipe.isConnectedAtSide(s))) continue; + final TileEntity tNewTileEntity = tPipe.getTileEntityAtSide(s); + if (tNewTileEntity == null) continue; + if (isPipe(tNewTileEntity)) { + aPipes.add(tMetaPipe); + return getNextValidTileEntity(tNewTileEntity, aPipes, s, aNodeMap); + } else { + return new Pair(aTileEntity, s); + } + } + } else { + return new Pair(aTileEntity, side); + } + } else { + return new Pair(aTileEntity, side); + } + return null; + } + + // check if the tile entity is the correct pipe + protected boolean isPipe(TileEntity aTileEntity) { + return aTileEntity instanceof BaseMetaPipeEntity; + } + + // checks if the tile entity is a consumer and add to the list + protected abstract boolean addConsumer(TileEntity aTileEntity, ForgeDirection side, int aNodeValue, + ArrayList<ConsumerNode> aConsumers); + + // get correct pathClass that you need for your node network + protected abstract NodePath getNewPath(MetaPipeEntity[] aPipes); + + // used for if you need to use dead ends for something can be null + protected Node getEmptyNode(int aNodeValue, ForgeDirection side, TileEntity aTileEntity, + ArrayList<ConsumerNode> aConsumers) { + return null; + } + + // get correct node type you need for your network + protected Node getPipeNode(int aNodeValue, ForgeDirection side, TileEntity aTileEntity, + ArrayList<ConsumerNode> aConsumers) { + return new Node(aNodeValue, aTileEntity, aConsumers); + } + + private static class Pair { + + public ForgeDirection mSide; + public TileEntity mTileEntity; + + public Pair(TileEntity aTileEntity, ForgeDirection side) { + this.mTileEntity = aTileEntity; + this.mSide = side; + } + } +} diff --git a/src/main/java/gregtech/api/graphs/GenerateNodeMapPower.java b/src/main/java/gregtech/api/graphs/GenerateNodeMapPower.java new file mode 100644 index 0000000000..95f9aee32d --- /dev/null +++ b/src/main/java/gregtech/api/graphs/GenerateNodeMapPower.java @@ -0,0 +1,104 @@ +package gregtech.api.graphs; + +import java.util.ArrayList; +import java.util.HashSet; + +import net.minecraft.tileentity.TileEntity; +import net.minecraftforge.common.util.ForgeDirection; + +import cofh.api.energy.IEnergyReceiver; +import gregtech.api.GregTech_API; +import gregtech.api.graphs.consumers.ConsumerNode; +import gregtech.api.graphs.consumers.EmptyPowerConsumer; +import gregtech.api.graphs.consumers.NodeEnergyConnected; +import gregtech.api.graphs.consumers.NodeEnergyReceiver; +import gregtech.api.graphs.consumers.NodeEnergySink; +import gregtech.api.graphs.consumers.NodeGTBaseMetaTile; +import gregtech.api.graphs.paths.NodePath; +import gregtech.api.graphs.paths.PowerNodePath; +import gregtech.api.interfaces.tileentity.IEnergyConnected; +import gregtech.api.metatileentity.BaseMetaPipeEntity; +import gregtech.api.metatileentity.BaseMetaTileEntity; +import gregtech.api.metatileentity.MetaPipeEntity; +import gregtech.api.metatileentity.implementations.GT_MetaPipeEntity_Cable; +import ic2.api.energy.tile.IEnergySink; + +// node map generator for power distribution +public class GenerateNodeMapPower extends GenerateNodeMap { + + public GenerateNodeMapPower(BaseMetaPipeEntity aTileEntity) { + generateNode(aTileEntity, null, 1, null, ForgeDirection.UNKNOWN, new ArrayList<>(), new HashSet<>()); + } + + @Override + protected boolean isPipe(TileEntity aTileEntity) { + return super.isPipe(aTileEntity) + && ((BaseMetaPipeEntity) aTileEntity).getMetaTileEntity() instanceof GT_MetaPipeEntity_Cable; + } + + @Override + protected boolean addConsumer(TileEntity aTileEntity, ForgeDirection side, int aNodeValue, + ArrayList<ConsumerNode> aConsumers) { + if (aTileEntity instanceof BaseMetaTileEntity tBaseTileEntity) { + if (tBaseTileEntity.inputEnergyFrom(side, false)) { + ConsumerNode tConsumerNode = new NodeGTBaseMetaTile(aNodeValue, tBaseTileEntity, side, aConsumers); + aConsumers.add(tConsumerNode); + return true; + } + + } else if (aTileEntity instanceof IEnergyConnected tTileEntity) { + if (tTileEntity.inputEnergyFrom(side, false)) { + ConsumerNode tConsumerNode = new NodeEnergyConnected(aNodeValue, tTileEntity, side, aConsumers); + aConsumers.add(tConsumerNode); + return true; + } + } else if (aTileEntity instanceof IEnergySink sink) { + // ic2 wants the tilentity next to it of that side not going to add a bunch of arguments just for ic2 + // crossborder checks to not load chuncks just to make sure + int dX = aTileEntity.xCoord + side.offsetX; + int dY = aTileEntity.yCoord + side.offsetY; + int dZ = aTileEntity.zCoord + side.offsetZ; + boolean crossesChuncks = dX >> 4 != aTileEntity.xCoord >> 4 || dZ >> 4 != aTileEntity.zCoord >> 4; + TileEntity tNextTo = null; + if (!crossesChuncks || !aTileEntity.getWorldObj() + .blockExists(dX, dY, dZ)) + tNextTo = aTileEntity.getWorldObj() + .getTileEntity(dX, dY, dZ); + + if (sink.acceptsEnergyFrom(tNextTo, side)) { + ConsumerNode tConsumerNode = new NodeEnergySink( + aNodeValue, + (IEnergySink) aTileEntity, + side, + aConsumers); + aConsumers.add(tConsumerNode); + return true; + } + } else if (GregTech_API.mOutputRF && aTileEntity instanceof IEnergyReceiver receiver) { + ConsumerNode tConsumerNode = new NodeEnergyReceiver(aNodeValue, receiver, side, aConsumers); + aConsumers.add(tConsumerNode); + return true; + } + return false; + } + + @Override + protected NodePath getNewPath(MetaPipeEntity[] aPipes) { + return new PowerNodePath(aPipes); + } + + // used to apply voltage on dead ends + @Override + protected Node getEmptyNode(int aNodeValue, ForgeDirection side, TileEntity aTileEntity, + ArrayList<ConsumerNode> aConsumers) { + ConsumerNode tNode = new EmptyPowerConsumer(aNodeValue, aTileEntity, side, aConsumers); + aConsumers.add(tNode); + return tNode; + } + + @Override + protected Node getPipeNode(int aNodeValue, ForgeDirection side, TileEntity aTileEntity, + ArrayList<ConsumerNode> aConsumers) { + return new PowerNode(aNodeValue, aTileEntity, aConsumers); + } +} diff --git a/src/main/java/gregtech/api/graphs/Lock.java b/src/main/java/gregtech/api/graphs/Lock.java new file mode 100644 index 0000000000..d3c8c49169 --- /dev/null +++ b/src/main/java/gregtech/api/graphs/Lock.java @@ -0,0 +1,38 @@ +package gregtech.api.graphs; + +import java.util.ArrayList; + +import net.minecraft.tileentity.TileEntity; + +public class Lock { + + protected ArrayList<TileEntity> tiles = new ArrayList<>(); + + public void addTileEntity(TileEntity tileEntity) { + int i = contains(tileEntity); + if (i == -1) { + tiles.add(tileEntity); + } + } + + public void removeTileEntity(TileEntity tileEntity) { + int i = contains(tileEntity); + if (i > -1) { + tiles.remove(i); + } + } + + public boolean isLocked() { + return !tiles.isEmpty(); + } + + // i want to check for the exact object not equals + protected int contains(TileEntity tileEntity) { + for (int i = 0; i < tiles.size(); i++) { + if (tiles.get(i) == tileEntity) { + return i; + } + } + return -1; + } +} diff --git a/src/main/java/gregtech/api/graphs/Node.java b/src/main/java/gregtech/api/graphs/Node.java new file mode 100644 index 0000000000..9afe009d3e --- /dev/null +++ b/src/main/java/gregtech/api/graphs/Node.java @@ -0,0 +1,40 @@ +package gregtech.api.graphs; + +import java.util.ArrayList; + +import net.minecraft.server.MinecraftServer; +import net.minecraft.tileentity.TileEntity; + +import gregtech.api.graphs.consumers.ConsumerNode; +import gregtech.api.graphs.paths.NodePath; + +// base Node class +public class Node { + + public Node(int aNodeValue, TileEntity aTileEntity, ArrayList<ConsumerNode> aConsumers) { + this.mNodeValue = aNodeValue; + this.mTileEntity = aTileEntity; + this.mConsumers = aConsumers; + mHighestNodeValue = aNodeValue; + // you don't want to generate map multiple times in the same tick + mCreationTime = MinecraftServer.getServer() + .getTickCounter(); + } + + public final TileEntity mTileEntity; + public Node[] mNeighbourNodes = new Node[6]; + public NodePath[] mNodePaths = new NodePath[6]; + public Lock[] locks = new Lock[6]; + public ReturnPair returnValues = new ReturnPair(); + public NodePath mSelfPath; + public ArrayList<ConsumerNode> mConsumers; + public int mCreationTime; + public int mNodeValue; + public int mHighestNodeValue; + + public static class ReturnPair { + + public NodePath mReturnPath; + public Lock returnLock; + } +} diff --git a/src/main/java/gregtech/api/graphs/NodeList.java b/src/main/java/gregtech/api/graphs/NodeList.java new file mode 100644 index 0000000000..899384b3d4 --- /dev/null +++ b/src/main/java/gregtech/api/graphs/NodeList.java @@ -0,0 +1,22 @@ +package gregtech.api.graphs; + +// keep track on which node is being looked for across the recursive functions +public class NodeList { + + Node[] mNodes; + int mCounter = 0; + + public NodeList(Node[] mNodes) { + this.mNodes = mNodes; + } + + Node getNextNode() { + if (++mCounter < mNodes.length) return mNodes[mCounter]; + else return null; + } + + Node getNode() { + if (mCounter < mNodes.length) return mNodes[mCounter]; + else return null; + } +} diff --git a/src/main/java/gregtech/api/graphs/PowerNode.java b/src/main/java/gregtech/api/graphs/PowerNode.java new file mode 100644 index 0000000000..75a8e8d73b --- /dev/null +++ b/src/main/java/gregtech/api/graphs/PowerNode.java @@ -0,0 +1,17 @@ +package gregtech.api.graphs; + +import java.util.ArrayList; + +import net.minecraft.tileentity.TileEntity; + +import gregtech.api.graphs.consumers.ConsumerNode; + +// base node for power networks +public class PowerNode extends Node { + + public boolean mHadVoltage = false; + + public PowerNode(int aNodeValue, TileEntity aTileEntity, ArrayList<ConsumerNode> aConsumers) { + super(aNodeValue, aTileEntity, aConsumers); + } +} diff --git a/src/main/java/gregtech/api/graphs/PowerNodes.java b/src/main/java/gregtech/api/graphs/PowerNodes.java new file mode 100644 index 0000000000..98d35e2971 --- /dev/null +++ b/src/main/java/gregtech/api/graphs/PowerNodes.java @@ -0,0 +1,182 @@ +package gregtech.api.graphs; + +import gregtech.api.graphs.consumers.ConsumerNode; +import gregtech.api.graphs.paths.PowerNodePath; + +/* + * look for and power node that need power how this works a node only contains nodes that has a higher value then it + * self except for 1 which is the return node this node also contains the highest known node value of its network this + * network only includes nodes that have a higher value then it self so it does not know the highest known value that + * the return node knows with these rules we can know for the target node to be in the network of a node, the target + * node must have a value no less than the node we are looking and no greater than the highest value that node knows + * this way we don't have to go over the entire network to look for it we also hold a list of all consumers so we can + * check before looking if that consumer actually needs power and only look for nodes that actually need power + */ +public class PowerNodes { + + // check if the looked for node is next to or get the next node that is closer to it + public static long powerNode(Node aCurrentNode, Node aPreviousNode, NodeList aConsumers, long aVoltage, + long aMaxAmps) { + long tAmpsUsed = 0; + ConsumerNode tConsumer = (ConsumerNode) aConsumers.getNode(); + int tLoopProtection = 0; + while (tConsumer != null) { + int tTargetNodeValue = tConsumer.mNodeValue; + // if the target node has a value less then the current node + if (tTargetNodeValue < aCurrentNode.mNodeValue || tTargetNodeValue > aCurrentNode.mHighestNodeValue) { + for (int j = 0; j < 6; j++) { + final Node tNextNode = aCurrentNode.mNeighbourNodes[j]; + if (tNextNode != null && tNextNode.mNodeValue < aCurrentNode.mNodeValue) { + if (tNextNode.mNodeValue == tConsumer.mNodeValue) { + tAmpsUsed += processNodeInject(aCurrentNode, tConsumer, j, aMaxAmps - tAmpsUsed, aVoltage); + tConsumer = (ConsumerNode) aConsumers.getNextNode(); + } else { + if (aPreviousNode == tNextNode) return tAmpsUsed; + tAmpsUsed += processNextNode( + aCurrentNode, + tNextNode, + aConsumers, + j, + aMaxAmps - tAmpsUsed, + aVoltage); + tConsumer = (ConsumerNode) aConsumers.getNode(); + } + break; + } + } + } else { + // if the target node has a node value greater then current node value + for (int side = 5; side > -1; side--) { + final Node tNextNode = aCurrentNode.mNeighbourNodes[side]; + if (tNextNode == null) continue; + if (tNextNode.mNodeValue > aCurrentNode.mNodeValue && tNextNode.mNodeValue < tTargetNodeValue) { + if (tNextNode == aPreviousNode) return tAmpsUsed; + tAmpsUsed += processNextNodeAbove( + aCurrentNode, + tNextNode, + aConsumers, + side, + aMaxAmps - tAmpsUsed, + aVoltage); + tConsumer = (ConsumerNode) aConsumers.getNode(); + break; + } else if (tNextNode.mNodeValue == tTargetNodeValue) { + tAmpsUsed += processNodeInject(aCurrentNode, tConsumer, side, aMaxAmps - tAmpsUsed, aVoltage); + tConsumer = (ConsumerNode) aConsumers.getNextNode(); + break; + } + } + } + if (aMaxAmps - tAmpsUsed <= 0) { + return tAmpsUsed; + } + if (tLoopProtection++ > 20) { + throw new NullPointerException("infinite loop in powering nodes "); + } + } + return tAmpsUsed; + } + + // checking if target node is next to it or has a higher value then current node value + // these functions are different to either go down or up the stack + protected static long powerNodeAbove(Node aCurrentNode, Node aPreviousNode, NodeList aConsumers, long aVoltage, + long aMaxAmps) { + long tAmpsUsed = 0; + int tLoopProtection = 0; + ConsumerNode tConsumer = (ConsumerNode) aConsumers.getNode(); + while (tConsumer != null) { + int tTargetNodeValue = tConsumer.mNodeValue; + if (tTargetNodeValue > aCurrentNode.mHighestNodeValue || tTargetNodeValue < aCurrentNode.mNodeValue) { + return tAmpsUsed; + } else { + for (int side = 5; side > -1; side--) { + final Node tNextNode = aCurrentNode.mNeighbourNodes[side]; + if (tNextNode == null) continue; + if (tNextNode.mNodeValue > aCurrentNode.mNodeValue && tNextNode.mNodeValue < tTargetNodeValue) { + if (tNextNode == aPreviousNode) return tAmpsUsed; + tAmpsUsed += processNextNodeAbove( + aCurrentNode, + tNextNode, + aConsumers, + side, + aMaxAmps - tAmpsUsed, + aVoltage); + tConsumer = (ConsumerNode) aConsumers.getNode(); + break; + } else if (tNextNode.mNodeValue == tTargetNodeValue) { + tAmpsUsed += processNodeInject(aCurrentNode, tConsumer, side, aMaxAmps - tAmpsUsed, aVoltage); + tConsumer = (ConsumerNode) aConsumers.getNextNode(); + break; + } + } + } + if (aMaxAmps - tAmpsUsed <= 0) { + return tAmpsUsed; + } + if (tLoopProtection++ > 20) { + throw new NullPointerException("infinite loop in powering nodes "); + } + } + return tAmpsUsed; + } + + protected static long processNextNode(Node aCurrentNode, Node aNextNode, NodeList aConsumers, int ordinalSide, + long aMaxAmps, long aVoltage) { + if (aCurrentNode.locks[ordinalSide].isLocked()) { + aConsumers.getNextNode(); + return 0; + } + final PowerNodePath tPath = (PowerNodePath) aCurrentNode.mNodePaths[ordinalSide]; + final PowerNodePath tSelfPath = (PowerNodePath) aCurrentNode.mSelfPath; + long tVoltLoss = 0; + if (tSelfPath != null) { + tVoltLoss += tSelfPath.getLoss(); + tSelfPath.applyVoltage(aVoltage, false); + } + tPath.applyVoltage(aVoltage - tVoltLoss, true); + tVoltLoss += tPath.getLoss(); + long tAmps = powerNode(aNextNode, aCurrentNode, aConsumers, aVoltage - tVoltLoss, aMaxAmps); + tPath.addAmps(tAmps); + if (tSelfPath != null) tSelfPath.addAmps(tAmps); + return tAmps; + } + + protected static long processNextNodeAbove(Node aCurrentNode, Node aNextNode, NodeList aConsumers, int ordinalSide, + long aMaxAmps, long aVoltage) { + if (aCurrentNode.locks[ordinalSide].isLocked()) { + aConsumers.getNextNode(); + return 0; + } + final PowerNodePath tPath = (PowerNodePath) aCurrentNode.mNodePaths[ordinalSide]; + final PowerNodePath tSelfPath = (PowerNodePath) aCurrentNode.mSelfPath; + long tVoltLoss = 0; + if (tSelfPath != null) { + tVoltLoss += tSelfPath.getLoss(); + tSelfPath.applyVoltage(aVoltage, false); + } + tPath.applyVoltage(aVoltage - tVoltLoss, true); + tVoltLoss += tPath.getLoss(); + long tAmps = powerNodeAbove(aNextNode, aCurrentNode, aConsumers, aVoltage - tVoltLoss, aMaxAmps); + tPath.addAmps(tAmps); + if (tSelfPath != null) tSelfPath.addAmps(tAmps); + return tAmps; + } + + protected static long processNodeInject(Node aCurrentNode, ConsumerNode aConsumer, int ordinalSide, long aMaxAmps, + long aVoltage) { + if (aCurrentNode.locks[ordinalSide].isLocked()) return 0; + final PowerNodePath tPath = (PowerNodePath) aCurrentNode.mNodePaths[ordinalSide]; + final PowerNodePath tSelfPath = (PowerNodePath) aCurrentNode.mSelfPath; + long tVoltLoss = 0; + if (tSelfPath != null) { + tVoltLoss += tSelfPath.getLoss(); + tSelfPath.applyVoltage(aVoltage, false); + } + tPath.applyVoltage(aVoltage - tVoltLoss, true); + tVoltLoss += tPath.getLoss(); + long tAmps = aConsumer.injectEnergy(aVoltage - tVoltLoss, aMaxAmps); + tPath.addAmps(tAmps); + if (tSelfPath != null) tSelfPath.addAmps(tAmps); + return tAmps; + } +} diff --git a/src/main/java/gregtech/api/graphs/consumers/ConsumerNode.java b/src/main/java/gregtech/api/graphs/consumers/ConsumerNode.java new file mode 100644 index 0000000000..392fe74a32 --- /dev/null +++ b/src/main/java/gregtech/api/graphs/consumers/ConsumerNode.java @@ -0,0 +1,30 @@ +package gregtech.api.graphs.consumers; + +import java.util.ArrayList; + +import net.minecraft.tileentity.TileEntity; +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.graphs.Node; + +/** + * A node attached to a {@code TileEntity} that can consume stuff from the network. + */ +public class ConsumerNode extends Node { + + public ForgeDirection mSide; + + public ConsumerNode(int aNodeValue, TileEntity aTileEntity, ForgeDirection side, + ArrayList<ConsumerNode> aConsumers) { + super(aNodeValue, aTileEntity, aConsumers); + this.mSide = side; + } + + public boolean needsEnergy() { + return !mTileEntity.isInvalid(); + } + + public int injectEnergy(long aVoltage, long aMaxAmps) { + return 0; + } +} diff --git a/src/main/java/gregtech/api/graphs/consumers/EmptyPowerConsumer.java b/src/main/java/gregtech/api/graphs/consumers/EmptyPowerConsumer.java new file mode 100644 index 0000000000..48cf330bdb --- /dev/null +++ b/src/main/java/gregtech/api/graphs/consumers/EmptyPowerConsumer.java @@ -0,0 +1,31 @@ +package gregtech.api.graphs.consumers; + +import java.util.ArrayList; + +import net.minecraft.tileentity.TileEntity; +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.graphs.paths.PowerNodePath; +import gregtech.api.metatileentity.BaseMetaPipeEntity; + +// this is here to apply voltage to dead ends +public class EmptyPowerConsumer extends ConsumerNode { + + public EmptyPowerConsumer(int aNodeValue, TileEntity aTileEntity, ForgeDirection side, + ArrayList<ConsumerNode> aConsumers) { + super(aNodeValue, aTileEntity, side, aConsumers); + } + + @Override + public boolean needsEnergy() { + return false; + } + + @Override + public int injectEnergy(long aVoltage, long aMaxAmps) { + BaseMetaPipeEntity tPipe = (BaseMetaPipeEntity) mTileEntity; + PowerNodePath tPath = (PowerNodePath) tPipe.getNodePath(); + tPath.applyVoltage(aVoltage, true); + return 0; + } +} diff --git a/src/main/java/gregtech/api/graphs/consumers/NodeEnergyConnected.java b/src/main/java/gregtech/api/graphs/consumers/NodeEnergyConnected.java new file mode 100644 index 0000000000..fb0a8cf287 --- /dev/null +++ b/src/main/java/gregtech/api/graphs/consumers/NodeEnergyConnected.java @@ -0,0 +1,21 @@ +package gregtech.api.graphs.consumers; + +import java.util.ArrayList; + +import net.minecraft.tileentity.TileEntity; +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.interfaces.tileentity.IEnergyConnected; + +public class NodeEnergyConnected extends ConsumerNode { + + public NodeEnergyConnected(int aNodeValue, IEnergyConnected aTileEntity, ForgeDirection side, + ArrayList<ConsumerNode> aConsumers) { + super(aNodeValue, (TileEntity) aTileEntity, side, aConsumers); + } + + @Override + public int injectEnergy(long aVoltage, long aMaxAmps) { + return (int) ((IEnergyConnected) mTileEntity).injectEnergyUnits(mSide, aVoltage, aMaxAmps); + } +} diff --git a/src/main/java/gregtech/api/graphs/consumers/NodeEnergyReceiver.java b/src/main/java/gregtech/api/graphs/consumers/NodeEnergyReceiver.java new file mode 100644 index 0000000000..4f35922029 --- /dev/null +++ b/src/main/java/gregtech/api/graphs/consumers/NodeEnergyReceiver.java @@ -0,0 +1,68 @@ +package gregtech.api.graphs.consumers; + +import java.util.ArrayList; + +import net.minecraft.init.Blocks; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; + +import cofh.api.energy.IEnergyReceiver; +import gregtech.GT_Mod; +import gregtech.api.GregTech_API; +import gregtech.api.enums.GT_Values; +import gregtech.api.enums.SoundResource; +import gregtech.api.util.GT_Utility; +import gregtech.api.util.WorldSpawnedEventBuilder; +import gregtech.common.GT_Pollution; + +// consumer for RF machines +public class NodeEnergyReceiver extends ConsumerNode { + + int mRestRF = 0; + + public NodeEnergyReceiver(int aNodeValue, IEnergyReceiver aTileEntity, ForgeDirection side, + ArrayList<ConsumerNode> aConsumers) { + super(aNodeValue, (TileEntity) aTileEntity, side, aConsumers); + } + + @Override + public int injectEnergy(long aVoltage, long aMaxAmps) { + ForgeDirection tDirection = mSide; + int rfOut = GT_Utility.safeInt(aVoltage * GregTech_API.mEUtoRF / 100); + int ampsUsed = 0; + if (mRestRF < rfOut) { + mRestRF += rfOut; + ampsUsed = 1; + } + if (((IEnergyReceiver) mTileEntity).receiveEnergy(tDirection, mRestRF, true) > 0) { + int consumed = ((IEnergyReceiver) mTileEntity).receiveEnergy(tDirection, mRestRF, false); + mRestRF -= consumed; + return ampsUsed; + } + if (GregTech_API.mRFExplosions && GregTech_API.sMachineExplosions + && ((IEnergyReceiver) mTileEntity).getMaxEnergyStored(tDirection) < rfOut * 600L) { + explode(rfOut); + } + return 0; + } + + // copied from IEnergyConnected + private void explode(int aRfOut) { + if (aRfOut > 32L * GregTech_API.mEUtoRF / 100L) { + float tStrength = GT_Values.getExplosionPowerForVoltage(aRfOut); + int tX = mTileEntity.xCoord, tY = mTileEntity.yCoord, tZ = mTileEntity.zCoord; + World tWorld = mTileEntity.getWorldObj(); + GT_Utility.sendSoundToPlayers(tWorld, SoundResource.IC2_MACHINES_MACHINE_OVERLOAD, 1.0F, -1, tX, tY, tZ); + tWorld.setBlock(tX, tY, tZ, Blocks.air); + if (GregTech_API.sMachineExplosions) if (GT_Mod.gregtechproxy.mPollution) GT_Pollution + .addPollution(tWorld.getChunkFromBlockCoords(tX, tZ), GT_Mod.gregtechproxy.mPollutionOnExplosion); + + new WorldSpawnedEventBuilder.ExplosionEffectEventBuilder().setStrength(tStrength) + .setSmoking(true) + .setPosition(tX + 0.5, tY + 0.5, tZ + 0.5) + .setWorld(tWorld) + .run(); + } + } +} diff --git a/src/main/java/gregtech/api/graphs/consumers/NodeEnergySink.java b/src/main/java/gregtech/api/graphs/consumers/NodeEnergySink.java new file mode 100644 index 0000000000..44fb88e5e8 --- /dev/null +++ b/src/main/java/gregtech/api/graphs/consumers/NodeEnergySink.java @@ -0,0 +1,30 @@ +package gregtech.api.graphs.consumers; + +import java.util.ArrayList; + +import net.minecraft.tileentity.TileEntity; +import net.minecraftforge.common.util.ForgeDirection; + +import ic2.api.energy.tile.IEnergySink; + +// consumer for IC2 machines +public class NodeEnergySink extends ConsumerNode { + + public NodeEnergySink(int nodeValue, IEnergySink tileEntity, ForgeDirection side, + ArrayList<ConsumerNode> consumers) { + super(nodeValue, (TileEntity) tileEntity, side, consumers); + } + + @Override + public boolean needsEnergy() { + return super.needsEnergy() && ((IEnergySink) mTileEntity).getDemandedEnergy() > 0; + } + + @Override + public int injectEnergy(long aVoltage, long aMaxAmps) { + int tUsedAmps = 0; + while (aMaxAmps > tUsedAmps && ((IEnergySink) mTileEntity).getDemandedEnergy() > 0 + && ((IEnergySink) mTileEntity).injectEnergy(mSide, aVoltage, aVoltage) < aVoltage) tUsedAmps++; + return tUsedAmps; + } +} diff --git a/src/main/java/gregtech/api/graphs/consumers/NodeGTBaseMetaTile.java b/src/main/java/gregtech/api/graphs/consumers/NodeGTBaseMetaTile.java new file mode 100644 index 0000000000..e8d8304eb3 --- /dev/null +++ b/src/main/java/gregtech/api/graphs/consumers/NodeGTBaseMetaTile.java @@ -0,0 +1,28 @@ +package gregtech.api.graphs.consumers; + +import java.util.ArrayList; + +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.interfaces.tileentity.IEnergyConnected; +import gregtech.api.metatileentity.BaseMetaTileEntity; + +// consumer for gt machines +public class NodeGTBaseMetaTile extends ConsumerNode { + + public NodeGTBaseMetaTile(int aNodeValue, BaseMetaTileEntity aTileEntity, ForgeDirection side, + ArrayList<ConsumerNode> aConsumers) { + super(aNodeValue, aTileEntity, side, aConsumers); + } + + @Override + public int injectEnergy(long aVoltage, long aMaxAmps) { + return (int) ((IEnergyConnected) mTileEntity).injectEnergyUnits(mSide, aVoltage, aMaxAmps); + } + + @Override + public boolean needsEnergy() { + BaseMetaTileEntity tTileEntity = (BaseMetaTileEntity) mTileEntity; + return super.needsEnergy() && tTileEntity.getStoredEU() < tTileEntity.getEUCapacity(); + } +} diff --git a/src/main/java/gregtech/api/graphs/paths/NodePath.java b/src/main/java/gregtech/api/graphs/paths/NodePath.java new file mode 100644 index 0000000000..0e852bd484 --- /dev/null +++ b/src/main/java/gregtech/api/graphs/paths/NodePath.java @@ -0,0 +1,42 @@ +package gregtech.api.graphs.paths; + +import gregtech.api.graphs.Lock; +import gregtech.api.metatileentity.BaseMetaPipeEntity; +import gregtech.api.metatileentity.MetaPipeEntity; + +// to contain all info about the path between nodes +public class NodePath { + + protected MetaPipeEntity[] mPipes; + public Lock lock = new Lock(); + + public NodePath(MetaPipeEntity[] aCables) { + this.mPipes = aCables; + processPipes(); + } + + protected void processPipes() { + for (MetaPipeEntity tPipe : mPipes) { + BaseMetaPipeEntity basePipe = (BaseMetaPipeEntity) tPipe.getBaseMetaTileEntity(); + basePipe.setNodePath(this); + } + } + + public void clearPath() { + for (MetaPipeEntity mPipe : mPipes) { + BaseMetaPipeEntity tBasePipe = (BaseMetaPipeEntity) mPipe.getBaseMetaTileEntity(); + if (tBasePipe != null) { + tBasePipe.setNodePath(null); + } + } + } + + public void reloadLocks() { + for (MetaPipeEntity pipe : mPipes) { + BaseMetaPipeEntity basePipe = (BaseMetaPipeEntity) pipe.getBaseMetaTileEntity(); + if (basePipe != null) { + basePipe.reloadLocks(); + } + } + } +} diff --git a/src/main/java/gregtech/api/graphs/paths/PowerNodePath.java b/src/main/java/gregtech/api/graphs/paths/PowerNodePath.java new file mode 100644 index 0000000000..8a869c333e --- /dev/null +++ b/src/main/java/gregtech/api/graphs/paths/PowerNodePath.java @@ -0,0 +1,153 @@ +package gregtech.api.graphs.paths; + +import net.minecraft.server.MinecraftServer; + +import gregtech.api.enums.TickTime; +import gregtech.api.metatileentity.BaseMetaPipeEntity; +import gregtech.api.metatileentity.MetaPipeEntity; +import gregtech.api.metatileentity.implementations.GT_MetaPipeEntity_Cable; +import gregtech.api.util.AveragePerTickCounter; + +// path for cables +// all calculations like amp and voltage happens here +public class PowerNodePath extends NodePath { + + long mMaxAmps; + long mAmps = 0; + long mLoss; + long mVoltage = 0; + long mMaxVoltage; + int mTick = 0; + boolean mCountUp = true; + + private AveragePerTickCounter avgAmperageCounter = new AveragePerTickCounter(TickTime.SECOND); + private AveragePerTickCounter avgVoltageCounter = new AveragePerTickCounter(TickTime.SECOND); + + public PowerNodePath(MetaPipeEntity[] aCables) { + super(aCables); + } + + public long getLoss() { + return mLoss; + } + + public void applyVoltage(long aVoltage, boolean aCountUp) { + + avgVoltageCounter.addValue(Math.max(aVoltage - mLoss, 0)); + + int tNewTime = MinecraftServer.getServer() + .getTickCounter(); + if (mTick != tNewTime) { + reset(tNewTime - mTick); + mTick = tNewTime; + this.mVoltage = aVoltage; + this.mCountUp = aCountUp; + } else if (this.mCountUp != aCountUp && (aVoltage - mLoss) > this.mVoltage || aVoltage > this.mVoltage) { + this.mCountUp = aCountUp; + this.mVoltage = aVoltage; + } + if (aVoltage > mMaxVoltage) { + lock.addTileEntity(null); + for (MetaPipeEntity tCable : mPipes) { + if (((GT_MetaPipeEntity_Cable) tCable).mVoltage < this.mVoltage) { + BaseMetaPipeEntity tBaseCable = (BaseMetaPipeEntity) tCable.getBaseMetaTileEntity(); + if (tBaseCable != null) { + tBaseCable.setToFire(); + } + } + } + } + } + + private void reset(int aTimePassed) { + if (aTimePassed < 0 || aTimePassed > 100) { + mAmps = 0; + return; + } + mAmps = Math.max(0, mAmps - (mMaxAmps * aTimePassed)); + } + + public void addAmps(long aAmps) { + + avgAmperageCounter.addValue(aAmps); + + this.mAmps += aAmps; + if (this.mAmps > mMaxAmps * 40) { + lock.addTileEntity(null); + for (MetaPipeEntity tCable : mPipes) { + if (((GT_MetaPipeEntity_Cable) tCable).mAmperage * 40 < this.mAmps) { + BaseMetaPipeEntity tBaseCable = (BaseMetaPipeEntity) tCable.getBaseMetaTileEntity(); + if (tBaseCable != null) { + tBaseCable.setToFire(); + } + } + } + } + } + + // if no amps pass through for more than 0.5 second reduce them to minimize wrong results + // but still allow the player to see if activity is happening + @Deprecated + public long getAmps() { + int tTime = MinecraftServer.getServer() + .getTickCounter() - 10; + if (mTick < tTime) { + reset(tTime - mTick); + mTick = tTime; + } + return mAmps; + } + + @Deprecated + public long getVoltage(MetaPipeEntity aCable) { + int tLoss = 0; + if (mCountUp) { + for (MetaPipeEntity mPipe : mPipes) { + GT_MetaPipeEntity_Cable tCable = (GT_MetaPipeEntity_Cable) mPipe; + tLoss += tCable.mCableLossPerMeter; + if (aCable == tCable) { + return Math.max(mVoltage - tLoss, 0); + } + } + } else { + for (int i = mPipes.length - 1; i >= 0; i--) { + GT_MetaPipeEntity_Cable tCable = (GT_MetaPipeEntity_Cable) mPipes[i]; + tLoss += tCable.mCableLossPerMeter; + if (aCable == tCable) { + return Math.max(mVoltage - tLoss, 0); + } + } + } + return -1; + } + + public long getAmperage() { + return avgAmperageCounter.getLast(); + } + + public double getAvgAmperage() { + return avgAmperageCounter.getAverage(); + } + + public long getVoltage() { + return avgVoltageCounter.getLast(); + } + + public double getAvgVoltage() { + return avgVoltageCounter.getAverage(); + } + + @Override + protected void processPipes() { + super.processPipes(); + mMaxAmps = Integer.MAX_VALUE; + mMaxVoltage = Integer.MAX_VALUE; + for (MetaPipeEntity tCable : mPipes) { + if (tCable instanceof GT_MetaPipeEntity_Cable) { + mMaxAmps = Math.min(((GT_MetaPipeEntity_Cable) tCable).mAmperage, mMaxAmps); + mLoss += ((GT_MetaPipeEntity_Cable) tCable).mCableLossPerMeter; + mMaxVoltage = Math.min(((GT_MetaPipeEntity_Cable) tCable).mVoltage, mMaxVoltage); + } + } + } +} |