mirror of
https://github.com/HbmMods/Hbm-s-Nuclear-Tech-GIT.git
synced 2026-01-25 10:32:49 +00:00
nodespace is dead, let's wear its skin
This commit is contained in:
parent
0ccf81443a
commit
bd3b52cd36
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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...
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user