2020-04-14 04:21:28 +02:00
|
|
|
package de.ellpeck.prettypipes.network;
|
|
|
|
|
2020-04-17 20:03:54 +02:00
|
|
|
import com.google.common.collect.ArrayListMultimap;
|
|
|
|
import com.google.common.collect.ListMultimap;
|
2020-04-15 02:16:23 +02:00
|
|
|
import com.google.common.collect.Streams;
|
2020-04-16 00:39:53 +02:00
|
|
|
import de.ellpeck.prettypipes.PrettyPipes;
|
2020-04-14 04:21:28 +02:00
|
|
|
import de.ellpeck.prettypipes.Registry;
|
2020-04-14 14:10:58 +02:00
|
|
|
import de.ellpeck.prettypipes.Utility;
|
2021-03-03 01:56:19 +01:00
|
|
|
import de.ellpeck.prettypipes.misc.ItemEquality;
|
2020-11-22 19:50:31 +01:00
|
|
|
import de.ellpeck.prettypipes.packets.PacketHandler;
|
|
|
|
import de.ellpeck.prettypipes.packets.PacketItemEnterPipe;
|
2020-10-18 02:32:28 +02:00
|
|
|
import de.ellpeck.prettypipes.pipe.IPipeItem;
|
2020-04-16 04:42:42 +02:00
|
|
|
import de.ellpeck.prettypipes.pipe.PipeBlock;
|
|
|
|
import de.ellpeck.prettypipes.pipe.PipeTileEntity;
|
2020-04-14 04:21:28 +02:00
|
|
|
import net.minecraft.block.BlockState;
|
2020-04-14 17:14:24 +02:00
|
|
|
import net.minecraft.item.ItemStack;
|
2020-04-14 04:21:28 +02:00
|
|
|
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.math.BlockPos;
|
|
|
|
import net.minecraft.world.World;
|
|
|
|
import net.minecraftforge.common.capabilities.Capability;
|
|
|
|
import net.minecraftforge.common.capabilities.ICapabilitySerializable;
|
2020-05-09 12:57:25 +02:00
|
|
|
import net.minecraftforge.common.util.Constants.NBT;
|
2020-04-14 04:21:28 +02:00
|
|
|
import net.minecraftforge.common.util.LazyOptional;
|
2020-04-17 20:03:54 +02:00
|
|
|
import net.minecraftforge.items.IItemHandler;
|
|
|
|
import org.apache.commons.lang3.tuple.Pair;
|
2020-04-14 17:14:24 +02:00
|
|
|
import org.jgrapht.GraphPath;
|
2020-04-15 02:16:23 +02:00
|
|
|
import org.jgrapht.ListenableGraph;
|
|
|
|
import org.jgrapht.alg.interfaces.ShortestPathAlgorithm;
|
2020-04-14 04:21:28 +02:00
|
|
|
import org.jgrapht.alg.shortestpath.DijkstraShortestPath;
|
2020-04-15 02:16:23 +02:00
|
|
|
import org.jgrapht.event.GraphEdgeChangeEvent;
|
|
|
|
import org.jgrapht.event.GraphListener;
|
|
|
|
import org.jgrapht.event.GraphVertexChangeEvent;
|
|
|
|
import org.jgrapht.graph.DefaultListenableGraph;
|
2020-04-14 04:21:28 +02:00
|
|
|
import org.jgrapht.graph.SimpleWeightedGraph;
|
2020-04-15 02:16:23 +02:00
|
|
|
import org.jgrapht.traverse.BreadthFirstIterator;
|
2020-04-14 04:21:28 +02:00
|
|
|
|
|
|
|
import javax.annotation.Nonnull;
|
|
|
|
import javax.annotation.Nullable;
|
2020-04-15 02:16:23 +02:00
|
|
|
import java.util.*;
|
2020-05-17 16:14:27 +02:00
|
|
|
import java.util.function.BiFunction;
|
2020-10-15 04:46:23 +02:00
|
|
|
import java.util.function.Consumer;
|
2020-04-16 23:40:35 +02:00
|
|
|
import java.util.function.Function;
|
2020-04-15 02:16:23 +02:00
|
|
|
import java.util.stream.Collectors;
|
2020-04-17 22:29:02 +02:00
|
|
|
import java.util.stream.Stream;
|
2020-04-14 04:21:28 +02:00
|
|
|
|
2020-04-15 02:16:23 +02:00
|
|
|
public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphListener<BlockPos, NetworkEdge> {
|
2020-04-14 04:21:28 +02:00
|
|
|
|
2020-04-15 02:16:23 +02:00
|
|
|
public final ListenableGraph<BlockPos, NetworkEdge> graph;
|
|
|
|
private final DijkstraShortestPath<BlockPos, NetworkEdge> dijkstra;
|
|
|
|
private final Map<BlockPos, List<BlockPos>> nodeToConnectedNodes = new HashMap<>();
|
2020-04-14 17:14:24 +02:00
|
|
|
private final Map<BlockPos, PipeTileEntity> tileCache = new HashMap<>();
|
2020-10-18 02:32:28 +02:00
|
|
|
private final ListMultimap<BlockPos, IPipeItem> pipeItems = ArrayListMultimap.create();
|
2020-05-09 01:00:59 +02:00
|
|
|
private final ListMultimap<BlockPos, NetworkLock> networkLocks = ArrayListMultimap.create();
|
2020-04-14 04:21:28 +02:00
|
|
|
private final World world;
|
2020-10-19 21:31:23 +02:00
|
|
|
private final LazyOptional<PipeNetwork> lazyThis = LazyOptional.of(() -> this);
|
2020-04-14 04:21:28 +02:00
|
|
|
|
|
|
|
public PipeNetwork(World world) {
|
|
|
|
this.world = world;
|
2020-04-15 02:16:23 +02:00
|
|
|
this.graph = new DefaultListenableGraph<>(new SimpleWeightedGraph<>(NetworkEdge.class));
|
|
|
|
this.graph.addGraphListener(this);
|
|
|
|
this.dijkstra = new DijkstraShortestPath<>(this.graph);
|
2020-04-14 04:21:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Nonnull
|
|
|
|
@Override
|
|
|
|
public <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, @Nullable Direction side) {
|
2020-10-19 21:31:23 +02:00
|
|
|
return cap == Registry.pipeNetworkCapability ? this.lazyThis.cast() : LazyOptional.empty();
|
2020-04-14 04:21:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@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);
|
2020-05-09 12:57:25 +02:00
|
|
|
nbt.put("items", Utility.serializeAll(this.pipeItems.values()));
|
|
|
|
nbt.put("locks", Utility.serializeAll(this.networkLocks.values()));
|
2020-04-14 15:02:21 +02:00
|
|
|
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()));
|
2020-04-17 21:32:01 +02:00
|
|
|
this.pipeItems.clear();
|
2020-05-09 12:57:25 +02:00
|
|
|
this.networkLocks.clear();
|
2020-04-17 21:32:01 +02:00
|
|
|
|
2020-05-09 12:57:25 +02:00
|
|
|
ListNBT nodes = nbt.getList("nodes", NBT.TAG_COMPOUND);
|
2020-04-14 15:02:21 +02:00
|
|
|
for (int i = 0; i < nodes.size(); i++)
|
|
|
|
this.graph.addVertex(NBTUtil.readBlockPos(nodes.getCompound(i)));
|
2020-05-09 12:57:25 +02:00
|
|
|
ListNBT edges = nbt.getList("edges", NBT.TAG_COMPOUND);
|
2020-04-14 17:14:24 +02:00
|
|
|
for (int i = 0; i < edges.size(); i++)
|
|
|
|
this.addEdge(new NetworkEdge(edges.getCompound(i)));
|
2020-10-18 02:32:28 +02:00
|
|
|
for (IPipeItem item : Utility.deserializeAll(nbt.getList("items", NBT.TAG_COMPOUND), IPipeItem::load))
|
2020-04-17 21:32:01 +02:00
|
|
|
this.pipeItems.put(item.getCurrentPipe(), item);
|
2020-05-09 12:57:25 +02:00
|
|
|
for (NetworkLock lock : Utility.deserializeAll(nbt.getList("locks", NBT.TAG_COMPOUND), NetworkLock::new))
|
|
|
|
this.createNetworkLock(lock);
|
2020-04-14 04:21:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void addNode(BlockPos pos, BlockState state) {
|
2020-04-14 17:14:24 +02:00
|
|
|
if (!this.isNode(pos)) {
|
2020-04-14 04:21:28 +02:00
|
|
|
this.graph.addVertex(pos);
|
|
|
|
this.refreshNode(pos, state);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void removeNode(BlockPos pos) {
|
2020-04-14 17:14:24 +02:00
|
|
|
if (this.isNode(pos))
|
2020-04-14 04:21:28 +02:00
|
|
|
this.graph.removeVertex(pos);
|
|
|
|
}
|
|
|
|
|
2020-04-14 17:14:24 +02:00
|
|
|
public boolean isNode(BlockPos pos) {
|
|
|
|
return this.graph.containsVertex(pos);
|
|
|
|
}
|
|
|
|
|
2020-04-14 04:21:28 +02:00
|
|
|
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
|
2020-04-14 17:14:24 +02:00
|
|
|
if (neighbors.size() <= 1 && !this.isNode(pos))
|
2020-04-14 04:21:28 +02:00
|
|
|
return;
|
2020-04-20 03:37:23 +02:00
|
|
|
for (NetworkEdge edge : neighbors) {
|
|
|
|
BlockPos end = edge.getEndPipe();
|
|
|
|
this.refreshNode(end, this.world.getBlockState(end));
|
|
|
|
}
|
2020-04-14 04:21:28 +02:00
|
|
|
}
|
|
|
|
|
2020-10-16 22:36:44 +02:00
|
|
|
public ItemStack routeItem(BlockPos startPipePos, BlockPos startInventory, ItemStack stack, boolean preventOversending) {
|
2020-05-17 16:14:27 +02:00
|
|
|
return this.routeItem(startPipePos, startInventory, stack, PipeItem::new, preventOversending);
|
2020-04-14 21:04:41 +02:00
|
|
|
}
|
|
|
|
|
2020-10-18 02:32:28 +02:00
|
|
|
public ItemStack routeItem(BlockPos startPipePos, BlockPos startInventory, ItemStack stack, BiFunction<ItemStack, Float, IPipeItem> itemSupplier, boolean preventOversending) {
|
2020-04-14 17:14:24 +02:00
|
|
|
if (!this.isNode(startPipePos))
|
2020-05-17 16:14:27 +02:00
|
|
|
return stack;
|
2020-04-14 21:04:41 +02:00
|
|
|
if (!this.world.isBlockLoaded(startPipePos))
|
2020-05-17 16:14:27 +02:00
|
|
|
return stack;
|
2020-04-14 17:14:24 +02:00
|
|
|
PipeTileEntity startPipe = this.getPipe(startPipePos);
|
|
|
|
if (startPipe == null)
|
2020-05-17 16:14:27 +02:00
|
|
|
return stack;
|
2020-04-16 00:39:53 +02:00
|
|
|
this.startProfile("find_destination");
|
2020-12-04 19:32:16 +01:00
|
|
|
List<BlockPos> nodes = this.getOrderedNetworkNodes(startPipePos);
|
|
|
|
for (int i = 0; i < nodes.size(); i++) {
|
|
|
|
BlockPos pipePos = nodes.get(startPipe.getNextNode(nodes, i));
|
2020-05-09 21:53:00 +02:00
|
|
|
if (!this.world.isBlockLoaded(pipePos))
|
|
|
|
continue;
|
2020-04-15 02:16:23 +02:00
|
|
|
PipeTileEntity pipe = this.getPipe(pipePos);
|
2020-05-17 16:14:27 +02:00
|
|
|
Pair<BlockPos, ItemStack> dest = pipe.getAvailableDestination(stack, false, preventOversending);
|
|
|
|
if (dest == null || dest.getLeft().equals(startInventory))
|
2020-05-09 21:35:34 +02:00
|
|
|
continue;
|
2020-10-18 02:32:28 +02:00
|
|
|
Function<Float, IPipeItem> sup = speed -> itemSupplier.apply(dest.getRight(), speed);
|
2020-10-13 18:11:40 +02:00
|
|
|
if (this.routeItemToLocation(startPipePos, startInventory, pipe.getPos(), dest.getLeft(), dest.getRight(), sup)) {
|
2020-05-17 16:14:27 +02:00
|
|
|
ItemStack remain = stack.copy();
|
|
|
|
remain.shrink(dest.getRight().getCount());
|
|
|
|
this.endProfile();
|
|
|
|
return remain;
|
|
|
|
}
|
2020-04-14 17:14:24 +02:00
|
|
|
}
|
2020-04-16 00:39:53 +02:00
|
|
|
this.endProfile();
|
2020-05-17 16:14:27 +02:00
|
|
|
return stack;
|
2020-04-14 17:14:24 +02:00
|
|
|
}
|
|
|
|
|
2020-10-18 02:32:28 +02:00
|
|
|
public boolean routeItemToLocation(BlockPos startPipePos, BlockPos startInventory, BlockPos destPipePos, BlockPos destInventory, ItemStack stack, Function<Float, IPipeItem> itemSupplier) {
|
2020-04-17 21:32:01 +02:00
|
|
|
if (!this.isNode(startPipePos) || !this.isNode(destPipePos))
|
2020-04-14 21:04:41 +02:00
|
|
|
return false;
|
2020-04-17 21:32:01 +02:00
|
|
|
if (!this.world.isBlockLoaded(startPipePos) || !this.world.isBlockLoaded(destPipePos))
|
2020-04-14 21:04:41 +02:00
|
|
|
return false;
|
|
|
|
PipeTileEntity startPipe = this.getPipe(startPipePos);
|
|
|
|
if (startPipe == null)
|
|
|
|
return false;
|
2020-04-16 00:39:53 +02:00
|
|
|
this.startProfile("get_path");
|
2020-04-17 21:32:01 +02:00
|
|
|
GraphPath<BlockPos, NetworkEdge> path = this.dijkstra.getPath(startPipePos, destPipePos);
|
2020-04-16 00:39:53 +02:00
|
|
|
this.endProfile();
|
2020-04-15 23:13:05 +02:00
|
|
|
if (path == null)
|
|
|
|
return false;
|
2020-10-18 02:32:28 +02:00
|
|
|
IPipeItem item = itemSupplier.apply(startPipe.getItemSpeed(stack));
|
2020-04-17 21:32:01 +02:00
|
|
|
item.setDestination(startInventory, destInventory, path);
|
2020-10-13 18:11:40 +02:00
|
|
|
startPipe.addNewItem(item);
|
2020-04-14 21:04:41 +02:00
|
|
|
PacketHandler.sendToAllLoaded(this.world, startPipePos, new PacketItemEnterPipe(startPipePos, item));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-03-03 01:56:19 +01:00
|
|
|
public ItemStack requestItem(BlockPos destPipe, BlockPos destInventory, ItemStack stack, ItemEquality... equalityTypes) {
|
2020-05-09 20:12:50 +02:00
|
|
|
ItemStack remain = stack.copy();
|
2020-10-15 01:19:22 +02:00
|
|
|
// check existing items
|
|
|
|
for (NetworkLocation location : this.getOrderedNetworkItems(destPipe)) {
|
2020-10-15 04:15:52 +02:00
|
|
|
remain = this.requestExistingItem(location, destPipe, destInventory, null, remain, equalityTypes);
|
2020-05-09 20:12:50 +02:00
|
|
|
if (remain.isEmpty())
|
2020-10-15 01:19:22 +02:00
|
|
|
return remain;
|
|
|
|
}
|
|
|
|
// check craftable items
|
2021-02-06 00:26:27 +01:00
|
|
|
return this.requestCraftedItem(destPipe, null, remain, new Stack<>(), equalityTypes);
|
2020-10-15 01:19:22 +02:00
|
|
|
}
|
|
|
|
|
2021-03-03 01:56:19 +01:00
|
|
|
public ItemStack requestCraftedItem(BlockPos destPipe, Consumer<ItemStack> unavailableConsumer, ItemStack stack, Stack<ItemStack> dependencyChain, ItemEquality... equalityTypes) {
|
2020-10-15 04:15:52 +02:00
|
|
|
for (Pair<BlockPos, ItemStack> craftable : this.getAllCraftables(destPipe)) {
|
2021-03-03 01:56:19 +01:00
|
|
|
if (!ItemEquality.compareItems(stack, craftable.getRight(), equalityTypes))
|
2020-10-15 01:19:22 +02:00
|
|
|
continue;
|
|
|
|
PipeTileEntity pipe = this.getPipe(craftable.getLeft());
|
|
|
|
if (pipe == null)
|
|
|
|
continue;
|
2021-02-06 00:26:27 +01:00
|
|
|
stack = pipe.craft(destPipe, unavailableConsumer, stack, dependencyChain);
|
2020-10-15 01:19:22 +02:00
|
|
|
if (stack.isEmpty())
|
2020-05-09 20:12:50 +02:00
|
|
|
break;
|
2020-05-09 16:27:49 +02:00
|
|
|
}
|
2020-10-15 01:19:22 +02:00
|
|
|
return stack;
|
2020-05-09 16:27:49 +02:00
|
|
|
}
|
|
|
|
|
2021-03-03 01:56:19 +01:00
|
|
|
public ItemStack requestExistingItem(NetworkLocation location, BlockPos destPipe, BlockPos destInventory, NetworkLock ignoredLock, ItemStack stack, ItemEquality... equalityTypes) {
|
2020-10-16 22:36:44 +02:00
|
|
|
return this.requestExistingItem(location, destPipe, destInventory, ignoredLock, PipeItem::new, stack, equalityTypes);
|
|
|
|
}
|
|
|
|
|
2021-03-03 01:56:19 +01:00
|
|
|
public ItemStack requestExistingItem(NetworkLocation location, BlockPos destPipe, BlockPos destInventory, NetworkLock ignoredLock, BiFunction<ItemStack, Float, IPipeItem> itemSupplier, ItemStack stack, ItemEquality... equalityTypes) {
|
2020-05-09 21:35:34 +02:00
|
|
|
if (location.getPos().equals(destInventory))
|
2020-05-09 20:12:50 +02:00
|
|
|
return stack;
|
2020-10-15 01:56:42 +02:00
|
|
|
// make sure we don't pull any locked items
|
|
|
|
int amount = location.getItemAmount(this.world, stack, equalityTypes);
|
|
|
|
if (amount <= 0)
|
|
|
|
return stack;
|
2020-10-15 04:15:52 +02:00
|
|
|
amount -= this.getLockedAmount(location.getPos(), stack, ignoredLock, equalityTypes);
|
2020-10-15 01:56:42 +02:00
|
|
|
if (amount <= 0)
|
|
|
|
return stack;
|
2020-05-09 20:12:50 +02:00
|
|
|
ItemStack remain = stack.copy();
|
2020-10-15 01:56:42 +02:00
|
|
|
// make sure we only extract less than or equal to the requested amount
|
|
|
|
if (remain.getCount() < amount)
|
|
|
|
amount = remain.getCount();
|
|
|
|
remain.shrink(amount);
|
2020-05-09 16:27:49 +02:00
|
|
|
for (int slot : location.getStackSlots(this.world, stack, equalityTypes)) {
|
|
|
|
// try to extract from that location's inventory and send the item
|
|
|
|
IItemHandler handler = location.getItemHandler(this.world);
|
2020-10-15 01:56:42 +02:00
|
|
|
ItemStack extracted = handler.extractItem(slot, amount, true);
|
2020-10-16 22:36:44 +02:00
|
|
|
if (this.routeItemToLocation(location.pipePos, location.getPos(), destPipe, destInventory, extracted, speed -> itemSupplier.apply(extracted, speed))) {
|
2020-05-09 16:27:49 +02:00
|
|
|
handler.extractItem(slot, extracted.getCount(), false);
|
2020-10-15 01:56:42 +02:00
|
|
|
amount -= extracted.getCount();
|
|
|
|
if (amount <= 0)
|
2020-05-09 20:12:50 +02:00
|
|
|
break;
|
2020-05-09 16:27:49 +02:00
|
|
|
}
|
|
|
|
}
|
2020-05-09 20:12:50 +02:00
|
|
|
return remain;
|
2020-05-09 16:27:49 +02:00
|
|
|
}
|
|
|
|
|
2020-04-14 17:14:24 +02:00
|
|
|
public PipeTileEntity getPipe(BlockPos pos) {
|
|
|
|
PipeTileEntity tile = this.tileCache.get(pos);
|
|
|
|
if (tile == null || tile.isRemoved()) {
|
|
|
|
tile = Utility.getTileEntity(PipeTileEntity.class, this.world, pos);
|
|
|
|
this.tileCache.put(pos, tile);
|
|
|
|
}
|
|
|
|
return tile;
|
|
|
|
}
|
|
|
|
|
2021-06-19 14:48:47 +02:00
|
|
|
public void uncachePipe(BlockPos pos) {
|
|
|
|
if (this.tileCache.remove(pos) != null)
|
|
|
|
System.out.println("Uncaching pipe at " + pos);
|
|
|
|
}
|
|
|
|
|
2021-03-03 01:56:19 +01:00
|
|
|
public List<Pair<BlockPos, ItemStack>> getCurrentlyCrafting(BlockPos node, ItemEquality... equalityTypes) {
|
2020-10-20 01:30:33 +02:00
|
|
|
this.startProfile("get_currently_crafting");
|
|
|
|
List<Pair<BlockPos, ItemStack>> items = new ArrayList<>();
|
2020-11-22 19:50:31 +01:00
|
|
|
Iterator<PipeTileEntity> craftingPipes = this.getAllCraftables(node).stream().map(c -> this.getPipe(c.getLeft())).distinct().iterator();
|
|
|
|
while (craftingPipes.hasNext()) {
|
|
|
|
PipeTileEntity pipe = craftingPipes.next();
|
2020-10-20 01:30:33 +02:00
|
|
|
for (Pair<BlockPos, ItemStack> request : pipe.craftResultRequests) {
|
|
|
|
BlockPos dest = request.getLeft();
|
|
|
|
ItemStack stack = request.getRight();
|
|
|
|
// add up all the items that should go to the same location
|
|
|
|
Optional<Pair<BlockPos, ItemStack>> existing = items.stream()
|
2021-03-03 01:56:19 +01:00
|
|
|
.filter(s -> s.getLeft().equals(dest) && ItemEquality.compareItems(s.getRight(), stack, equalityTypes))
|
2020-10-20 01:30:33 +02:00
|
|
|
.findFirst();
|
|
|
|
if (existing.isPresent()) {
|
|
|
|
existing.get().getRight().grow(stack.getCount());
|
|
|
|
} else {
|
|
|
|
items.add(Pair.of(dest, stack.copy()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.endProfile();
|
|
|
|
return items;
|
|
|
|
}
|
|
|
|
|
2021-03-03 01:56:19 +01:00
|
|
|
public int getCurrentlyCraftingAmount(BlockPos destNode, ItemStack stack, ItemEquality... equalityTypes) {
|
2020-10-20 01:30:33 +02:00
|
|
|
return this.getCurrentlyCrafting(destNode).stream()
|
2021-03-03 01:56:19 +01:00
|
|
|
.filter(p -> p.getLeft().equals(destNode) && ItemEquality.compareItems(p.getRight(), stack, equalityTypes))
|
2020-10-20 01:30:33 +02:00
|
|
|
.mapToInt(p -> p.getRight().getCount()).sum();
|
|
|
|
}
|
|
|
|
|
2020-10-15 04:15:52 +02:00
|
|
|
public List<Pair<BlockPos, ItemStack>> getAllCraftables(BlockPos node) {
|
2020-10-14 22:30:02 +02:00
|
|
|
if (!this.isNode(node))
|
|
|
|
return Collections.emptyList();
|
2020-10-15 04:15:52 +02:00
|
|
|
this.startProfile("get_all_craftables");
|
2020-10-14 22:30:02 +02:00
|
|
|
List<Pair<BlockPos, ItemStack>> craftables = new ArrayList<>();
|
|
|
|
for (BlockPos dest : this.getOrderedNetworkNodes(node)) {
|
|
|
|
if (!this.world.isBlockLoaded(dest))
|
|
|
|
continue;
|
|
|
|
PipeTileEntity pipe = this.getPipe(dest);
|
2020-10-15 04:15:52 +02:00
|
|
|
for (ItemStack stack : pipe.getAllCraftables())
|
2020-10-14 22:30:02 +02:00
|
|
|
craftables.add(Pair.of(pipe.getPos(), stack));
|
|
|
|
}
|
|
|
|
this.endProfile();
|
|
|
|
return craftables;
|
|
|
|
}
|
|
|
|
|
2021-03-03 01:56:19 +01:00
|
|
|
public int getCraftableAmount(BlockPos node, Consumer<ItemStack> unavailableConsumer, ItemStack stack, Stack<ItemStack> dependencyChain, ItemEquality... equalityTypes) {
|
2020-10-15 04:15:52 +02:00
|
|
|
int total = 0;
|
|
|
|
for (Pair<BlockPos, ItemStack> pair : this.getAllCraftables(node)) {
|
2021-03-03 01:56:19 +01:00
|
|
|
if (!ItemEquality.compareItems(pair.getRight(), stack, equalityTypes))
|
2020-10-15 04:15:52 +02:00
|
|
|
continue;
|
|
|
|
if (!this.world.isBlockLoaded(pair.getLeft()))
|
|
|
|
continue;
|
|
|
|
PipeTileEntity pipe = this.getPipe(pair.getLeft());
|
|
|
|
if (pipe != null)
|
2020-12-25 15:23:44 +01:00
|
|
|
total += pipe.getCraftableAmount(unavailableConsumer, stack, dependencyChain);
|
2020-10-15 04:15:52 +02:00
|
|
|
}
|
|
|
|
return total;
|
|
|
|
}
|
|
|
|
|
2020-04-17 20:03:54 +02:00
|
|
|
public List<NetworkLocation> getOrderedNetworkItems(BlockPos node) {
|
|
|
|
if (!this.isNode(node))
|
|
|
|
return Collections.emptyList();
|
|
|
|
this.startProfile("get_network_items");
|
|
|
|
List<NetworkLocation> info = new ArrayList<>();
|
2020-04-20 03:37:23 +02:00
|
|
|
for (BlockPos dest : this.getOrderedNetworkNodes(node)) {
|
2020-05-09 21:53:00 +02:00
|
|
|
if (!this.world.isBlockLoaded(dest))
|
|
|
|
continue;
|
2020-04-17 20:03:54 +02:00
|
|
|
PipeTileEntity pipe = this.getPipe(dest);
|
2020-04-17 22:29:02 +02:00
|
|
|
if (!pipe.canNetworkSee())
|
|
|
|
continue;
|
2020-04-17 20:03:54 +02:00
|
|
|
for (Direction dir : Direction.values()) {
|
2020-10-15 01:19:22 +02:00
|
|
|
IItemHandler handler = pipe.getItemHandler(dir);
|
2020-04-17 20:03:54 +02:00
|
|
|
if (handler == null)
|
|
|
|
continue;
|
2020-04-19 18:23:53 +02:00
|
|
|
// check if this handler already exists (double-connected pipes, double chests etc.)
|
2020-10-17 17:33:31 +02:00
|
|
|
if (info.stream().anyMatch(l -> handler.equals(l.getItemHandler(this.world))))
|
2020-04-19 18:23:53 +02:00
|
|
|
continue;
|
2020-05-09 12:57:25 +02:00
|
|
|
NetworkLocation location = new NetworkLocation(dest, dir);
|
|
|
|
if (!location.isEmpty(this.world))
|
2020-04-19 18:23:53 +02:00
|
|
|
info.add(location);
|
2020-04-17 20:03:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
this.endProfile();
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
|
2020-05-09 01:00:59 +02:00
|
|
|
public void createNetworkLock(NetworkLock lock) {
|
2020-05-09 12:57:25 +02:00
|
|
|
this.networkLocks.put(lock.location.getPos(), lock);
|
2020-05-09 01:00:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void resolveNetworkLock(NetworkLock lock) {
|
2020-05-09 12:57:25 +02:00
|
|
|
this.networkLocks.remove(lock.location.getPos(), lock);
|
2020-05-09 01:00:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public List<NetworkLock> getNetworkLocks(BlockPos pos) {
|
|
|
|
return this.networkLocks.get(pos);
|
|
|
|
}
|
|
|
|
|
2021-03-03 01:56:19 +01:00
|
|
|
public int getLockedAmount(BlockPos pos, ItemStack stack, NetworkLock ignoredLock, ItemEquality... equalityTypes) {
|
2020-05-09 01:00:59 +02:00
|
|
|
return this.getNetworkLocks(pos).stream()
|
2021-03-03 01:56:19 +01:00
|
|
|
.filter(l -> !l.equals(ignoredLock) && ItemEquality.compareItems(l.stack, stack, equalityTypes))
|
2020-05-09 16:27:49 +02:00
|
|
|
.mapToInt(l -> l.stack.getCount()).sum();
|
2020-05-09 01:00:59 +02:00
|
|
|
}
|
|
|
|
|
2020-04-14 04:21:28 +02:00
|
|
|
private void refreshNode(BlockPos pos, BlockState state) {
|
2020-04-16 00:39:53 +02:00
|
|
|
this.startProfile("refresh_node");
|
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-16 00:39:53 +02:00
|
|
|
this.endProfile();
|
2020-04-14 15:02:21 +02:00
|
|
|
}
|
2020-04-14 04:21:28 +02:00
|
|
|
|
2020-04-14 15:02:21 +02:00
|
|
|
private void addEdge(NetworkEdge edge) {
|
2020-04-20 03:37:23 +02:00
|
|
|
this.graph.addEdge(edge.getStartPipe(), edge.getEndPipe(), edge);
|
2020-04-15 02:16:23 +02:00
|
|
|
// only use size - 1 so that nodes aren't counted twice for multi-edge paths
|
|
|
|
this.graph.setEdgeWeight(edge, edge.pipes.size() - 1);
|
2020-04-14 04:21:28 +02:00
|
|
|
}
|
|
|
|
|
2020-04-20 03:37:23 +02:00
|
|
|
public BlockPos getNodeFromPipe(BlockPos pos) {
|
|
|
|
if (this.isNode(pos))
|
|
|
|
return pos;
|
|
|
|
BlockState state = this.world.getBlockState(pos);
|
|
|
|
if (!(state.getBlock() instanceof PipeBlock))
|
|
|
|
return null;
|
|
|
|
for (Direction dir : Direction.values()) {
|
|
|
|
NetworkEdge edge = this.createEdge(pos, state, dir, false);
|
|
|
|
if (edge != null)
|
|
|
|
return edge.getEndPipe();
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
private List<NetworkEdge> createAllEdges(BlockPos pos, BlockState state, boolean ignoreCurrBlocked) {
|
2020-04-16 00:39:53 +02:00
|
|
|
this.startProfile("create_all_edges");
|
2020-04-14 14:10:58 +02:00
|
|
|
List<NetworkEdge> edges = new ArrayList<>();
|
|
|
|
for (Direction dir : Direction.values()) {
|
2020-04-20 03:37:23 +02:00
|
|
|
NetworkEdge edge = this.createEdge(pos, state, dir, ignoreCurrBlocked);
|
2020-04-14 14:10:58 +02:00
|
|
|
if (edge != null)
|
|
|
|
edges.add(edge);
|
|
|
|
}
|
2020-04-16 00:39:53 +02:00
|
|
|
this.endProfile();
|
2020-04-14 14:10:58 +02:00
|
|
|
return edges;
|
2020-04-14 04:21:28 +02:00
|
|
|
}
|
|
|
|
|
2020-04-20 03:37:23 +02:00
|
|
|
private NetworkEdge createEdge(BlockPos pos, BlockState state, Direction dir, boolean ignoreCurrBlocked) {
|
|
|
|
if (!ignoreCurrBlocked && !state.get(PipeBlock.DIRECTIONS.get(dir)).isConnected())
|
2020-04-14 14:10:58 +02:00
|
|
|
return null;
|
|
|
|
BlockPos currPos = pos.offset(dir);
|
|
|
|
BlockState currState = this.world.getBlockState(currPos);
|
|
|
|
if (!(currState.getBlock() instanceof PipeBlock))
|
|
|
|
return null;
|
2020-04-16 00:39:53 +02:00
|
|
|
this.startProfile("create_edge");
|
2020-04-14 17:14:24 +02:00
|
|
|
NetworkEdge edge = new NetworkEdge();
|
2020-04-14 15:02:21 +02:00
|
|
|
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
|
2020-04-14 17:14:24 +02:00
|
|
|
if (this.isNode(currPos)) {
|
2020-04-16 00:39:53 +02:00
|
|
|
this.endProfile();
|
2020-04-14 14:10:58 +02:00
|
|
|
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-16 00:39:53 +02:00
|
|
|
this.endProfile();
|
2020-04-14 14:10:58 +02:00
|
|
|
return null;
|
2020-04-14 04:21:28 +02:00
|
|
|
}
|
|
|
|
|
2020-10-13 19:00:11 +02:00
|
|
|
public List<BlockPos> getOrderedNetworkNodes(BlockPos node) {
|
|
|
|
if (!this.isNode(node))
|
|
|
|
return Collections.emptyList();
|
2020-04-15 02:16:23 +02:00
|
|
|
List<BlockPos> ret = this.nodeToConnectedNodes.get(node);
|
|
|
|
if (ret == null) {
|
2020-04-16 00:39:53 +02:00
|
|
|
this.startProfile("compile_connected_nodes");
|
2020-04-15 02:16:23 +02:00
|
|
|
ShortestPathAlgorithm.SingleSourcePaths<BlockPos, NetworkEdge> paths = this.dijkstra.getPaths(node);
|
|
|
|
// sort destinations first by their priority (eg trash pipes should be last)
|
|
|
|
// and then by their distance from the specified node
|
|
|
|
ret = Streams.stream(new BreadthFirstIterator<>(this.graph, node))
|
2020-10-10 17:38:20 +02:00
|
|
|
.filter(p -> this.getPipe(p) != null)
|
2020-04-15 02:16:23 +02:00
|
|
|
.sorted(Comparator.<BlockPos>comparingInt(p -> this.getPipe(p).getPriority()).reversed().thenComparing(paths::getWeight))
|
|
|
|
.collect(Collectors.toList());
|
|
|
|
this.nodeToConnectedNodes.put(node, ret);
|
2020-04-16 00:39:53 +02:00
|
|
|
this.endProfile();
|
2020-04-15 02:16:23 +02:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-04-17 17:18:25 +02:00
|
|
|
public void clearDestinationCache(BlockPos... nodes) {
|
|
|
|
this.startProfile("clear_node_cache");
|
2020-04-19 22:04:39 +02:00
|
|
|
// remove caches for the nodes
|
2020-04-20 03:37:23 +02:00
|
|
|
for (BlockPos node : nodes)
|
2020-04-19 22:04:39 +02:00
|
|
|
this.nodeToConnectedNodes.keySet().remove(node);
|
2020-04-20 03:37:23 +02:00
|
|
|
// remove caches that contain the nodes as a destination
|
2020-04-17 17:18:25 +02:00
|
|
|
this.nodeToConnectedNodes.values().removeIf(cached -> Arrays.stream(nodes).anyMatch(cached::contains));
|
|
|
|
this.endProfile();
|
|
|
|
}
|
|
|
|
|
2020-10-18 02:32:28 +02:00
|
|
|
public List<IPipeItem> getItemsInPipe(BlockPos pos) {
|
2020-04-17 21:32:01 +02:00
|
|
|
return this.pipeItems.get(pos);
|
|
|
|
}
|
|
|
|
|
2020-10-18 02:32:28 +02:00
|
|
|
public Stream<IPipeItem> getPipeItemsOnTheWay(BlockPos goalInv) {
|
2020-04-17 22:29:02 +02:00
|
|
|
this.startProfile("get_pipe_items_on_the_way");
|
2020-10-18 02:32:28 +02:00
|
|
|
Stream<IPipeItem> ret = this.pipeItems.values().stream().filter(i -> i.getDestInventory().equals(goalInv));
|
2020-04-17 22:29:02 +02:00
|
|
|
this.endProfile();
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2021-03-03 01:56:19 +01:00
|
|
|
public int getItemsOnTheWay(BlockPos goalInv, ItemStack type, ItemEquality... equalityTypes) {
|
2020-05-10 12:50:24 +02:00
|
|
|
return this.getPipeItemsOnTheWay(goalInv)
|
2021-03-03 01:56:19 +01:00
|
|
|
.filter(i -> type == null || ItemEquality.compareItems(i.getContent(), type, equalityTypes))
|
2020-10-18 01:57:51 +02:00
|
|
|
.mapToInt(i -> i.getItemsOnTheWay(goalInv)).sum();
|
2020-04-17 22:29:02 +02:00
|
|
|
}
|
|
|
|
|
2020-04-15 02:16:23 +02:00
|
|
|
@Override
|
|
|
|
public void edgeAdded(GraphEdgeChangeEvent<BlockPos, NetworkEdge> e) {
|
2020-04-17 17:18:25 +02:00
|
|
|
this.clearDestinationCache(e.getEdgeSource(), e.getEdgeTarget());
|
2020-04-15 02:16:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void edgeRemoved(GraphEdgeChangeEvent<BlockPos, NetworkEdge> e) {
|
2020-04-17 17:18:25 +02:00
|
|
|
this.clearDestinationCache(e.getEdgeSource(), e.getEdgeTarget());
|
2020-04-15 02:16:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void vertexAdded(GraphVertexChangeEvent<BlockPos> e) {
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void vertexRemoved(GraphVertexChangeEvent<BlockPos> e) {
|
|
|
|
}
|
2020-04-16 00:39:53 +02:00
|
|
|
|
2020-05-07 18:30:40 +02:00
|
|
|
public void startProfile(String name) {
|
2020-04-16 00:39:53 +02:00
|
|
|
this.world.getProfiler().startSection(() -> PrettyPipes.ID + ":pipe_network_" + name);
|
|
|
|
}
|
|
|
|
|
2020-05-07 18:30:40 +02:00
|
|
|
public void endProfile() {
|
2020-04-16 00:39:53 +02:00
|
|
|
this.world.getProfiler().endSection();
|
|
|
|
}
|
2020-04-17 21:32:01 +02:00
|
|
|
|
|
|
|
public static PipeNetwork get(World world) {
|
|
|
|
return world.getCapability(Registry.pipeNetworkCapability).orElse(null);
|
|
|
|
}
|
|
|
|
|
2020-04-14 04:21:28 +02:00
|
|
|
}
|