2020-04-14 04:21:28 +02:00
|
|
|
package de.ellpeck.prettypipes.network;
|
|
|
|
|
|
|
|
import com.google.common.collect.Sets;
|
|
|
|
import de.ellpeck.prettypipes.Registry;
|
2020-04-14 14:10:58 +02:00
|
|
|
import de.ellpeck.prettypipes.Utility;
|
2020-04-14 04:21:28 +02:00
|
|
|
import de.ellpeck.prettypipes.blocks.pipe.PipeBlock;
|
2020-04-14 14:10:58 +02:00
|
|
|
import de.ellpeck.prettypipes.blocks.pipe.PipeTileEntity;
|
2020-04-14 04:21:28 +02:00
|
|
|
import net.minecraft.block.Block;
|
|
|
|
import net.minecraft.block.BlockState;
|
|
|
|
import net.minecraft.nbt.CompoundNBT;
|
2020-04-14 15:02:21 +02:00
|
|
|
import net.minecraft.nbt.ListNBT;
|
|
|
|
import net.minecraft.nbt.NBTUtil;
|
2020-04-14 04:21:28 +02:00
|
|
|
import net.minecraft.util.Direction;
|
|
|
|
import net.minecraft.util.Tuple;
|
|
|
|
import net.minecraft.util.math.BlockPos;
|
|
|
|
import net.minecraft.world.World;
|
|
|
|
import net.minecraftforge.common.capabilities.Capability;
|
|
|
|
import net.minecraftforge.common.capabilities.ICapabilitySerializable;
|
2020-04-14 15:02:21 +02:00
|
|
|
import net.minecraftforge.common.util.Constants;
|
2020-04-14 04:21:28 +02:00
|
|
|
import net.minecraftforge.common.util.LazyOptional;
|
|
|
|
import org.jgrapht.alg.shortestpath.DijkstraShortestPath;
|
|
|
|
import org.jgrapht.graph.SimpleWeightedGraph;
|
|
|
|
|
|
|
|
import javax.annotation.Nonnull;
|
|
|
|
import javax.annotation.Nullable;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
public class PipeNetwork implements ICapabilitySerializable<CompoundNBT> {
|
|
|
|
|
2020-04-14 14:10:58 +02:00
|
|
|
public final SimpleWeightedGraph<BlockPos, NetworkEdge> graph = new SimpleWeightedGraph<>(NetworkEdge.class);
|
|
|
|
private final DijkstraShortestPath<BlockPos, NetworkEdge> dijkstra = new DijkstraShortestPath<>(this.graph);
|
2020-04-14 04:21:28 +02:00
|
|
|
private final World world;
|
|
|
|
|
|
|
|
public PipeNetwork(World world) {
|
|
|
|
this.world = world;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Nonnull
|
|
|
|
@Override
|
|
|
|
public <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, @Nullable Direction side) {
|
|
|
|
return cap == Registry.pipeNetworkCapability ? LazyOptional.of(() -> (T) this) : null;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public CompoundNBT serializeNBT() {
|
2020-04-14 15:02:21 +02:00
|
|
|
CompoundNBT nbt = new CompoundNBT();
|
|
|
|
ListNBT nodes = new ListNBT();
|
|
|
|
for (BlockPos node : this.graph.vertexSet())
|
|
|
|
nodes.add(NBTUtil.writeBlockPos(node));
|
|
|
|
nbt.put("nodes", nodes);
|
|
|
|
ListNBT edges = new ListNBT();
|
|
|
|
for (NetworkEdge edge : this.graph.edgeSet())
|
|
|
|
edges.add(edge.serializeNBT());
|
|
|
|
nbt.put("edges", edges);
|
|
|
|
return nbt;
|
2020-04-14 04:21:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void deserializeNBT(CompoundNBT nbt) {
|
2020-04-14 15:02:21 +02:00
|
|
|
this.graph.removeAllVertices(new ArrayList<>(this.graph.vertexSet()));
|
|
|
|
ListNBT nodes = nbt.getList("nodes", Constants.NBT.TAG_COMPOUND);
|
|
|
|
for (int i = 0; i < nodes.size(); i++)
|
|
|
|
this.graph.addVertex(NBTUtil.readBlockPos(nodes.getCompound(i)));
|
|
|
|
ListNBT edges = nbt.getList("edges", Constants.NBT.TAG_COMPOUND);
|
|
|
|
for (int i = 0; i < edges.size(); i++) {
|
|
|
|
NetworkEdge edge = new NetworkEdge(this.world);
|
|
|
|
edge.deserializeNBT(edges.getCompound(i));
|
|
|
|
this.addEdge(edge);
|
|
|
|
}
|
2020-04-14 04:21:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void addNode(BlockPos pos, BlockState state) {
|
|
|
|
if (!this.graph.containsVertex(pos)) {
|
|
|
|
this.graph.addVertex(pos);
|
|
|
|
this.refreshNode(pos, state);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void removeNode(BlockPos pos) {
|
|
|
|
if (this.graph.containsVertex(pos))
|
|
|
|
this.graph.removeVertex(pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void onPipeChanged(BlockPos pos, BlockState state) {
|
2020-04-14 14:10:58 +02:00
|
|
|
List<NetworkEdge> neighbors = this.createAllEdges(pos, state, true);
|
2020-04-14 04:21:28 +02:00
|
|
|
// if we only have one neighbor, then there can't be any new connections
|
|
|
|
if (neighbors.size() <= 1 && !this.graph.containsVertex(pos))
|
|
|
|
return;
|
2020-04-14 15:02:21 +02:00
|
|
|
for (NetworkEdge edge : neighbors)
|
|
|
|
this.refreshNode(edge.endPipe, this.world.getBlockState(edge.endPipe));
|
2020-04-14 04:21:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private void refreshNode(BlockPos pos, BlockState state) {
|
2020-04-14 15:02:21 +02:00
|
|
|
this.graph.removeAllEdges(new ArrayList<>(this.graph.edgesOf(pos)));
|
|
|
|
for (NetworkEdge edge : this.createAllEdges(pos, state, false))
|
|
|
|
this.addEdge(edge);
|
|
|
|
}
|
2020-04-14 04:21:28 +02:00
|
|
|
|
2020-04-14 15:02:21 +02:00
|
|
|
private void addEdge(NetworkEdge edge) {
|
|
|
|
this.graph.addEdge(edge.startPipe, edge.endPipe, edge);
|
|
|
|
this.graph.setEdgeWeight(edge, edge.pipes.size());
|
2020-04-14 04:21:28 +02:00
|
|
|
}
|
|
|
|
|
2020-04-14 14:10:58 +02:00
|
|
|
private List<NetworkEdge> createAllEdges(BlockPos pos, BlockState state, boolean allAround) {
|
|
|
|
List<NetworkEdge> edges = new ArrayList<>();
|
|
|
|
for (Direction dir : Direction.values()) {
|
|
|
|
NetworkEdge edge = this.createEdge(pos, state, dir, allAround);
|
|
|
|
if (edge != null)
|
|
|
|
edges.add(edge);
|
|
|
|
}
|
|
|
|
return edges;
|
2020-04-14 04:21:28 +02:00
|
|
|
}
|
|
|
|
|
2020-04-14 14:10:58 +02:00
|
|
|
private NetworkEdge createEdge(BlockPos pos, BlockState state, Direction dir, boolean allAround) {
|
|
|
|
if (!allAround && !state.get(PipeBlock.DIRECTIONS.get(dir)).isConnected())
|
|
|
|
return null;
|
|
|
|
BlockPos currPos = pos.offset(dir);
|
|
|
|
BlockState currState = this.world.getBlockState(currPos);
|
|
|
|
if (!(currState.getBlock() instanceof PipeBlock))
|
|
|
|
return null;
|
2020-04-14 15:02:21 +02:00
|
|
|
NetworkEdge edge = new NetworkEdge(this.world);
|
|
|
|
edge.startPipe = pos;
|
|
|
|
edge.pipes.add(pos);
|
|
|
|
edge.pipes.add(currPos);
|
2020-04-14 14:10:58 +02:00
|
|
|
|
|
|
|
while (true) {
|
|
|
|
// if we found a vertex, we can stop since that's the next node
|
|
|
|
// we do this here since the first offset pipe also needs to check this
|
|
|
|
if (this.graph.containsVertex(currPos)) {
|
|
|
|
edge.endPipe = edge.pipes.get(edge.pipes.size() - 1);
|
|
|
|
return edge;
|
|
|
|
}
|
|
|
|
|
|
|
|
boolean found = false;
|
|
|
|
for (Direction nextDir : Direction.values()) {
|
|
|
|
if (!currState.get(PipeBlock.DIRECTIONS.get(nextDir)).isConnected())
|
|
|
|
continue;
|
|
|
|
BlockPos offset = currPos.offset(nextDir);
|
|
|
|
BlockState offState = this.world.getBlockState(offset);
|
|
|
|
if (!(offState.getBlock() instanceof PipeBlock))
|
|
|
|
continue;
|
2020-04-14 15:02:21 +02:00
|
|
|
if (edge.pipes.contains(offset))
|
|
|
|
continue;
|
|
|
|
edge.pipes.add(offset);
|
2020-04-14 14:10:58 +02:00
|
|
|
currPos = offset;
|
|
|
|
currState = offState;
|
|
|
|
found = true;
|
2020-04-14 04:21:28 +02:00
|
|
|
break;
|
|
|
|
}
|
2020-04-14 14:10:58 +02:00
|
|
|
if (!found)
|
|
|
|
break;
|
2020-04-14 04:21:28 +02:00
|
|
|
}
|
2020-04-14 14:10:58 +02:00
|
|
|
return null;
|
2020-04-14 04:21:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public static PipeNetwork get(World world) {
|
|
|
|
return world.getCapability(Registry.pipeNetworkCapability).orElse(null);
|
|
|
|
}
|
|
|
|
}
|