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; 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.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.BlockPos;
import com.hbm.util.fauxpointtwelve.DirPos; import com.hbm.util.fauxpointtwelve.DirPos;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.World; 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.
* @author hbm * 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 final PowerProvider THE_POWER_PROVIDER = new PowerProvider();
public static HashMap<World, NodeWorld> worlds = new HashMap<>();
public static Set<PowerNetMK2> activePowerNets = new HashSet<>();
public static PowerNode getNode(World world, int x, int y, int z) { public static PowerNode getNode(World world, int x, int y, int z) {
NodeWorld nodeWorld = worlds.get(world); return (PowerNode) UniNodespace.getNode(world, x, y, z, THE_POWER_PROVIDER);
if(nodeWorld != null) return nodeWorld.nodes.get(new BlockPos(x, y, z));
return null;
} }
public static void createNode(World world, PowerNode node) { @Deprecated public static void createNode(World world, PowerNode node) {
NodeWorld nodeWorld = worlds.get(world); UniNodespace.createNode(world, node);
if(nodeWorld == null) {
nodeWorld = new NodeWorld();
worlds.put(world, nodeWorld);
}
nodeWorld.pushNode(node);
} }
public static void destroyNode(World world, int x, int y, int z) { @Deprecated public static void destroyNode(World world, int x, int y, int z) {
PowerNode node = getNode(world, x, y, z); UniNodespace.destroyNode(world, x, y, z, THE_POWER_PROVIDER);
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);
}
} }
@NotableComments @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. * 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. * 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 boolean recentlyChanged = true;
public PowerNode(BlockPos... positions) { public PowerNode(BlockPos... positions) {
super(THE_POWER_PROVIDER, positions);
this.positions = positions; this.positions = positions;
} }
@Override
public PowerNode setConnections(DirPos... connections) { public PowerNode setConnections(DirPos... connections) {
this.connections = connections; super.setConnections(connections);
return this; 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.ArrayList;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Set;
import com.hbm.uninos.NodeNet;
import com.hbm.util.Tuple.Pair; import com.hbm.util.Tuple.Pair;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Random;
import api.hbm.energymk2.IEnergyReceiverMK2.ConnectionPriority; import api.hbm.energymk2.IEnergyReceiverMK2.ConnectionPriority;
import api.hbm.energymk2.Nodespace.PowerNode; import api.hbm.energymk2.Nodespace.PowerNode;
public class PowerNetMK2 { /**
* Technically MK3 since it's now UNINOS compatible, although UNINOS was build out of 95% nodespace code
public static Random rand = new Random(); *
public boolean valid = true; * @author hbm
public Set<PowerNode> links = new HashSet(); */
public class PowerNetMK2 extends NodeNet<IEnergyReceiverMK2, IEnergyProviderMK2, PowerNode> {
/** 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();
public long energyTracker = 0L; 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; 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(providerEntries.isEmpty()) return;
if(receiverEntries.isEmpty()) return; if(receiverEntries.isEmpty()) return;
@ -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) { public long sendPowerDiode(long power) {
if(receiverEntries.isEmpty()) return power; if(receiverEntries.isEmpty()) return power;

View File

@ -1,6 +1,5 @@
package com.hbm.main; package com.hbm.main;
import api.hbm.energymk2.Nodespace;
import com.google.common.collect.HashMultimap; import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import com.hbm.blocks.IStepTickReceiver; 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.machine.rbmk.RBMKDials;
import com.hbm.tileentity.network.RTTYSystem; import com.hbm.tileentity.network.RTTYSystem;
import com.hbm.tileentity.network.RequestNetwork; import com.hbm.tileentity.network.RequestNetwork;
import com.hbm.uninos.UniNodespace;
import com.hbm.util.*; import com.hbm.util.*;
import com.hbm.util.ArmorRegistry.HazardClass; import com.hbm.util.ArmorRegistry.HazardClass;
import com.hbm.world.generator.TimedGenerator; import com.hbm.world.generator.TimedGenerator;
@ -1185,8 +1185,8 @@ public class ModEventHandler {
RTTYSystem.updateBroadcastQueue(); RTTYSystem.updateBroadcastQueue();
RequestNetwork.updateEntries(); RequestNetwork.updateEntries();
TileEntityMachineRadarNT.updateSystem(); TileEntityMachineRadarNT.updateSystem();
Nodespace.updateNodespace(); //Nodespace.updateNodespace();
// bob i beg of you i need fluid nodespace :pray: UniNodespace.updateNodespace();
} }
// There is an issue here somewhere... // 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.BlockPos;
import com.hbm.util.fauxpointtwelve.DirPos; import com.hbm.util.fauxpointtwelve.DirPos;
public class GenNode { public class GenNode<N extends NodeNet> {
public BlockPos[] positions; public BlockPos[] positions;
public DirPos[] connections; public DirPos[] connections;
public NodeNet net; public N net;
public boolean expired = false; public boolean expired = false;
public boolean recentlyChanged = true; public boolean recentlyChanged = true;
public INetworkProvider networkProvider; public INetworkProvider networkProvider;
public GenNode(INetworkProvider provider, BlockPos... positions) { public GenNode(INetworkProvider<N> provider, BlockPos... positions) {
this.networkProvider = provider; this.networkProvider = provider;
this.positions = positions; this.positions = positions;
} }
public GenNode setConnections(DirPos... connections) { public GenNode<N> setConnections(DirPos... connections) {
this.connections = connections; this.connections = connections;
return this; return this;
} }
public GenNode addConnection(DirPos connection) { public GenNode<N> addConnection(DirPos connection) {
DirPos[] newCons = new DirPos[this.connections.length + 1]; DirPos[] newCons = new DirPos[this.connections.length + 1];
for(int i = 0; i < this.connections.length; i++) newCons[i] = this.connections[i]; for(int i = 0; i < this.connections.length; i++) newCons[i] = this.connections[i];
newCons[newCons.length - 1] = connection; newCons[newCons.length - 1] = connection;
@ -34,7 +34,7 @@ public class GenNode {
return this.net != null && this.net.isValid(); return this.net != null && this.net.isValid();
} }
public void setNet(NodeNet net) { public void setNet(N net) {
this.net = net; this.net = net;
this.recentlyChanged = true; this.recentlyChanged = true;
} }

View File

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

View File

@ -3,10 +3,13 @@ package com.hbm.uninos;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.Map.Entry;
import com.hbm.util.Tuple.Pair; import com.hbm.util.Tuple.Pair;
import com.hbm.util.fauxpointtwelve.BlockPos; import com.hbm.util.fauxpointtwelve.BlockPos;
import com.hbm.util.fauxpointtwelve.DirPos;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.World; import net.minecraft.world.World;
public class UniNodespace { 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 static class UniNodeWorld {
public HashMap<Pair<BlockPos, INetworkProvider>, GenNode> nodes = new HashMap(); public HashMap<Pair<BlockPos, INetworkProvider>, GenNode> nodes = new HashMap();

View File

@ -1,12 +1,13 @@
package com.hbm.uninos.networkproviders; package com.hbm.uninos.networkproviders;
import com.hbm.uninos.INetworkProvider; 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 @Override
public PowerNetwork provideNetwork() { public PowerNetMK2 provideNetwork() {
return new PowerNetwork(); 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;
}
}
}