Skip to content

Commit 5fa6e58

Browse files
committed
First draft of tunnel graph code
1 parent 0a43e6f commit 5fa6e58

File tree

8 files changed

+197
-0
lines changed

8 files changed

+197
-0
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package dev.compactmods.machines.tunnel.graph;
2+
3+
public interface ITunnelGraphEdge {
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package dev.compactmods.machines.tunnel.graph;
2+
3+
public interface ITunnelGraphNode {
4+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package dev.compactmods.machines.tunnel.graph;
2+
3+
public record MachineNode(int machId) implements ITunnelGraphNode {}
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
package dev.compactmods.machines.tunnel.graph;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
import java.util.Optional;
6+
import java.util.stream.Collectors;
7+
import com.google.common.graph.MutableValueGraph;
8+
import com.google.common.graph.ValueGraphBuilder;
9+
import dev.compactmods.machines.CompactMachines;
10+
import dev.compactmods.machines.api.tunnels.TunnelDefinition;
11+
import net.minecraft.core.BlockPos;
12+
import net.minecraft.core.Direction;
13+
import net.minecraft.resources.ResourceLocation;
14+
import org.jetbrains.annotations.NotNull;
15+
16+
/**
17+
* Represents a room's tunnel connections in a graph-style format.
18+
* This should be accessed through the saved data for specific machine room chunks.
19+
*/
20+
public class TunnelConnectionGraph {
21+
22+
/**
23+
* The full data graph. Contains tunnel nodes, machine ids, and tunnel type information.
24+
*/
25+
private final MutableValueGraph<ITunnelGraphNode, ITunnelGraphEdge> graph;
26+
27+
/**
28+
* Quick access to tunnel information for specific locations.
29+
*/
30+
private final Map<BlockPos, TunnelNode> tunnels;
31+
32+
/**
33+
* Quick access to machine information nodes.
34+
*/
35+
private final Map<Integer, MachineNode> machines;
36+
37+
/**
38+
* Quick access to tunnel definition nodes.
39+
*/
40+
private final Map<ResourceLocation, TunnelTypeNode> tunnelTypes;
41+
42+
public TunnelConnectionGraph() {
43+
graph = ValueGraphBuilder
44+
.directed()
45+
.build();
46+
47+
tunnels = new HashMap<>();
48+
machines = new HashMap<>();
49+
tunnelTypes = new HashMap<>();
50+
}
51+
52+
/**
53+
* Finds which machine a tunnel is connected to.
54+
*
55+
* @param tunnel The tunnel to find a connection for.
56+
* @return The id of the connected machine.
57+
*/
58+
public Optional<Integer> connectedMachine(BlockPos tunnel) {
59+
if (!tunnels.containsKey(tunnel))
60+
return Optional.empty();
61+
62+
var tNode = tunnels.get(tunnel);
63+
return graph.successors(tNode)
64+
.stream()
65+
.findFirst()
66+
.filter(n -> n instanceof MachineNode)
67+
.map(mNode -> ((MachineNode) mNode).machId());
68+
}
69+
70+
/**
71+
* Registers a tunnel as being connected to a machine on a particular side.
72+
* If the tunnel already is registered, this will report a failure.
73+
*
74+
* @param tunnelPos The position of the tunnel inside the room.
75+
* @param type The type of tunnel being registered.
76+
* @param machineId The machine the tunnel is to be connected to.
77+
* @param side The side of the machine the tunnel is connecting to.
78+
* @return True if the connection could be established; false if the tunnel type is already registered for the given side.
79+
*/
80+
public boolean registerTunnel(BlockPos tunnelPos, TunnelDefinition type, int machineId, Direction side) {
81+
// First we need to get the machine the tunnel is trying to connect to
82+
var machineRegistered = machines.containsKey(machineId);
83+
MachineNode machineNode;
84+
if (!machineRegistered) {
85+
machineNode = new MachineNode(machineId);
86+
machines.put(machineId, machineNode);
87+
graph.addNode(machineNode);
88+
} else {
89+
machineNode = machines.get(machineId);
90+
}
91+
92+
TunnelNode tunnelNode = getOrCreateTunnelNode(tunnelPos);
93+
if(graph.hasEdgeConnecting(tunnelNode, machineNode)) {
94+
// connection already formed between the tunnel at pos and the machine
95+
graph.edgeValue(tunnelNode, machineNode).ifPresent(edge -> {
96+
CompactMachines.LOGGER.info("Tunnel already registered for machine {} at position {}.",
97+
machineId,
98+
tunnelPos);
99+
});
100+
101+
return false;
102+
}
103+
104+
// graph direction is (tunnel)-[connected_to]->(machine)
105+
var tunnelsForSide = graph.predecessors(machineNode)
106+
.stream()
107+
.filter(n -> n instanceof TunnelNode)
108+
.filter(tunnel -> graph.edgeValue(machineNode, tunnel)
109+
.map(edge -> !((TunnelMachineEdge) edge).side().equals(side))
110+
.orElse(false))
111+
.map(t -> (TunnelNode) t)
112+
.collect(Collectors.toSet());
113+
114+
var tunnelTypeNode = createOrRegisterTunnelType(type);
115+
116+
// If tunnels are registered for the requested side, make sure there isn't a type conflict
117+
if (!tunnelsForSide.isEmpty()) {
118+
for (var sidedTunnel : tunnelsForSide) {
119+
// if we already have a tunnel with the same side and type, log the conflict and early exit
120+
var existingConn = graph.edgeValue(sidedTunnel, tunnelTypeNode);
121+
if(existingConn.isPresent()) {
122+
CompactMachines.LOGGER.info("Tunnel type {} already registered for side {} at position {}.", type.getRegistryName(),
123+
side.getSerializedName(),
124+
sidedTunnel.position());
125+
126+
return false;
127+
}
128+
}
129+
}
130+
131+
// no tunnels registered for side yet - free to make new tunnel node
132+
createTunnelAndLink(tunnelNode, side, machineNode, tunnelTypeNode);
133+
return true;
134+
}
135+
136+
private void createTunnelAndLink(TunnelNode newTunnel, Direction side, MachineNode machineNode, TunnelTypeNode typeNode) {
137+
var newEdge = new TunnelMachineEdge(side);
138+
graph.putEdgeValue(newTunnel, machineNode, newEdge);
139+
graph.putEdgeValue(newTunnel, typeNode, new TunnelTypeEdge());
140+
}
141+
142+
@NotNull
143+
private TunnelNode getOrCreateTunnelNode(BlockPos tunnelPos) {
144+
if(tunnels.containsKey(tunnelPos))
145+
return tunnels.get(tunnelPos);
146+
147+
var newTunnel = new TunnelNode(tunnelPos);
148+
graph.addNode(newTunnel);
149+
tunnels.put(tunnelPos, newTunnel);
150+
return newTunnel;
151+
}
152+
153+
public TunnelTypeNode createOrRegisterTunnelType(TunnelDefinition definition) {
154+
final ResourceLocation id = definition.getRegistryName();
155+
156+
if (tunnelTypes.containsKey(id))
157+
return tunnelTypes.get(id);
158+
159+
TunnelTypeNode newType = new TunnelTypeNode(id);
160+
graph.addNode(newType);
161+
tunnelTypes.put(id, newType);
162+
return newType;
163+
}
164+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package dev.compactmods.machines.tunnel.graph;
2+
3+
import net.minecraft.core.Direction;
4+
5+
public record TunnelMachineEdge(Direction side) implements ITunnelGraphEdge {
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package dev.compactmods.machines.tunnel.graph;
2+
3+
import net.minecraft.core.BlockPos;
4+
5+
public record TunnelNode(BlockPos position) implements ITunnelGraphNode {
6+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package dev.compactmods.machines.tunnel.graph;
2+
3+
public record TunnelTypeEdge() implements ITunnelGraphEdge {
4+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package dev.compactmods.machines.tunnel.graph;
2+
3+
import net.minecraft.resources.ResourceLocation;
4+
5+
public record TunnelTypeNode(ResourceLocation id) implements ITunnelGraphNode {
6+
}

0 commit comments

Comments
 (0)