package com.hbm.uninos; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; 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; /** * Unified Nodespace, a Nodespace for all applications. * "Nodespace" is an invisible "dimension" where nodes exist, a node is basically the "soul" of a tile entity with networking capabilities. * Instead of tile entities having to find each other which is costly and assumes the tiles are loaded, tiles simply create nodes at their * respective position in nodespace, the nodespace itself handles stuff like connections which can also happen in unloaded chunks. * A node is so to say the "soul" of a tile entity which can act independent of its "body". * @author hbm */ public class UniNodespace { public static Map worlds = new HashMap(); public static Set activeNodeNets = new HashSet(); public static GenNode getNode(World world, int x, int y, int z, INetworkProvider type) { UniNodeWorld nodeWorld = worlds.get(world); if(nodeWorld != null) return nodeWorld.nodes.get(new Pair(new BlockPos(x, y, z), type)); return null; } public static void createNode(World world, GenNode node) { UniNodeWorld nodeWorld = worlds.get(world); if(nodeWorld == null) { nodeWorld = new UniNodeWorld(); worlds.put(world, nodeWorld); } nodeWorld.pushNode(node); } public static void destroyNode(World world, int x, int y, int z, INetworkProvider type) { GenNode node = getNode(world, x, y, z, type); if(node != null) { worlds.get(world).popNode(node); } } public static void destroyNode(World world, GenNode node) { if(node != null) { worlds.get(world).popNode(node); } } private static int reapTimer = 0; public static void updateNodespace() { for(World world : MinecraftServer.getServer().worldServers) { UniNodeWorld nodeWorld = worlds.get(world); if(nodeWorld == null) continue; for(Entry, 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(); updateReapTimer(); } private static void updateNetworks() { for(NodeNet net : activeNodeNets) net.resetTrackers(); //reset has to be done before everything else for(NodeNet net : activeNodeNets) net.update(); if(reapTimer <= 0) { activeNodeNets.forEach((net) -> { net.links.removeIf((link) -> { return ((GenNode) link).expired; }); }); activeNodeNets.removeIf((net) -> { return net.links.size() <= 0; }); // reap empty networks } } private static void updateReapTimer() { if(reapTimer <= 0) reapTimer = 5 * 60 * 20; // 5 minutes is more than plenty else reapTimer--; } /** 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, GenNode> nodes = new LinkedHashMap<>(); /** Adds a node at all its positions to the nodespace */ public void pushNode(GenNode node) { for(BlockPos pos : node.positions) { nodes.put(new Pair(pos, node.networkProvider), node); } } /** Removes the specified node from all positions from nodespace */ public void popNode(GenNode node) { if(node.net != null) node.net.destroy(); for(BlockPos pos : node.positions) { nodes.remove(new Pair(pos, node.networkProvider)); } node.expired = true; } } }