Skip to content

Commit

Permalink
Fluid pipes rework, MultiFluid Pipes (GregTechCEu#53)
Browse files Browse the repository at this point in the history
  • Loading branch information
brachy84 authored Aug 14, 2021
1 parent ba74b2f commit 859bcb9
Show file tree
Hide file tree
Showing 42 changed files with 1,477 additions and 770 deletions.
65 changes: 0 additions & 65 deletions src/main/java/gregtech/api/pipenet/MonolithicPipeNet.java

This file was deleted.

66 changes: 66 additions & 0 deletions src/main/java/gregtech/api/pipenet/PipeGatherer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package gregtech.api.pipenet;

import gregtech.api.pipenet.tile.IPipeTile;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;

import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;

public class PipeGatherer extends PipeNetWalker {

@Nullable
public static IPipeTile<?, ?> findFirstMatching(PipeNet<?> net, World world, BlockPos sourcePipe, Predicate<IPipeTile<?, ?>> pipePredicate) {
PipeGatherer gatherer = new PipeGatherer(net, world, sourcePipe, 1, pipePredicate, new ArrayList<>());
gatherer.returnAfterFirst = true;
gatherer.traversePipeNet();
return gatherer.pipes.size() > 0 ? gatherer.pipes.get(0) : null;
}

public static List<IPipeTile<?, ?>> gatherPipes(PipeNet<?> net, World world, BlockPos sourcePipe, Predicate<IPipeTile<?, ?>> pipePredicate) {
PipeGatherer gatherer = new PipeGatherer(net, world, sourcePipe, 1, pipePredicate, new ArrayList<>());
gatherer.traversePipeNet();
return gatherer.pipes;
}

public static List<IPipeTile<?, ?>> gatherPipesInDistance(PipeNet<?> net, World world, BlockPos sourcePipe, Predicate<IPipeTile<?, ?>> pipePredicate, int distance) {
PipeGatherer gatherer = new PipeGatherer(net, world, sourcePipe, 1, pipePredicate, new ArrayList<>());
gatherer.traversePipeNet(distance);
return gatherer.pipes;
}

private final Predicate<IPipeTile<?, ?>> pipePredicate;
private final List<IPipeTile<?, ?>> pipes;
private boolean returnAfterFirst = false;

protected PipeGatherer(PipeNet<?> net, World world, BlockPos sourcePipe, int walkedBlocks, Predicate<IPipeTile<?, ?>> pipePredicate, List<IPipeTile<?, ?>> pipes) {
super(net, world, sourcePipe, walkedBlocks);
this.pipePredicate = pipePredicate;
this.pipes = pipes;
}

@Override
protected PipeNetWalker createSubWalker(PipeNet<?> net, World world, BlockPos nextPos, int walkedBlocks) {
return new PipeGatherer(net, world, nextPos, walkedBlocks, pipePredicate, pipes);
}

@Override
protected void checkPipe(IPipeTile<?, ?> pipeTile, BlockPos pos) {
if (pipePredicate.test(pipeTile)) {
pipes.add(pipeTile);
}
}

@Override
protected void checkNeighbour(IPipeTile<?, ?> pipeTile, BlockPos pipePos, EnumFacing faceToNeighbour, @Nullable TileEntity neighbourTile) {
}

@Override
protected boolean isValidPipe(IPipeTile<?, ?> currentPipe, IPipeTile<?, ?> neighbourPipe, BlockPos pipePos, EnumFacing faceToNeighbour) {
return (!returnAfterFirst || pipes.size() <= 0) && pipePredicate.test(neighbourPipe);
}
}
183 changes: 183 additions & 0 deletions src/main/java/gregtech/api/pipenet/PipeNetWalker.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
package gregtech.api.pipenet;

import gregtech.api.pipenet.tile.IPipeTile;
import gregtech.api.util.GTLog;
import gregtech.common.pipelike.fluidpipe.net.FluidNetWalker;
import gregtech.common.pipelike.itempipe.net.ItemNetWalker;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;

import javax.annotation.Nullable;
import java.util.*;

/**
* This is a helper class to get information about a pipe net
* <p>The walker is written that it will always find the shortest path to any destination
* <p>On the way it can collect information about the pipes and it's neighbours
* <p>After creating a walker simply call {@link #traversePipeNet()} to start walking, then you can just collect the data
* <p><b>Do not walk a walker more than once</b>
* <p>For example implementations look at {@link ItemNetWalker} and {@link FluidNetWalker}
*/
public abstract class PipeNetWalker {

private final PipeNet<?> net;
private final World world;
private final Set<IPipeTile<?, ?>> walked = new HashSet<>();
private final List<EnumFacing> pipes = new ArrayList<>();
private List<PipeNetWalker> walkers;
private BlockPos currentPos;
private int walkedBlocks;
private boolean invalid;

protected PipeNetWalker(PipeNet<?> net, World world, BlockPos sourcePipe, int walkedBlocks) {
this.world = Objects.requireNonNull(world);
this.net = Objects.requireNonNull(net);
this.walkedBlocks = walkedBlocks;
this.currentPos = Objects.requireNonNull(sourcePipe);
}

/**
* Creates a sub walker
* Will be called when a pipe has multiple valid pipes
*
* @param net pipe net
* @param world world
* @param nextPos next pos to check
* @param walkedBlocks distance from source in blocks
* @return new sub walker
*/
protected abstract PipeNetWalker createSubWalker(PipeNet<?> net, World world, BlockPos nextPos, int walkedBlocks);

/**
* You can increase walking stats here. for example
*
* @param pipeTile current checking pipe
* @param pos current pipe pos
*/
protected abstract void checkPipe(IPipeTile<?, ?> pipeTile, BlockPos pos);

/**
* Checks the neighbour of the current pos
* neighbourTile is NEVER an instance of {@link IPipeTile}
*
* @param pipePos current pos
* @param faceToNeighbour face to neighbour
* @param neighbourTile neighbour tile
*/
protected abstract void checkNeighbour(IPipeTile<?, ?> pipeTile, BlockPos pipePos, EnumFacing faceToNeighbour, @Nullable TileEntity neighbourTile);

/**
* If the pipe is valid to perform a walk on
*
* @param currentPipe current pipe
* @param neighbourPipe neighbour pipe to check
* @param pipePos current pos (tile.getPipePos() != pipePos)
* @param faceToNeighbour face to pipeTile
* @return if the pipe is valid
*/
protected abstract boolean isValidPipe(IPipeTile<?, ?> currentPipe, IPipeTile<?, ?> neighbourPipe, BlockPos pipePos, EnumFacing faceToNeighbour);

public void traversePipeNet() {
traversePipeNet(Integer.MAX_VALUE);
}

/**
* Starts walking the pipe net and gathers information.
*
* @param maxWalks max walks to prevent possible stack overflow
* @throws IllegalStateException if the walker already walked
*/
public void traversePipeNet(int maxWalks) {
if (invalid)
throw new IllegalStateException("This walker already walked. Create a new one if you want to walk again");
int i = 0;
while (!walk() && i++ < maxWalks) ;
walked.forEach(IPipeTile::resetWalk);
invalid = true;
}

private boolean walk() {
if (walkers == null)
checkPos(currentPos);

if (pipes.size() == 0)
return true;
if (pipes.size() == 1) {
currentPos = currentPos.offset(pipes.get(0));
walkedBlocks++;
return false;
}

if (walkers == null) {
walkers = new ArrayList<>();
for (EnumFacing side : pipes) {
walkers.add(Objects.requireNonNull(createSubWalker(net, world, currentPos.offset(side), walkedBlocks + 1), "Walker can't be null"));
}
} else {
Iterator<PipeNetWalker> iterator = walkers.iterator();
while (iterator.hasNext()) {
PipeNetWalker walker = iterator.next();
if (walker.walk()) {
walked.addAll(walker.walked);
iterator.remove();
}
}
}

return walkers.size() == 0;
}

private void checkPos(BlockPos pos) {
pipes.clear();
TileEntity thisPipe = world.getTileEntity(pos);
IPipeTile<?, ?> pipeTile = (IPipeTile<?, ?>) thisPipe;
if (pipeTile == null) {
if (walkedBlocks == 1) {
// if it is the first block, it wasn't already checked
GTLog.logger.warn("First PipeTile is null during walk");
return;
} else
throw new IllegalStateException("PipeTile was not null last walk, but now is");
}
checkPipe(pipeTile, pos);
pipeTile.markWalked();
walked.add(pipeTile);

// check for surrounding pipes and item handlers
for (EnumFacing accessSide : EnumFacing.VALUES) {
//skip sides reported as blocked by pipe network
if (!pipeTile.isConnectionOpenAny(accessSide))
continue;

TileEntity tile = world.getTileEntity(pos.offset(accessSide));
if (tile instanceof IPipeTile) {
IPipeTile<?, ?> otherPipe = (IPipeTile<?, ?>) tile;
if (otherPipe.isWalked())
continue;
if (isValidPipe(pipeTile, otherPipe, pos, accessSide)) {
pipes.add(accessSide);
continue;
}
}
checkNeighbour(pipeTile, pos, accessSide, tile);
}
}

public PipeNet<?> getNet() {
return net;
}

public World getWorld() {
return world;
}

public BlockPos getCurrentPos() {
return currentPos;
}

public int getWalkedBlocks() {
return walkedBlocks;
}
}
5 changes: 2 additions & 3 deletions src/main/java/gregtech/api/pipenet/block/BlockPipe.java
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ public void neighborChanged(@Nonnull IBlockState state, @Nonnull World worldIn,
}
}
if (facing == null) throw new NullPointerException("Facing is null");
boolean open = pipeTile.isConnectionOpenVisual(facing);
boolean open = pipeTile.isConnectionOpenAny(facing);
boolean canConnect = canConnect(pipeTile, facing);
if (!open && canConnect && !ConfigHolder.U.GT6.gt6StylePipesCables)
pipeTile.setConnectionBlocked(AttachmentType.PIPE, facing, false, false);
Expand Down Expand Up @@ -193,7 +193,6 @@ public int getWeakPower(@Nonnull IBlockState blockState, @Nonnull IBlockAccess b
public void updateActiveNodeStatus(World worldIn, BlockPos pos, IPipeTile<PipeType, NodeDataType> pipeTile) {
PipeNet<NodeDataType> pipeNet = getWorldPipeNet(worldIn).getNetFromPos(pos);
if (pipeNet != null && pipeTile != null) {
//int activeConnections = ~getActiveNodeConnections(worldIn, pos, pipeTile);
int activeConnections = pipeTile.getOpenConnections(); //remove blocked connections
boolean isActiveNodeNow = activeConnections != 0;
boolean modeChanged = pipeNet.markNodeAsActive(pos, isActiveNodeNow);
Expand Down Expand Up @@ -407,7 +406,7 @@ public int getVisualConnections(IPipeTile<PipeType, NodeDataType> selfTile) {
int connections = selfTile.getOpenConnections();
for (EnumFacing facing : EnumFacing.values()) {
// continue if connection is already open
if (selfTile.isConnectionOpenVisual(facing)) continue;
if (selfTile.isConnectionOpenAny(facing)) continue;
CoverBehavior cover = selfTile.getCoverableImplementation().getCoverAtSide(facing);
if (cover == null) continue;
// adds side to open connections of it isn't already open & has a cover
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,37 +8,36 @@
import net.minecraft.world.chunk.Chunk;
import org.apache.commons.lang3.tuple.Pair;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;

public abstract class TickableWorldPipeNet<NodeDataType, T extends PipeNet<NodeDataType> & ITickable> extends WorldPipeNet<NodeDataType, T> {

private final Map<T, List<ChunkPos>> loadedChunksByPipeNet = new HashMap<>();
private final List<T> tickingPipeNets = new ArrayList<>();
private final Set<T> tickingPipeNets = new HashSet<>();

public TickableWorldPipeNet(String name) {
super(name);
}

private boolean isChunkLoaded(ChunkPos chunkPos) {
WorldServer worldServer = (WorldServer) getWorld();
if (worldServer == null) return false;
return worldServer.getChunkProvider().chunkExists(chunkPos.x, chunkPos.z);
}

protected abstract int getUpdateRate();

public void update() {
if (getWorld().getTotalWorldTime() % getUpdateRate() == 0L) {
tickingPipeNets.forEach(ITickable::update);
tickingPipeNets.forEach(net -> net.update());
}
}

public void onChunkLoaded(Chunk chunk) {
ChunkPos chunkPos = chunk.getPos();
List<T> pipeNetsInThisChunk = this.pipeNetsByChunk.get(chunkPos);
if (pipeNetsInThisChunk == null) return;
for (T pipeNet : pipeNetsInThisChunk) {
List<ChunkPos> loadedChunks = getOrCreateChunkListForPipeNet(pipeNet);
if (loadedChunks.isEmpty()) {
Expand All @@ -51,6 +50,7 @@ public void onChunkLoaded(Chunk chunk) {
public void onChunkUnloaded(Chunk chunk) {
ChunkPos chunkPos = chunk.getPos();
List<T> pipeNetsInThisChunk = this.pipeNetsByChunk.get(chunkPos);
if (pipeNetsInThisChunk == null) return;
for (T pipeNet : pipeNetsInThisChunk) {
List<ChunkPos> loadedChunks = this.loadedChunksByPipeNet.get(pipeNet);
if (loadedChunks != null && loadedChunks.contains(chunkPos)) {
Expand Down
Loading

0 comments on commit 859bcb9

Please sign in to comment.