nodespace is dead, let's wear its skin

This commit is contained in:
Boblet 2025-02-24 14:45:23 +01:00
parent 0ccf81443a
commit bd3b52cd36
8 changed files with 135 additions and 528 deletions

View File

@ -1,161 +1,40 @@
package api.hbm.energymk2;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map.Entry;
import java.util.Set;
import com.hbm.interfaces.NotableComments;
import com.hbm.uninos.GenNode;
import com.hbm.uninos.UniNodespace;
import com.hbm.uninos.networkproviders.PowerProvider;
import com.hbm.util.fauxpointtwelve.BlockPos;
import com.hbm.util.fauxpointtwelve.DirPos;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.World;
/**
* The "Nodespace" is an intermediate, "ethereal" layer of abstraction that tracks nodes (i.e. cables) even when they are no longer loaded, allowing continued operation even when unloaded
* The dead fucking corpse of nodespace MK1.
* A fantastic proof of concept, but ultimately it was killed for being just not that versatile.
* This class is mostly just a compatibility husk that should allow uninodespace to slide into the mod with as much lubrication as it deserves.
*
* @author hbm
*
*/
public class Nodespace {
@Deprecated public class Nodespace {
/** Contains all "NodeWorld" instances, i.e. lists of nodes existing per world */
public static HashMap<World, NodeWorld> worlds = new HashMap<>();
public static Set<PowerNetMK2> activePowerNets = new HashSet<>();
public static final PowerProvider THE_POWER_PROVIDER = new PowerProvider();
public static PowerNode getNode(World world, int x, int y, int z) {
NodeWorld nodeWorld = worlds.get(world);
if(nodeWorld != null) return nodeWorld.nodes.get(new BlockPos(x, y, z));
return null;
return (PowerNode) UniNodespace.getNode(world, x, y, z, THE_POWER_PROVIDER);
}
public static void createNode(World world, PowerNode node) {
NodeWorld nodeWorld = worlds.get(world);
if(nodeWorld == null) {
nodeWorld = new NodeWorld();
worlds.put(world, nodeWorld);
}
nodeWorld.pushNode(node);
@Deprecated public static void createNode(World world, PowerNode node) {
UniNodespace.createNode(world, node);
}
public static void destroyNode(World world, int x, int y, int z) {
PowerNode node = getNode(world, x, y, z);
if(node != null) {
worlds.get(world).popNode(node);
}
}
/** Goes over each node and manages connections */
public static void updateNodespace() {
for(World world : MinecraftServer.getServer().worldServers) {
NodeWorld nodes = worlds.get(world);
if(nodes == null)
continue;
for(Entry<BlockPos, PowerNode> entry : nodes.nodes.entrySet()) {
PowerNode node = entry.getValue();
if(!node.hasValidNet() || node.recentlyChanged) {
checkNodeConnection(world, node);
node.recentlyChanged = false;
}
}
}
updatePowerNets();
}
private static void updatePowerNets() {
for(PowerNetMK2 net : activePowerNets) net.resetEnergyTracker(); //reset has to be done before everything else
for(PowerNetMK2 net : activePowerNets) net.transferPower();
}
/** Goes over each connection point of the given node, tries to find neighbor nodes and to join networks with them */
private static void checkNodeConnection(World world, PowerNode node) {
for(DirPos con : node.connections) {
PowerNode conNode = getNode(world, con.getX(), con.getY(), con.getZ()); // get whatever neighbor node intersects with that connection
if(conNode != null) { // if there is a node at that place
if(conNode.hasValidNet() && conNode.net == node.net) continue; // if the net is valid and both nodes have the same net, skip
if(checkConnection(conNode, con, false)) {
connectToNode(node, conNode);
}
}
}
if(node.net == null || !node.net.isValid()) new PowerNetMK2().joinLink(node);
}
public static boolean checkConnection(PowerNode connectsTo, DirPos connectFrom, boolean skipSideCheck) {
for(DirPos revCon : connectsTo.connections) {
if(revCon.getX() - revCon.getDir().offsetX == connectFrom.getX() && revCon.getY() - revCon.getDir().offsetY == connectFrom.getY() && revCon.getZ() - revCon.getDir().offsetZ == connectFrom.getZ() && (revCon.getDir() == connectFrom.getDir().getOpposite() || skipSideCheck)) {
return true;
}
}
return false;
}
/** Links two nodes with different or potentially no networks */
private static void connectToNode(PowerNode origin, PowerNode connection) {
if(origin.hasValidNet() && connection.hasValidNet()) { // both nodes have nets, but the nets are different (previous assumption), join networks
if(origin.net.links.size() > connection.net.links.size()) {
origin.net.joinNetworks(connection.net);
} else {
connection.net.joinNetworks(origin.net);
}
} else if(!origin.hasValidNet() && connection.hasValidNet()) { // origin has no net, connection does, have origin join connection's net
connection.net.joinLink(origin);
} else if(origin.hasValidNet() && !connection.hasValidNet()) { // ...and vice versa
origin.net.joinLink(connection);
}
}
public static class NodeWorld {
/** Contains a map showing where each node is, a node is every spot that a cable exists at.
* Instead of the old proxy system, things like substation now create multiple nodes at their connection points */
public HashMap<BlockPos, PowerNode> nodes = new HashMap<>();
/** Adds a node at all its positions to the nodespace */
public void pushNode(PowerNode node) {
for(BlockPos pos : node.positions) {
nodes.put(pos, node);
}
}
/** Removes the specified node from all positions from nodespace */
public void popNode(PowerNode node) {
if(node.net != null) node.net.destroy();
for(BlockPos pos : node.positions) {
nodes.remove(pos);
node.expired = true;
}
}
/** Grabs the node at one position, then removes it from all positions it occupies */
public void popNode(BlockPos pos) {
PowerNode node = nodes.get(pos);
if(node != null) popNode(node);
}
@Deprecated public static void destroyNode(World world, int x, int y, int z) {
UniNodespace.destroyNode(world, x, y, z, THE_POWER_PROVIDER);
}
@NotableComments
public static class PowerNode {
public static class PowerNode extends GenNode<PowerNetMK2> {
public BlockPos[] positions;
public DirPos[] connections;
public PowerNetMK2 net;
public boolean expired = false;
/**
* Okay so here's the deal: The code has shit idiot brain fungus. I don't know why. I re-tested every part involved several times.
* I don't know why. But for some reason, during neighbor checks, on certain arbitrary fucking places, the joining operation just fails.
@ -170,29 +49,14 @@ public class Nodespace {
public boolean recentlyChanged = true;
public PowerNode(BlockPos... positions) {
super(THE_POWER_PROVIDER, positions);
this.positions = positions;
}
@Override
public PowerNode setConnections(DirPos... connections) {
this.connections = connections;
super.setConnections(connections);
return this;
}
public PowerNode addConnection(DirPos connection) {
DirPos[] newCons = new DirPos[this.connections.length + 1];
for(int i = 0; i < this.connections.length; i++) newCons[i] = this.connections[i];
newCons[newCons.length - 1] = connection;
this.connections = newCons;
return this;
}
public boolean hasValidNet() {
return this.net != null && this.net.isValid();
}
public void setNet(PowerNetMK2 net) {
this.net = net;
this.recentlyChanged = true;
}
}
}

View File

@ -2,124 +2,32 @@ package api.hbm.energymk2;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import com.hbm.uninos.NodeNet;
import com.hbm.util.Tuple.Pair;
import java.util.Map.Entry;
import java.util.Random;
import api.hbm.energymk2.IEnergyReceiverMK2.ConnectionPriority;
import api.hbm.energymk2.Nodespace.PowerNode;
public class PowerNetMK2 {
public static Random rand = new Random();
public boolean valid = true;
public Set<PowerNode> links = new HashSet();
/** Maps all active subscribers to a timestamp, handy for handling timeouts. In a good system this shouldn't be necessary, but the previous system taught me to be cautious anyway */
public HashMap<IEnergyReceiverMK2, Long> receiverEntries = new HashMap();
public HashMap<IEnergyProviderMK2, Long> providerEntries = new HashMap();
/**
* Technically MK3 since it's now UNINOS compatible, although UNINOS was build out of 95% nodespace code
*
* @author hbm
*/
public class PowerNetMK2 extends NodeNet<IEnergyReceiverMK2, IEnergyProviderMK2, PowerNode> {
public long energyTracker = 0L;
public PowerNetMK2() {
Nodespace.activePowerNets.add(this);
}
/// SUBSCRIBER HANDLING ///
public boolean isSubscribed(IEnergyReceiverMK2 receiver) {
return this.receiverEntries.containsKey(receiver);
}
public void addReceiver(IEnergyReceiverMK2 receiver) {
this.receiverEntries.put(receiver, System.currentTimeMillis());
}
public void removeReceiver(IEnergyReceiverMK2 receiver) {
this.receiverEntries.remove(receiver);
}
/// PROVIDER HANDLING ///
public boolean isProvider(IEnergyProviderMK2 provider) {
return this.providerEntries.containsKey(provider);
}
public void addProvider(IEnergyProviderMK2 provider) {
this.providerEntries.put(provider, System.currentTimeMillis());
}
public void removeProvider(IEnergyProviderMK2 provider) {
this.providerEntries.remove(provider);
}
/// LINK JOINING ///
/** Combines two networks into one */
public void joinNetworks(PowerNetMK2 network) {
if(network == this) return; //wtf?!
List<PowerNode> oldNodes = new ArrayList(network.links.size());
oldNodes.addAll(network.links); // might prevent oddities related to joining - nvm it does nothing
for(PowerNode conductor : oldNodes) forceJoinLink(conductor);
network.links.clear();
for(IEnergyReceiverMK2 connector : network.receiverEntries.keySet()) this.addReceiver(connector);
for(IEnergyProviderMK2 connector : network.providerEntries.keySet()) this.addProvider(connector);
network.destroy();
}
/** Adds the power node as part of this network's links */
public PowerNetMK2 joinLink(PowerNode node) {
if(node.net != null) node.net.leaveLink(node);
return forceJoinLink(node);
}
/** Adds the power node as part of this network's links, skips the part about removing it from existing networks */
public PowerNetMK2 forceJoinLink(PowerNode node) {
this.links.add(node);
node.setNet(this);
return this;
}
/** Removes the specified power node */
public void leaveLink(PowerNode node) {
node.setNet(null);
this.links.remove(node);
}
/// GENERAL POWER NET CONTROL ///
public void invalidate() {
this.valid = false;
Nodespace.activePowerNets.remove(this);
}
public boolean isValid() {
return this.valid;
}
public void destroy() {
this.invalidate();
for(PowerNode link : this.links) if(link.net == this) link.setNet(null);
this.links.clear();
this.receiverEntries.clear();
this.providerEntries.clear();
}
public void resetEnergyTracker() {
this.energyTracker = 0;
}
protected static int timeout = 3_000;
public void transferPower() {
@Override public void resetTrackers() { this.energyTracker = 0; }
@Override
public void update() {
if(providerEntries.isEmpty()) return;
if(receiverEntries.isEmpty()) return;
@ -170,7 +78,7 @@ public class PowerNetMK2 {
toTransfer -= energyUsed;
}
this.energyTracker += energyUsed;
long leftover = energyUsed;
@ -195,118 +103,6 @@ public class PowerNetMK2 {
}
}
@Deprecated public void transferPowerOld() {
if(providerEntries.isEmpty()) return;
if(receiverEntries.isEmpty()) return;
long timestamp = System.currentTimeMillis();
long transferCap = 100_000_000_000_000_00L; // that ought to be enough
long supply = 0;
long demand = 0;
long[] priorityDemand = new long[ConnectionPriority.values().length];
Iterator<Entry<IEnergyProviderMK2, Long>> provIt = providerEntries.entrySet().iterator();
while(provIt.hasNext()) {
Entry<IEnergyProviderMK2, Long> entry = provIt.next();
if(timestamp - entry.getValue() > timeout) { provIt.remove(); continue; }
supply += Math.min(entry.getKey().getPower(), entry.getKey().getProviderSpeed());
}
if(supply <= 0) return;
Iterator<Entry<IEnergyReceiverMK2, Long>> recIt = receiverEntries.entrySet().iterator();
while(recIt.hasNext()) {
Entry<IEnergyReceiverMK2, Long> entry = recIt.next();
if(timestamp - entry.getValue() > timeout) { recIt.remove(); continue; }
long rec = Math.min(entry.getKey().getMaxPower() - entry.getKey().getPower(), entry.getKey().getReceiverSpeed());
demand += rec;
for(int i = 0; i <= entry.getKey().getPriority().ordinal(); i++) priorityDemand[i] += rec;
}
if(demand <= 0) return;
long toTransfer = Math.min(supply, demand);
if(toTransfer > transferCap) toTransfer = transferCap;
if(toTransfer <= 0) return;
List<IEnergyProviderMK2> buffers = new ArrayList();
List<IEnergyProviderMK2> providers = new ArrayList();
Set<IEnergyReceiverMK2> receiverSet = receiverEntries.keySet();
for(IEnergyProviderMK2 provider : providerEntries.keySet()) {
if(receiverSet.contains(provider)) {
buffers.add(provider);
} else {
providers.add(provider);
}
}
providers.addAll(buffers); //makes buffers go last
List<IEnergyReceiverMK2> receivers = new ArrayList() {{ addAll(receiverSet); }};
receivers.sort(COMP);
int maxIteration = 1000;
//how much the current sender/receiver have already sent/received
long prevSrc = 0;
long prevDest = 0;
while(!receivers.isEmpty() && !providers.isEmpty() && maxIteration > 0) {
maxIteration--;
IEnergyProviderMK2 src = providers.get(0);
IEnergyReceiverMK2 dest = receivers.get(0);
if(src.getPower() <= 0) { providers.remove(0); prevSrc = 0; continue; }
if(src == dest) { // STALEMATE DETECTED
//if this happens, a buffer will waste both its share of transfer and receiving potential and do effectively nothing, essentially breaking
//try if placing the conflicting provider at the end of the list does anything
//we do this first because providers have no priority, so we may shuffle those around as much as we want
if(providers.size() > 1) {
providers.add(providers.get(0));
providers.remove(0);
prevSrc = 0; //this might cause slight issues due to the tracking being effectively lost while there still might be pending operations
continue;
}
//if that didn't work, try shifting the receiver by one place (to minimize priority breakage)
if(receivers.size() > 1) {
receivers.add(2, receivers.get(0));
receivers.remove(0);
prevDest = 0; //ditto
continue;
}
//if neither option could be performed, the only conclusion is that this buffer mode battery is alone in the power net, in which case: not my provlem
}
long pd = priorityDemand[dest.getPriority().ordinal()];
long receiverShare = Math.min((long) Math.ceil((double) Math.min(dest.getMaxPower() - dest.getPower(), dest.getReceiverSpeed()) * (double) supply / (double) pd), dest.getReceiverSpeed()) - prevDest;
long providerShare = Math.min((long) Math.ceil((double) Math.min(src.getPower(), src.getProviderSpeed()) * (double) demand / (double) supply), src.getProviderSpeed()) - prevSrc;
long toDrain = Math.min((long) (src.getPower()), providerShare);
long toFill = Math.min(dest.getMaxPower() - dest.getPower(), receiverShare);
long finalTransfer = Math.min(toDrain, toFill);
if(toFill <= 0) { receivers.remove(0); prevDest = 0; continue; }
finalTransfer -= dest.transferPower(finalTransfer);
src.usePower(finalTransfer);
prevSrc += finalTransfer;
prevDest += finalTransfer;
if(prevSrc >= src.getProviderSpeed()) { providers.remove(0); prevSrc = 0; continue; }
if(prevDest >= dest.getReceiverSpeed()) { receivers.remove(0); prevDest = 0; continue; }
toTransfer -= finalTransfer;
this.energyTracker += finalTransfer;
}
}
public long sendPowerDiode(long power) {
if(receiverEntries.isEmpty()) return power;

View File

@ -1,6 +1,5 @@
package com.hbm.main;
import api.hbm.energymk2.Nodespace;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.hbm.blocks.IStepTickReceiver;
@ -50,6 +49,7 @@ import com.hbm.tileentity.machine.TileEntityMachineRadarNT;
import com.hbm.tileentity.machine.rbmk.RBMKDials;
import com.hbm.tileentity.network.RTTYSystem;
import com.hbm.tileentity.network.RequestNetwork;
import com.hbm.uninos.UniNodespace;
import com.hbm.util.*;
import com.hbm.util.ArmorRegistry.HazardClass;
import com.hbm.world.generator.TimedGenerator;
@ -1185,8 +1185,8 @@ public class ModEventHandler {
RTTYSystem.updateBroadcastQueue();
RequestNetwork.updateEntries();
TileEntityMachineRadarNT.updateSystem();
Nodespace.updateNodespace();
// bob i beg of you i need fluid nodespace :pray:
//Nodespace.updateNodespace();
UniNodespace.updateNodespace();
}
// There is an issue here somewhere...

View File

@ -3,26 +3,26 @@ package com.hbm.uninos;
import com.hbm.util.fauxpointtwelve.BlockPos;
import com.hbm.util.fauxpointtwelve.DirPos;
public class GenNode {
public class GenNode<N extends NodeNet> {
public BlockPos[] positions;
public DirPos[] connections;
public NodeNet net;
public N net;
public boolean expired = false;
public boolean recentlyChanged = true;
public INetworkProvider networkProvider;
public GenNode(INetworkProvider provider, BlockPos... positions) {
public GenNode(INetworkProvider<N> provider, BlockPos... positions) {
this.networkProvider = provider;
this.positions = positions;
}
public GenNode setConnections(DirPos... connections) {
public GenNode<N> setConnections(DirPos... connections) {
this.connections = connections;
return this;
}
public GenNode addConnection(DirPos connection) {
public GenNode<N> addConnection(DirPos connection) {
DirPos[] newCons = new DirPos[this.connections.length + 1];
for(int i = 0; i < this.connections.length; i++) newCons[i] = this.connections[i];
newCons[newCons.length - 1] = connection;
@ -34,7 +34,7 @@ public class GenNode {
return this.net != null && this.net.isValid();
}
public void setNet(NodeNet net) {
public void setNet(N net) {
this.net = net;
this.recentlyChanged = true;
}

View File

@ -7,60 +7,61 @@ import java.util.List;
import java.util.Random;
import java.util.Set;
public abstract class NodeNet {
public abstract class NodeNet<R extends IGenReceiver, P extends IGenProvider, L extends GenNode> {
/** Global random for figuring things out like random leftover distribution */
public static Random rand = new Random();
public boolean valid = true;
public Set<GenNode> links = new HashSet();
public Set<L> links = new HashSet();
public abstract HashMap<IGenReceiver, Long> receiverEntries();
public abstract HashMap<IGenProvider, Long> providerEntries();
public HashMap<R, Long> receiverEntries = new HashMap();
public HashMap<P, Long> providerEntries = new HashMap();
public NodeNet() {
UniNodespace.activeNodeNets.add(this);
}
/// SUBSCRIBER HANDLING ///
public boolean isSubscribed(IGenReceiver receiver) { return this.receiverEntries().containsKey(receiver); }
public void addReceiver(IGenReceiver receiver) { this.receiverEntries().put(receiver, System.currentTimeMillis()); }
public void removeReceiver(IGenReceiver receiver) { this.receiverEntries().remove(receiver); }
public boolean isSubscribed(R receiver) { return this.receiverEntries.containsKey(receiver); }
public void addReceiver(R receiver) { this.receiverEntries.put(receiver, System.currentTimeMillis()); }
public void removeReceiver(R receiver) { this.receiverEntries.remove(receiver); }
/// PROVIDER HANDLING ///
public boolean isProvider(IGenProvider provider) { return this.providerEntries().containsKey(provider); }
public void addProvider(IGenProvider provider) { this.providerEntries().put(provider, System.currentTimeMillis()); }
public void removeProvider(IGenProvider provider) { this.providerEntries().remove(provider); }
public boolean isProvider(P provider) { return this.providerEntries.containsKey(provider); }
public void addProvider(P provider) { this.providerEntries.put(provider, System.currentTimeMillis()); }
public void removeProvider(P provider) { this.providerEntries.remove(provider); }
/** Combines two networks into one */
public void joinNetworks(NodeNet network) {
if(network == this) return;
List<GenNode> oldNodes = new ArrayList(network.links.size());
List<L> oldNodes = new ArrayList(network.links.size());
oldNodes.addAll(network.links);
for(GenNode conductor : oldNodes) forceJoinLink(conductor);
for(L conductor : oldNodes) forceJoinLink(conductor);
network.links.clear();
for(IGenReceiver connector : network.receiverEntries().keySet()) this.addReceiver(connector);
for(IGenProvider connector : network.providerEntries().keySet()) this.addProvider(connector);
for(Object /*this is bullshit*/ connector : network.receiverEntries.keySet()) this.addReceiver((R) connector);
for(Object /*this is bullshit*/ connector : network.providerEntries.keySet()) this.addProvider((P) connector);
network.destroy();
}
/** Adds the node as part of this network's links */
public NodeNet joinLink(GenNode node) {
public NodeNet joinLink(L node) {
if(node.net != null) node.net.leaveLink(node);
return forceJoinLink(node);
}
/** Adds the node as part of this network's links, skips the part about removing it from existing networks */
public NodeNet forceJoinLink(GenNode node) {
public NodeNet forceJoinLink(L node) {
this.links.add(node);
node.setNet(this);
return this;
}
/** Removes the specified node */
public void leaveLink(GenNode node) {
public void leaveLink(L node) {
node.setNet(null);
this.links.remove(node);
}
@ -75,7 +76,7 @@ public abstract class NodeNet {
this.invalidate();
for(GenNode link : this.links) if(link.net == this) link.setNet(null);
this.links.clear();
this.receiverEntries().clear();
this.providerEntries().clear();
this.receiverEntries.clear();
this.providerEntries.clear();
}
}

View File

@ -3,10 +3,13 @@ package com.hbm.uninos;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.Map.Entry;
import com.hbm.util.Tuple.Pair;
import com.hbm.util.fauxpointtwelve.BlockPos;
import com.hbm.util.fauxpointtwelve.DirPos;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.World;
public class UniNodespace {
@ -36,6 +39,74 @@ public class UniNodespace {
}
}
public static void updateNodespace() {
for(World world : MinecraftServer.getServer().worldServers) {
UniNodeWorld nodeWorld = worlds.get(world);
if(nodeWorld == null) continue;
for(Entry<Pair<BlockPos, INetworkProvider>, GenNode> entry : nodeWorld.nodes.entrySet()) {
GenNode node = entry.getValue();
INetworkProvider provider = entry.getKey().getValue();
if(!node.hasValidNet() || node.recentlyChanged) {
checkNodeConnection(world, node, provider);
node.recentlyChanged = false;
}
}
}
updateNetworks();
}
private static void updateNetworks() {
for(NodeNet net : activeNodeNets) net.resetTrackers(); //reset has to be done before everything else
for(NodeNet net : activeNodeNets) net.update();
}
/** Goes over each connection point of the given node, tries to find neighbor nodes and to join networks with them */
private static void checkNodeConnection(World world, GenNode node, INetworkProvider provider) {
for(DirPos con : node.connections) {
GenNode conNode = getNode(world, con.getX(), con.getY(), con.getZ(), provider); // get whatever neighbor node intersects with that connection
if(conNode != null) { // if there is a node at that place
if(conNode.hasValidNet() && conNode.net == node.net) continue; // if the net is valid and both nodes have the same net, skip
if(checkConnection(conNode, con, false)) {
connectToNode(node, conNode);
}
}
}
if(node.net == null || !node.net.isValid()) provider.provideNetwork().joinLink(node);
}
/** Checks if the node can be connected to given the DirPos, skipSideCheck will ignore the DirPos' direction value */
public static boolean checkConnection(GenNode connectsTo, DirPos connectFrom, boolean skipSideCheck) {
for(DirPos revCon : connectsTo.connections) {
if(revCon.getX() - revCon.getDir().offsetX == connectFrom.getX() && revCon.getY() - revCon.getDir().offsetY == connectFrom.getY() && revCon.getZ() - revCon.getDir().offsetZ == connectFrom.getZ() && (revCon.getDir() == connectFrom.getDir().getOpposite() || skipSideCheck)) {
return true;
}
}
return false;
}
/** Links two nodes with different or potentially no networks */
private static void connectToNode(GenNode origin, GenNode connection) {
if(origin.hasValidNet() && connection.hasValidNet()) { // both nodes have nets, but the nets are different (previous assumption), join networks
if(origin.net.links.size() > connection.net.links.size()) {
origin.net.joinNetworks(connection.net);
} else {
connection.net.joinNetworks(origin.net);
}
} else if(!origin.hasValidNet() && connection.hasValidNet()) { // origin has no net, connection does, have origin join connection's net
connection.net.joinLink(origin);
} else if(origin.hasValidNet() && !connection.hasValidNet()) { // ...and vice versa
origin.net.joinLink(connection);
}
}
public static class UniNodeWorld {
public HashMap<Pair<BlockPos, INetworkProvider>, GenNode> nodes = new HashMap();

View File

@ -1,12 +1,13 @@
package com.hbm.uninos.networkproviders;
import com.hbm.uninos.INetworkProvider;
import com.hbm.uninos.networks.PowerNetwork;
public class PowerProvider implements INetworkProvider<PowerNetwork> {
import api.hbm.energymk2.PowerNetMK2;
public class PowerProvider implements INetworkProvider<PowerNetMK2> {
@Override
public PowerNetwork provideNetwork() {
return new PowerNetwork();
public PowerNetMK2 provideNetwork() {
return new PowerNetMK2();
}
}

View File

@ -1,126 +0,0 @@
package com.hbm.uninos.networks;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import com.hbm.uninos.NodeNet;
import com.hbm.util.Tuple.Pair;
import api.hbm.energymk2.IEnergyProviderMK2;
import api.hbm.energymk2.IEnergyReceiverMK2;
import api.hbm.energymk2.IEnergyReceiverMK2.ConnectionPriority;
public class PowerNetwork extends NodeNet {
/*
* the original idea was to have every part have a generic type <? extends INetworkProvider> so that once you get down to the level of nodes, you can
* still easily create new networks using the generic type. however:
* - having generics everywhere means that some overrides don't work due to "not being castable" (my ass)
* - most of the time, having generics there didn't really do anything, since the interface is already universally usable, and the type that is provided doesn't actually matter
* - for any case where network type does matter, any node handling instance (cable TEs for example) can just do handling separately, worst case it's just one extra cast
* my balls hurt
*/
public HashMap<IEnergyReceiverMK2, Long> receiverEntries = new HashMap();
public HashMap<IEnergyProviderMK2, Long> providerEntries = new HashMap();
public long energyTracker = 0L;
@Override
public HashMap receiverEntries() {
return receiverEntries;
// generic type erasure seems susipcious here - this either works because the types should be castable anyway,
// or this doesn't work because the compiler has an aneurysm and dies instantly
// technically, generics are obliterated when compiling, and the types are assignable, so i see no issue,
// but then again, HashMap *technically* isn't castable to HashMap<T>, and the compiler might scream about it
}
@Override
public HashMap providerEntries() {
return providerEntries;
}
protected static int timeout = 3_000;
@Override public void resetTrackers() { this.energyTracker = 0; }
@Override
public void update() {
if(providerEntries.isEmpty()) return;
if(receiverEntries.isEmpty()) return;
long timestamp = System.currentTimeMillis();
List<Pair<IEnergyProviderMK2, Long>> providers = new ArrayList();
long powerAvailable = 0;
Iterator<Entry<IEnergyProviderMK2, Long>> provIt = providerEntries.entrySet().iterator();
while(provIt.hasNext()) {
Entry<IEnergyProviderMK2, Long> entry = provIt.next();
if(timestamp - entry.getValue() > timeout) { provIt.remove(); continue; }
long src = Math.min(entry.getKey().getPower(), entry.getKey().getProviderSpeed());
providers.add(new Pair(entry.getKey(), src));
powerAvailable += src;
}
List<Pair<IEnergyReceiverMK2, Long>>[] receivers = new ArrayList[ConnectionPriority.values().length];
for(int i = 0; i < receivers.length; i++) receivers[i] = new ArrayList();
long[] demand = new long[ConnectionPriority.values().length];
long totalDemand = 0;
Iterator<Entry<IEnergyReceiverMK2, Long>> recIt = receiverEntries.entrySet().iterator();
while(recIt.hasNext()) {
Entry<IEnergyReceiverMK2, Long> entry = recIt.next();
if(timestamp - entry.getValue() > timeout) { recIt.remove(); continue; }
long rec = Math.min(entry.getKey().getMaxPower() - entry.getKey().getPower(), entry.getKey().getReceiverSpeed());
int p = entry.getKey().getPriority().ordinal();
receivers[p].add(new Pair(entry.getKey(), rec));
demand[p] += rec;
totalDemand += rec;
}
long toTransfer = Math.min(powerAvailable, totalDemand);
long energyUsed = 0;
for(int i = ConnectionPriority.values().length - 1; i >= 0; i--) {
List<Pair<IEnergyReceiverMK2, Long>> list = receivers[i];
long priorityDemand = demand[i];
for(Pair<IEnergyReceiverMK2, Long> entry : list) {
double weight = (double) entry.getValue() / (double) (priorityDemand);
long toSend = (long) Math.max(toTransfer * weight, 0D);
energyUsed += (toSend - entry.getKey().transferPower(toSend)); //leftovers are subtracted from the intended amount to use up
}
toTransfer -= energyUsed;
}
this.energyTracker += energyUsed;
long leftover = energyUsed;
for(Pair<IEnergyProviderMK2, Long> entry : providers) {
double weight = (double) entry.getValue() / (double) powerAvailable;
long toUse = (long) Math.max(energyUsed * weight, 0D);
entry.getKey().usePower(toUse);
leftover -= toUse;
}
//rounding error compensation, detects surplus that hasn't been used and removes it from random providers
int iterationsLeft = 100; // whiles without emergency brakes are a bad idea
while(iterationsLeft > 0 && leftover > 0 && providers.size() > 0) {
iterationsLeft--;
Pair<IEnergyProviderMK2, Long> selected = providers.get(rand.nextInt(providers.size()));
IEnergyProviderMK2 scapegoat = selected.getKey();
long toUse = Math.min(leftover, scapegoat.getPower());
scapegoat.usePower(toUse);
leftover -= toUse;
}
}
}