diff --git a/src/main/java/info/nukepowered/impressivelogic/common/block/AbstractNetworkBlock.java b/src/main/java/info/nukepowered/impressivelogic/common/block/AbstractNetworkBlock.java index 90112fd..02034f6 100644 --- a/src/main/java/info/nukepowered/impressivelogic/common/block/AbstractNetworkBlock.java +++ b/src/main/java/info/nukepowered/impressivelogic/common/block/AbstractNetworkBlock.java @@ -53,12 +53,13 @@ public void onPlace(BlockState state, Level level, BlockPos pos, BlockState prev if (!level.isClientSide && this.checkStateChanged(previousState, state)) { var networksJoined = new HashSet(); var part = this.getPart(level, pos); + Entity entity = null; for (var dir : part.getConnectableSides(level, pos)) { var opt = LogicNetManager.findNetwork(level, pos.relative(dir)); if (opt.isPresent()) { var network = opt.get(); - if (LogicNetManager.joinNetwork(level, network, pos, dir, part)) { + if ((entity = LogicNetManager.joinNetwork(level, network, pos, dir, part)) != null) { networksJoined.add(network); } } @@ -68,7 +69,7 @@ public void onPlace(BlockState state, Level level, BlockPos pos, BlockState prev if (networksJoined.isEmpty()) { LogicNetManager.registerNewNetwork(level, pos, part); } else { - LogicNetManager.mergeNetworks(level, networksJoined); + LogicNetManager.mergeNetworks(level, entity, networksJoined); } } } diff --git a/src/main/java/info/nukepowered/impressivelogic/common/logic/network/LogicNetManager.java b/src/main/java/info/nukepowered/impressivelogic/common/logic/network/LogicNetManager.java index b1360cd..de9eab4 100644 --- a/src/main/java/info/nukepowered/impressivelogic/common/logic/network/LogicNetManager.java +++ b/src/main/java/info/nukepowered/impressivelogic/common/logic/network/LogicNetManager.java @@ -4,7 +4,8 @@ import info.nukepowered.impressivelogic.common.block.AbstractNetworkBlock; import info.nukepowered.impressivelogic.common.logic.network.Network.Entity; import info.nukepowered.impressivelogic.common.logic.network.execution.NetworkExecutionManager; -import info.nukepowered.impressivelogic.common.logic.network.execution.tasks.NetCompileTask; +import info.nukepowered.impressivelogic.common.logic.network.execution.NetworkUpdateType; +import info.nukepowered.impressivelogic.common.logic.network.execution.tasks.NetworkUpdateCompileTask; import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos.MutableBlockPos; import net.minecraft.core.Direction; @@ -17,6 +18,9 @@ import java.util.*; +import static info.nukepowered.impressivelogic.ImpressiveLogic.LOGGER; +import static info.nukepowered.impressivelogic.common.logic.network.execution.tasks.AbstractNetworkUpdateTask.COMPILE_MARKER; + /** * Copyright (c) Nukepowered 2022. * @@ -51,7 +55,8 @@ public static Network registerNewNetwork(Level level, BlockPos partPos, INetwork * @param part requester part * @return true if joined to network */ - public static boolean joinNetwork(Level level, Network network, BlockPos partPos, Direction from, INetworkPart part) { + public static Entity joinNetwork(Level level, Network network, BlockPos partPos, Direction from, INetworkPart part) { + Entity entity = null; var entityOpt = network.findEntity(partPos); var netPos = partPos.relative(from); var netDir = from.getOpposite(); @@ -60,26 +65,22 @@ public static boolean joinNetwork(Level level, Network network, BlockPos partPos if (partOptional.isPresent()) { var netEntity = partOptional.get(); if (netEntity.getPart().acceptConnection(level, netPos, netDir)) { - Entity entity; if (entityOpt.isPresent()) { - entityOpt.get().getConnections().add(from); + entity = entityOpt.get(); + entity.getConnections().add(from); netEntity.getConnections().add(netDir); - - return true; } else if ((entity = network.registerPart(partPos, part)) != null) { NETWORKS.registerPartMapping(level.dimension().location(), network, partPos); netEntity.getConnections().add(netDir); entity.getConnections().add(from); - - return true; } } } - return false; + return entity; } - public static void mergeNetworks(Level level, Collection networks) { + public static void mergeNetworks(Level level, Entity cause, Collection networks) { var queue = new ArrayDeque<>(networks); var first = queue.poll(); @@ -88,7 +89,11 @@ public static void mergeNetworks(Level level, Collection networks) { NETWORKS.updateMappings(level.dimension().location(), first, net.getEntityLocations()); } - NetworkExecutionManager.instance().submit(new NetCompileTask(first)); + // If we've joined only one network, update network by faster procedure + var type = queue.isEmpty() ? + NetworkUpdateType.ADD_NODE : + NetworkUpdateType.MERGE; + updateNetworkStructure(first, cause, type); } /** @@ -106,7 +111,7 @@ public static void removeFromNetwork(Level level, BlockPos pos) { if (opt.isPresent()) { var network = opt.get(); NETWORKS.unregisterPartMapping(level.dimension().location(), pos); - network.unregisterPart(pos); + updateNetworkStructure(network, network.unregisterPart(pos), NetworkUpdateType.REMOVE_NODE); } } @@ -120,11 +125,13 @@ public static void removeFromNetwork(Level level, BlockPos pos) { * @param updatedFrom Direction update triggered from (will not check this direction) */ public static void validateNetwork(Level level, BlockPos updatedBlock, Direction updatedFrom) { + LOGGER.debug(COMPILE_MARKER, "Network validation run for block {}, called from: {}", updatedBlock, updatedFrom); + // If throws NPE here - you are using this method wrong // It should be called only if Part of network notice update around var currentNetwork = findNetwork(level, updatedBlock).get(); - var part = currentNetwork.findEntity(updatedBlock).get().getPart(); - final var initSides = new HashSet<>(part.getConnectableSides(level, updatedBlock)); + var entityUpdated = currentNetwork.findEntity(updatedBlock).get(); + final var initSides = new HashSet<>(entityUpdated.getPart().getConnectableSides(level, updatedBlock)); // Will not check for side update came from, block removal expected initSides.remove(updatedFrom); @@ -178,7 +185,7 @@ public static void validateNetwork(Level level, BlockPos updatedBlock, Direction if (!parts.containsAll(currentNetwork.getEntityLocations())) { var newNetwork = currentNetwork.split(parts); NETWORKS.updateMappings(level.dimension().location(), newNetwork, parts); - NetworkExecutionManager.instance().submit(new NetCompileTask(newNetwork)); + NetworkExecutionManager.instance().submit(new NetworkUpdateCompileTask(newNetwork, entityUpdated)); } } @@ -189,4 +196,9 @@ public static Optional findNetwork(Level level, BlockPos pos) { public static NetworkRegistry getRegistry() { return NETWORKS; } + + public static void updateNetworkStructure(Network network, Entity cause, NetworkUpdateType updateType) { + var task = updateType.createTask(network, cause); + NetworkExecutionManager.instance().submit(task); + } } diff --git a/src/main/java/info/nukepowered/impressivelogic/common/logic/network/Network.java b/src/main/java/info/nukepowered/impressivelogic/common/logic/network/Network.java index 897b4cd..2456adb 100644 --- a/src/main/java/info/nukepowered/impressivelogic/common/logic/network/Network.java +++ b/src/main/java/info/nukepowered/impressivelogic/common/logic/network/Network.java @@ -1,9 +1,13 @@ package info.nukepowered.impressivelogic.common.logic.network; import com.google.common.graph.Graph; +import com.google.common.graph.GraphBuilder; +import com.google.common.graph.Graphs; +import com.google.common.graph.ImmutableGraph; import info.nukepowered.impressivelogic.api.logic.INetworkPart; import info.nukepowered.impressivelogic.api.logic.INetworkPart.PartType; import info.nukepowered.impressivelogic.api.logic.io.INetworkInput; +import info.nukepowered.impressivelogic.api.logic.io.INetworkOutput; import info.nukepowered.impressivelogic.common.logic.network.execution.NetworkExecutionManager; import info.nukepowered.impressivelogic.common.logic.network.execution.tasks.NetStateUpdateTask; import info.nukepowered.impressivelogic.common.util.NetworkUtils; @@ -37,7 +41,7 @@ public class Network { private Set> entities = ConcurrentHashMap.newKeySet(); private Set>> inputs = new HashSet<>(); - private Graph> connections; + private volatile Graph> connections; /** * Will trigger network to check logic state and update outputs @@ -60,7 +64,7 @@ public Entity registerPart(BlockPos pos, T part) { return entity; } - public void unregisterPart(BlockPos pos) { + public Entity unregisterPart(BlockPos pos) { var opt = this.findEntity(pos); if (opt.isPresent()) { var entity = opt.get(); @@ -78,7 +82,11 @@ public void unregisterPart(BlockPos pos) { .filter(e -> toUpdate.containsKey(e.location)) .map(e -> Pair.of(e, toUpdate.get(e.location))) .forEach(p -> p.getKey().connections.remove(p.getValue())); + + return entity; } + + return null; } public void merge(Network other) { @@ -87,7 +95,7 @@ public void merge(Network other) { var oEntities = other.entities.stream() .collect(Collectors.toMap(Entity::getLocation, Function.identity())); - + // Merge connections of entities for (var entry : oEntities.entrySet()) { var oEntity = entry.getValue(); var tEntity = tEntities.get(entry.getKey()); @@ -98,12 +106,16 @@ public void merge(Network other) { tEntities.put(entry.getKey(), oEntity); } - + // Remove and put again entities of this net, to update hash this.entities.clear(); tEntities.values().forEach(this.entities::add); + // Merge entities itself this.entities.addAll(other.entities); this.inputs.addAll(other.inputs); + + // Merge Graphs + this.mergeGraph(other.connections); } public Network split(Collection parts) { @@ -157,6 +169,19 @@ public boolean isEmpty() { return this.entities.isEmpty(); } + private void mergeGraph(final Graph> other) { + final var graph = this.connections != null ? + Graphs.copyOf(this.connections) : + GraphBuilder.directed().>build(); + + if (other != null) { + other.nodes().forEach(graph::addNode); + other.edges().forEach(graph::putEdge); + } + + this.connections = ImmutableGraph.copyOf(graph); + } + public CompoundTag writeToNBT() { final var compound = new CompoundTag(); final var entities = new ListTag(); @@ -248,6 +273,11 @@ public boolean isInputType() { return part.getPartType() == PartType.IO && part instanceof INetworkInput; } + public boolean isOutputType() { + var part = this.getPart(); + return part.getPartType() == PartType.IO && part instanceof INetworkOutput; + } + @Override public boolean equals(Object obj) { return obj instanceof Entity entity && this.location.equals(entity.location); diff --git a/src/main/java/info/nukepowered/impressivelogic/common/logic/network/execution/AbstractNetworkTask.java b/src/main/java/info/nukepowered/impressivelogic/common/logic/network/execution/AbstractNetworkTask.java new file mode 100644 index 0000000..a002ce7 --- /dev/null +++ b/src/main/java/info/nukepowered/impressivelogic/common/logic/network/execution/AbstractNetworkTask.java @@ -0,0 +1,37 @@ +package info.nukepowered.impressivelogic.common.logic.network.execution; + +import info.nukepowered.impressivelogic.common.logic.network.Network; +import info.nukepowered.impressivelogic.common.logic.network.Network.Entity; +import net.minecraft.core.BlockPos; +import org.slf4j.Marker; +import org.slf4j.MarkerFactory; + +import java.util.Comparator; +import java.util.NavigableMap; +import java.util.TreeMap; + +/** + * Copyright (c) Nukepowered 2022. + * + * @author TheDarkDnKTv + */ +public abstract class AbstractNetworkTask implements Runnable { + + public static final Marker COMPILE_MARKER = MarkerFactory.getMarker("NET_COMPILE"); + + protected final Network suspect; + protected final NavigableMap> entities; + + public AbstractNetworkTask(Network suspect) { + this.suspect = suspect; + this.entities = new TreeMap<>(Comparator.naturalOrder()); + } + + protected abstract void execute(); + + @Override + public final void run() { + suspect.getEntities().forEach(e -> entities.put(e.getLocation(), e)); + this.execute(); + } +} diff --git a/src/main/java/info/nukepowered/impressivelogic/common/logic/network/execution/NetworkUpdateType.java b/src/main/java/info/nukepowered/impressivelogic/common/logic/network/execution/NetworkUpdateType.java new file mode 100644 index 0000000..19b9456 --- /dev/null +++ b/src/main/java/info/nukepowered/impressivelogic/common/logic/network/execution/NetworkUpdateType.java @@ -0,0 +1,30 @@ +package info.nukepowered.impressivelogic.common.logic.network.execution; + +import info.nukepowered.impressivelogic.common.logic.network.Network; +import info.nukepowered.impressivelogic.common.logic.network.Network.Entity; +import info.nukepowered.impressivelogic.common.logic.network.execution.tasks.*; + +import java.util.function.BiFunction; + +/** + * Copyright (c) Nukepowered 2022. + * + * @author TheDarkDnKTv + */ +public enum NetworkUpdateType { + + COMPILE(NetworkUpdateCompileTask::new), + ADD_NODE(NetworkUpdateAddTask::new), + MERGE(NetworkUpdateMergeTask::new), + REMOVE_NODE(NetworkUpdateRemoveTask::new); + + private final BiFunction, ? extends AbstractNetworkUpdateTask> factory; + + NetworkUpdateType(BiFunction, ? extends AbstractNetworkUpdateTask> factory) { + this.factory = factory; + } + + public AbstractNetworkUpdateTask createTask(Network network, Entity cause) { + return this.factory.apply(network, cause); + } +} diff --git a/src/main/java/info/nukepowered/impressivelogic/common/logic/network/execution/tasks/AbstractNetworkUpdateTask.java b/src/main/java/info/nukepowered/impressivelogic/common/logic/network/execution/tasks/AbstractNetworkUpdateTask.java new file mode 100644 index 0000000..8dca672 --- /dev/null +++ b/src/main/java/info/nukepowered/impressivelogic/common/logic/network/execution/tasks/AbstractNetworkUpdateTask.java @@ -0,0 +1,136 @@ +package info.nukepowered.impressivelogic.common.logic.network.execution.tasks; + +import com.google.common.graph.GraphBuilder; +import com.google.common.graph.Graphs; +import com.google.common.graph.ImmutableGraph; +import com.google.common.graph.MutableGraph; +import info.nukepowered.impressivelogic.api.logic.INetworkPart.PartType; +import info.nukepowered.impressivelogic.common.logic.network.Network; +import info.nukepowered.impressivelogic.common.logic.network.Network.Entity; +import info.nukepowered.impressivelogic.common.logic.network.execution.AbstractNetworkTask; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import org.apache.commons.lang3.tuple.Pair; + +import javax.annotation.Nullable; +import java.util.*; + +/** + * Copyright (c) Nukepowered 2022. + * + * @author TheDarkDnKTv + */ +public abstract class AbstractNetworkUpdateTask extends AbstractNetworkTask { + + protected final MutableGraph> graph; + @Nullable + protected final Entity cause; + + public AbstractNetworkUpdateTask(Network suspect, @Nullable Entity cause) { + super(suspect); + this.cause = cause; + this.graph = suspect.getConnections() == null ? + GraphBuilder.directed().build() : + Graphs.copyOf(suspect.getConnections()); + } + + /** + * Performs graph update operation + */ + protected abstract void update(); + + @Override + protected final void execute() { + this.update(); + suspect.setConnections(ImmutableGraph.copyOf(graph)); + } + + /** + * @param entity node to check + * @return return true if signal can be passed through (cable, or logical gate with output) + */ + protected boolean doNodePassThrough(Entity entity) { + return entity.getType() != PartType.IO; // TODO check if node has outputs + } + + /** + * Searches for any connected nodes (expect wires) from entity specified + * @param from + */ + protected Map, Queue> findConnectedNodes(Entity from) { + var result = new HashMap, Queue>(); + + Deque>> moveStack = new LinkedList<>(); + Queue sides = new LinkedList<>(from.getConnections()); + var visited = new HashSet(); + var pos = from.getLocation().mutable(); + + visited.add(pos); + + do { + if (!moveStack.isEmpty()) { + var pair = moveStack.pollLast(); + sides = pair.getValue(); + pos.move(pair.getKey()); + } + + while (!sides.isEmpty()) { + var side = sides.poll(); + pos.move(side); + + if (!visited.contains(pos)) { + visited.add(pos); + var node = entities.get(pos); + var nodeSides = new LinkedList<>(node.getConnections()); + nodeSides.remove(side.getOpposite()); + + if (node.getType() == PartType.CONNECTOR) { + if (!nodeSides.isEmpty()) { + moveStack.add(Pair.of(side.getOpposite(), sides)); + sides = nodeSides; + continue; + } + } else { + result.put(node, sides); + } + } + + pos.move(side.getOpposite()); + } + } while (!moveStack.isEmpty()); + + return result; + } + + protected void createDirectedEdges(Entity from, Set> rest) { + this.createDirectedEdges(from, rest, true); + } + + /** + * Will create edges in graph from entity to rest of them according to inputs/outputs + * @param from + * @param rest + * @param addToGraph will add nodes to graph + */ + protected void createDirectedEdges(Entity from, Set> rest, boolean addToGraph) { + // TODO raw impl, need to check inputs/outputs + if (addToGraph) { + graph.addNode(from); + rest.forEach(graph::addNode); + } + + if (from.isInputType()) { + for (var entity : rest) { + if (entity.isOutputType()) { + graph.putEdge(from, entity); + } + } + } else if (from.isOutputType()) { + for (var entity : rest) { + if (entity.isInputType()) { + graph.putEdge(entity, from); + } + } + } + } +} diff --git a/src/main/java/info/nukepowered/impressivelogic/common/logic/network/execution/tasks/NetCompileTask.java b/src/main/java/info/nukepowered/impressivelogic/common/logic/network/execution/tasks/NetCompileTask.java deleted file mode 100644 index 2c2f8ec..0000000 --- a/src/main/java/info/nukepowered/impressivelogic/common/logic/network/execution/tasks/NetCompileTask.java +++ /dev/null @@ -1,124 +0,0 @@ -package info.nukepowered.impressivelogic.common.logic.network.execution.tasks; - -import com.google.common.graph.GraphBuilder; -import com.google.common.graph.ImmutableGraph; -import info.nukepowered.impressivelogic.api.logic.INetworkPart.PartType; -import info.nukepowered.impressivelogic.common.logic.network.Network; -import info.nukepowered.impressivelogic.common.logic.network.Network.Entity; -import net.minecraft.core.BlockPos; -import net.minecraft.core.BlockPos.MutableBlockPos; -import net.minecraft.core.Direction; -import org.apache.commons.lang3.tuple.Pair; -import org.slf4j.Marker; -import org.slf4j.MarkerFactory; - -import java.util.*; -import java.util.function.Function; -import java.util.stream.Collectors; - -import static info.nukepowered.impressivelogic.ImpressiveLogic.LOGGER; - -/** - * Copyright (c) Nukepowered 2022. - * - * @author TheDarkDnKTv - */ -public class NetCompileTask implements Runnable { - - public static final Marker COMPILE_MARKER = MarkerFactory.getMarker("NET_COMPILE"); - - private final Network suspect; - private final ImmutableGraph.Builder> gbuilder; - private final NavigableMap> entities; - - public NetCompileTask(Network suspect) { - this.suspect = suspect; - this.gbuilder = GraphBuilder.directed().>immutable(); - this.entities = new TreeMap<>(Comparator.naturalOrder()); - } - - @Override - public void run() { - LOGGER.debug(COMPILE_MARKER, "Network compile execution for {}", suspect); - // Init of entities mapping, and filtering input nodes - Map, Queue> inputs = suspect.getInputs().stream() - .collect(Collectors.toMap(Function.identity(), e -> new LinkedList<>(e.getConnections()))); - suspect.getEntities().forEach(e -> entities.put(e.getLocation(), e)); - - this.compileGraph(inputs); - } - - /** - * Will assemble raw graph of network entity connections - */ - private void compileGraph(Map, Queue> nodes) { - var newNodes = new HashMap, Queue>(); - while (true) { - for (var entry : nodes.entrySet()) { - gbuilder.addNode(entry.getKey()); - this.findEdges(newNodes, entry.getKey(), entry.getValue()); - } - - nodes = newNodes; - if (newNodes.isEmpty()) { - break; - } - } - - suspect.setConnections(gbuilder.build()); - } - - /** - * Searches for edges of this node in sides of sidesToCheck - * - * @param foundNodes if new nodes was found, will be added to this map with their not checked directions - * @param entity node start edges from - * @param sidesToCheck this node not checked sides - */ - private void findEdges(Map, Queue> foundNodes, Entity entity, Queue sidesToCheck) { - Deque>> moveStack = new LinkedList<>(); - MutableBlockPos pos = entity.getLocation().mutable(); - - while (true) { - while (!sidesToCheck.isEmpty()) { - var side = sidesToCheck.poll(); - pos.move(side); - var node = entities.get(pos); - var sides = new LinkedList<>(node.getConnections()); - sides.remove(side.getOpposite()); - - if (node.getType() == PartType.CONNECTOR) { - if (!sides.isEmpty()) { - moveStack.add(Pair.of(side.getOpposite(), sidesToCheck)); - sidesToCheck = sides; - continue; - } - } else { - gbuilder.addNode(node); - gbuilder.putEdge(entity, node); - if (!sides.isEmpty() && this.doNodePassThrough(node)) { - foundNodes.put(node, sides); - } - } - - pos.move(side.getOpposite()); - } - - if (moveStack.isEmpty()) { - break; - } else { - var pair = moveStack.pollLast(); - sidesToCheck = pair.getValue(); - pos.move(pair.getKey()); - } - } - } - - /** - * @param entity node to check - * @return return true if signal can be passed through (cable, or logical gate with output) - */ - private boolean doNodePassThrough(Entity entity) { - return entity.getType() != PartType.IO; // TODO check if node has outputs - } -} diff --git a/src/main/java/info/nukepowered/impressivelogic/common/logic/network/execution/tasks/NetworkUpdateAddTask.java b/src/main/java/info/nukepowered/impressivelogic/common/logic/network/execution/tasks/NetworkUpdateAddTask.java new file mode 100644 index 0000000..a36cfcb --- /dev/null +++ b/src/main/java/info/nukepowered/impressivelogic/common/logic/network/execution/tasks/NetworkUpdateAddTask.java @@ -0,0 +1,38 @@ +package info.nukepowered.impressivelogic.common.logic.network.execution.tasks; + +import com.google.common.graph.ImmutableGraph; +import info.nukepowered.impressivelogic.api.logic.INetworkPart.PartType; +import info.nukepowered.impressivelogic.common.logic.network.Network; +import info.nukepowered.impressivelogic.common.logic.network.Network.Entity; +import net.minecraft.core.Direction; +import org.apache.commons.lang3.tuple.Pair; + +import javax.annotation.Nullable; + +import java.util.*; +import java.util.function.Supplier; + +import static info.nukepowered.impressivelogic.ImpressiveLogic.LOGGER; + +/** + * Copyright (c) Nukepowered 2022. + * + * @author TheDarkDnKTv + */ +public class NetworkUpdateAddTask extends AbstractNetworkUpdateTask { + + public NetworkUpdateAddTask(Network suspect, @Nullable Entity cause) { + super(suspect, cause); + } + + @Override + public void update() { + LOGGER.debug(COMPILE_MARKER, "Network ADD_NODE execution for {}, cause {}", suspect, cause); + + if (this.cause.getType() != PartType.CONNECTOR) { + var connectedNodes = this.findConnectedNodes(this.cause).keySet(); + // Just add new connections from new node, do not update graph fully + this.createDirectedEdges(this.cause, connectedNodes); + } + } +} diff --git a/src/main/java/info/nukepowered/impressivelogic/common/logic/network/execution/tasks/NetworkUpdateCompileTask.java b/src/main/java/info/nukepowered/impressivelogic/common/logic/network/execution/tasks/NetworkUpdateCompileTask.java new file mode 100644 index 0000000..2ef367b --- /dev/null +++ b/src/main/java/info/nukepowered/impressivelogic/common/logic/network/execution/tasks/NetworkUpdateCompileTask.java @@ -0,0 +1,61 @@ +package info.nukepowered.impressivelogic.common.logic.network.execution.tasks; + +import com.google.common.graph.GraphBuilder; +import com.google.common.graph.ImmutableGraph; +import info.nukepowered.impressivelogic.api.logic.INetworkPart.PartType; +import info.nukepowered.impressivelogic.common.logic.network.Network; +import info.nukepowered.impressivelogic.common.logic.network.Network.Entity; +import net.minecraft.core.BlockPos.MutableBlockPos; +import net.minecraft.core.Direction; +import org.apache.commons.lang3.tuple.Pair; + +import javax.annotation.Nullable; +import java.util.*; +import java.util.Map.Entry; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static info.nukepowered.impressivelogic.ImpressiveLogic.LOGGER; + +/** + * Copyright (c) Nukepowered 2022. + * + * @author TheDarkDnKTv + */ +public class NetworkUpdateCompileTask extends AbstractNetworkUpdateTask { + + public NetworkUpdateCompileTask(Network suspect, @Nullable Entity cause) { + super(suspect, cause); + } + + @Override + public void update() { + LOGGER.debug(COMPILE_MARKER, "Network COMPILE execution for {}", suspect); + // Init of entities mapping, and filtering input nodes + Map, Queue> inputs = suspect.getInputs().stream() + .collect(Collectors.toMap(Function.identity(), e -> new LinkedList<>(e.getConnections()))); + this.compileGraph(inputs); + } + + /** + * Will assemble raw graph of network entity connections + */ + private void compileGraph(Map, Queue> nodes) { + do { + for (var entry : nodes.entrySet()) { + var connectedNodes = this.findConnectedNodes(entry.getKey()); + if (!connectedNodes.isEmpty()) { + this.createDirectedEdges(entry.getKey(), connectedNodes.keySet()); + } + + nodes = connectedNodes.entrySet().stream() + .filter(this::checkNodesEntry) + .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue())); + } + } while (!nodes.isEmpty()); + } + + private boolean checkNodesEntry(Entry, Queue> entry) { + return !entry.getValue().isEmpty() && this.doNodePassThrough(entry.getKey()); + } +} diff --git a/src/main/java/info/nukepowered/impressivelogic/common/logic/network/execution/tasks/NetworkUpdateMergeTask.java b/src/main/java/info/nukepowered/impressivelogic/common/logic/network/execution/tasks/NetworkUpdateMergeTask.java new file mode 100644 index 0000000..7d544c2 --- /dev/null +++ b/src/main/java/info/nukepowered/impressivelogic/common/logic/network/execution/tasks/NetworkUpdateMergeTask.java @@ -0,0 +1,41 @@ +package info.nukepowered.impressivelogic.common.logic.network.execution.tasks; + +import com.google.common.collect.Sets; +import info.nukepowered.impressivelogic.api.logic.INetworkPart.PartType; +import info.nukepowered.impressivelogic.common.logic.network.Network; +import javax.annotation.Nullable; + +import java.util.Set; + +import static info.nukepowered.impressivelogic.ImpressiveLogic.LOGGER; + +/** + * Copyright (c) Nukepowered 2022. + * + * @author TheDarkDnKTv + */ +public class NetworkUpdateMergeTask extends AbstractNetworkUpdateTask { + + public NetworkUpdateMergeTask(Network suspect, @Nullable Network.Entity cause) { + super(suspect, cause); + } + + @Override + protected void update() { + LOGGER.debug(COMPILE_MARKER, "Network MERGE execution for {}, cause {}", suspect, cause); + var connectedNodes = Sets.newHashSet(this.findConnectedNodes(this.cause).keySet()); + + if (this.cause.getType() == PartType.CONNECTOR) { + // For each of entity create connections + for (var entity : Set.copyOf(connectedNodes)) { + connectedNodes.remove(entity); + this.createDirectedEdges(entity, connectedNodes); + connectedNodes.add(entity); + } + } else { + this.createDirectedEdges(this.cause, connectedNodes); + } + + LOGGER.info(this.graph.toString()); + } +} diff --git a/src/main/java/info/nukepowered/impressivelogic/common/logic/network/execution/tasks/NetworkUpdateRemoveTask.java b/src/main/java/info/nukepowered/impressivelogic/common/logic/network/execution/tasks/NetworkUpdateRemoveTask.java new file mode 100644 index 0000000..8fb21e2 --- /dev/null +++ b/src/main/java/info/nukepowered/impressivelogic/common/logic/network/execution/tasks/NetworkUpdateRemoveTask.java @@ -0,0 +1,25 @@ +package info.nukepowered.impressivelogic.common.logic.network.execution.tasks; + +import info.nukepowered.impressivelogic.common.logic.network.Network; +import info.nukepowered.impressivelogic.common.logic.network.Network.Entity; +import org.jetbrains.annotations.Nullable; + +import static info.nukepowered.impressivelogic.ImpressiveLogic.LOGGER; + +/** + * Copyright (c) Nukepowered 2022. + * + * @author TheDarkDnKTv + */ +public class NetworkUpdateRemoveTask extends AbstractNetworkUpdateTask { + + public NetworkUpdateRemoveTask(Network suspect, @Nullable Entity cause) { + super(suspect, cause); + } + + @Override + protected void update() { + LOGGER.debug(COMPILE_MARKER, "Network REMOVAL execution for {}", suspect); + this.graph.removeNode(this.cause); + } +}