Skip to content

Commit 859bcb9

Browse files
authored
Fluid pipes rework, MultiFluid Pipes (GregTechCEu#53)
1 parent ba74b2f commit 859bcb9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1477
-770
lines changed

src/main/java/gregtech/api/pipenet/MonolithicPipeNet.java

-65
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package gregtech.api.pipenet;
2+
3+
import gregtech.api.pipenet.tile.IPipeTile;
4+
import net.minecraft.tileentity.TileEntity;
5+
import net.minecraft.util.EnumFacing;
6+
import net.minecraft.util.math.BlockPos;
7+
import net.minecraft.world.World;
8+
9+
import javax.annotation.Nullable;
10+
import java.util.ArrayList;
11+
import java.util.List;
12+
import java.util.function.Predicate;
13+
14+
public class PipeGatherer extends PipeNetWalker {
15+
16+
@Nullable
17+
public static IPipeTile<?, ?> findFirstMatching(PipeNet<?> net, World world, BlockPos sourcePipe, Predicate<IPipeTile<?, ?>> pipePredicate) {
18+
PipeGatherer gatherer = new PipeGatherer(net, world, sourcePipe, 1, pipePredicate, new ArrayList<>());
19+
gatherer.returnAfterFirst = true;
20+
gatherer.traversePipeNet();
21+
return gatherer.pipes.size() > 0 ? gatherer.pipes.get(0) : null;
22+
}
23+
24+
public static List<IPipeTile<?, ?>> gatherPipes(PipeNet<?> net, World world, BlockPos sourcePipe, Predicate<IPipeTile<?, ?>> pipePredicate) {
25+
PipeGatherer gatherer = new PipeGatherer(net, world, sourcePipe, 1, pipePredicate, new ArrayList<>());
26+
gatherer.traversePipeNet();
27+
return gatherer.pipes;
28+
}
29+
30+
public static List<IPipeTile<?, ?>> gatherPipesInDistance(PipeNet<?> net, World world, BlockPos sourcePipe, Predicate<IPipeTile<?, ?>> pipePredicate, int distance) {
31+
PipeGatherer gatherer = new PipeGatherer(net, world, sourcePipe, 1, pipePredicate, new ArrayList<>());
32+
gatherer.traversePipeNet(distance);
33+
return gatherer.pipes;
34+
}
35+
36+
private final Predicate<IPipeTile<?, ?>> pipePredicate;
37+
private final List<IPipeTile<?, ?>> pipes;
38+
private boolean returnAfterFirst = false;
39+
40+
protected PipeGatherer(PipeNet<?> net, World world, BlockPos sourcePipe, int walkedBlocks, Predicate<IPipeTile<?, ?>> pipePredicate, List<IPipeTile<?, ?>> pipes) {
41+
super(net, world, sourcePipe, walkedBlocks);
42+
this.pipePredicate = pipePredicate;
43+
this.pipes = pipes;
44+
}
45+
46+
@Override
47+
protected PipeNetWalker createSubWalker(PipeNet<?> net, World world, BlockPos nextPos, int walkedBlocks) {
48+
return new PipeGatherer(net, world, nextPos, walkedBlocks, pipePredicate, pipes);
49+
}
50+
51+
@Override
52+
protected void checkPipe(IPipeTile<?, ?> pipeTile, BlockPos pos) {
53+
if (pipePredicate.test(pipeTile)) {
54+
pipes.add(pipeTile);
55+
}
56+
}
57+
58+
@Override
59+
protected void checkNeighbour(IPipeTile<?, ?> pipeTile, BlockPos pipePos, EnumFacing faceToNeighbour, @Nullable TileEntity neighbourTile) {
60+
}
61+
62+
@Override
63+
protected boolean isValidPipe(IPipeTile<?, ?> currentPipe, IPipeTile<?, ?> neighbourPipe, BlockPos pipePos, EnumFacing faceToNeighbour) {
64+
return (!returnAfterFirst || pipes.size() <= 0) && pipePredicate.test(neighbourPipe);
65+
}
66+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
package gregtech.api.pipenet;
2+
3+
import gregtech.api.pipenet.tile.IPipeTile;
4+
import gregtech.api.util.GTLog;
5+
import gregtech.common.pipelike.fluidpipe.net.FluidNetWalker;
6+
import gregtech.common.pipelike.itempipe.net.ItemNetWalker;
7+
import net.minecraft.tileentity.TileEntity;
8+
import net.minecraft.util.EnumFacing;
9+
import net.minecraft.util.math.BlockPos;
10+
import net.minecraft.world.World;
11+
12+
import javax.annotation.Nullable;
13+
import java.util.*;
14+
15+
/**
16+
* This is a helper class to get information about a pipe net
17+
* <p>The walker is written that it will always find the shortest path to any destination
18+
* <p>On the way it can collect information about the pipes and it's neighbours
19+
* <p>After creating a walker simply call {@link #traversePipeNet()} to start walking, then you can just collect the data
20+
* <p><b>Do not walk a walker more than once</b>
21+
* <p>For example implementations look at {@link ItemNetWalker} and {@link FluidNetWalker}
22+
*/
23+
public abstract class PipeNetWalker {
24+
25+
private final PipeNet<?> net;
26+
private final World world;
27+
private final Set<IPipeTile<?, ?>> walked = new HashSet<>();
28+
private final List<EnumFacing> pipes = new ArrayList<>();
29+
private List<PipeNetWalker> walkers;
30+
private BlockPos currentPos;
31+
private int walkedBlocks;
32+
private boolean invalid;
33+
34+
protected PipeNetWalker(PipeNet<?> net, World world, BlockPos sourcePipe, int walkedBlocks) {
35+
this.world = Objects.requireNonNull(world);
36+
this.net = Objects.requireNonNull(net);
37+
this.walkedBlocks = walkedBlocks;
38+
this.currentPos = Objects.requireNonNull(sourcePipe);
39+
}
40+
41+
/**
42+
* Creates a sub walker
43+
* Will be called when a pipe has multiple valid pipes
44+
*
45+
* @param net pipe net
46+
* @param world world
47+
* @param nextPos next pos to check
48+
* @param walkedBlocks distance from source in blocks
49+
* @return new sub walker
50+
*/
51+
protected abstract PipeNetWalker createSubWalker(PipeNet<?> net, World world, BlockPos nextPos, int walkedBlocks);
52+
53+
/**
54+
* You can increase walking stats here. for example
55+
*
56+
* @param pipeTile current checking pipe
57+
* @param pos current pipe pos
58+
*/
59+
protected abstract void checkPipe(IPipeTile<?, ?> pipeTile, BlockPos pos);
60+
61+
/**
62+
* Checks the neighbour of the current pos
63+
* neighbourTile is NEVER an instance of {@link IPipeTile}
64+
*
65+
* @param pipePos current pos
66+
* @param faceToNeighbour face to neighbour
67+
* @param neighbourTile neighbour tile
68+
*/
69+
protected abstract void checkNeighbour(IPipeTile<?, ?> pipeTile, BlockPos pipePos, EnumFacing faceToNeighbour, @Nullable TileEntity neighbourTile);
70+
71+
/**
72+
* If the pipe is valid to perform a walk on
73+
*
74+
* @param currentPipe current pipe
75+
* @param neighbourPipe neighbour pipe to check
76+
* @param pipePos current pos (tile.getPipePos() != pipePos)
77+
* @param faceToNeighbour face to pipeTile
78+
* @return if the pipe is valid
79+
*/
80+
protected abstract boolean isValidPipe(IPipeTile<?, ?> currentPipe, IPipeTile<?, ?> neighbourPipe, BlockPos pipePos, EnumFacing faceToNeighbour);
81+
82+
public void traversePipeNet() {
83+
traversePipeNet(Integer.MAX_VALUE);
84+
}
85+
86+
/**
87+
* Starts walking the pipe net and gathers information.
88+
*
89+
* @param maxWalks max walks to prevent possible stack overflow
90+
* @throws IllegalStateException if the walker already walked
91+
*/
92+
public void traversePipeNet(int maxWalks) {
93+
if (invalid)
94+
throw new IllegalStateException("This walker already walked. Create a new one if you want to walk again");
95+
int i = 0;
96+
while (!walk() && i++ < maxWalks) ;
97+
walked.forEach(IPipeTile::resetWalk);
98+
invalid = true;
99+
}
100+
101+
private boolean walk() {
102+
if (walkers == null)
103+
checkPos(currentPos);
104+
105+
if (pipes.size() == 0)
106+
return true;
107+
if (pipes.size() == 1) {
108+
currentPos = currentPos.offset(pipes.get(0));
109+
walkedBlocks++;
110+
return false;
111+
}
112+
113+
if (walkers == null) {
114+
walkers = new ArrayList<>();
115+
for (EnumFacing side : pipes) {
116+
walkers.add(Objects.requireNonNull(createSubWalker(net, world, currentPos.offset(side), walkedBlocks + 1), "Walker can't be null"));
117+
}
118+
} else {
119+
Iterator<PipeNetWalker> iterator = walkers.iterator();
120+
while (iterator.hasNext()) {
121+
PipeNetWalker walker = iterator.next();
122+
if (walker.walk()) {
123+
walked.addAll(walker.walked);
124+
iterator.remove();
125+
}
126+
}
127+
}
128+
129+
return walkers.size() == 0;
130+
}
131+
132+
private void checkPos(BlockPos pos) {
133+
pipes.clear();
134+
TileEntity thisPipe = world.getTileEntity(pos);
135+
IPipeTile<?, ?> pipeTile = (IPipeTile<?, ?>) thisPipe;
136+
if (pipeTile == null) {
137+
if (walkedBlocks == 1) {
138+
// if it is the first block, it wasn't already checked
139+
GTLog.logger.warn("First PipeTile is null during walk");
140+
return;
141+
} else
142+
throw new IllegalStateException("PipeTile was not null last walk, but now is");
143+
}
144+
checkPipe(pipeTile, pos);
145+
pipeTile.markWalked();
146+
walked.add(pipeTile);
147+
148+
// check for surrounding pipes and item handlers
149+
for (EnumFacing accessSide : EnumFacing.VALUES) {
150+
//skip sides reported as blocked by pipe network
151+
if (!pipeTile.isConnectionOpenAny(accessSide))
152+
continue;
153+
154+
TileEntity tile = world.getTileEntity(pos.offset(accessSide));
155+
if (tile instanceof IPipeTile) {
156+
IPipeTile<?, ?> otherPipe = (IPipeTile<?, ?>) tile;
157+
if (otherPipe.isWalked())
158+
continue;
159+
if (isValidPipe(pipeTile, otherPipe, pos, accessSide)) {
160+
pipes.add(accessSide);
161+
continue;
162+
}
163+
}
164+
checkNeighbour(pipeTile, pos, accessSide, tile);
165+
}
166+
}
167+
168+
public PipeNet<?> getNet() {
169+
return net;
170+
}
171+
172+
public World getWorld() {
173+
return world;
174+
}
175+
176+
public BlockPos getCurrentPos() {
177+
return currentPos;
178+
}
179+
180+
public int getWalkedBlocks() {
181+
return walkedBlocks;
182+
}
183+
}

src/main/java/gregtech/api/pipenet/block/BlockPipe.java

+2-3
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ public void neighborChanged(@Nonnull IBlockState state, @Nonnull World worldIn,
160160
}
161161
}
162162
if (facing == null) throw new NullPointerException("Facing is null");
163-
boolean open = pipeTile.isConnectionOpenVisual(facing);
163+
boolean open = pipeTile.isConnectionOpenAny(facing);
164164
boolean canConnect = canConnect(pipeTile, facing);
165165
if (!open && canConnect && !ConfigHolder.U.GT6.gt6StylePipesCables)
166166
pipeTile.setConnectionBlocked(AttachmentType.PIPE, facing, false, false);
@@ -193,7 +193,6 @@ public int getWeakPower(@Nonnull IBlockState blockState, @Nonnull IBlockAccess b
193193
public void updateActiveNodeStatus(World worldIn, BlockPos pos, IPipeTile<PipeType, NodeDataType> pipeTile) {
194194
PipeNet<NodeDataType> pipeNet = getWorldPipeNet(worldIn).getNetFromPos(pos);
195195
if (pipeNet != null && pipeTile != null) {
196-
//int activeConnections = ~getActiveNodeConnections(worldIn, pos, pipeTile);
197196
int activeConnections = pipeTile.getOpenConnections(); //remove blocked connections
198197
boolean isActiveNodeNow = activeConnections != 0;
199198
boolean modeChanged = pipeNet.markNodeAsActive(pos, isActiveNodeNow);
@@ -407,7 +406,7 @@ public int getVisualConnections(IPipeTile<PipeType, NodeDataType> selfTile) {
407406
int connections = selfTile.getOpenConnections();
408407
for (EnumFacing facing : EnumFacing.values()) {
409408
// continue if connection is already open
410-
if (selfTile.isConnectionOpenVisual(facing)) continue;
409+
if (selfTile.isConnectionOpenAny(facing)) continue;
411410
CoverBehavior cover = selfTile.getCoverableImplementation().getCoverAtSide(facing);
412411
if (cover == null) continue;
413412
// adds side to open connections of it isn't already open & has a cover

src/main/java/gregtech/api/pipenet/tickable/TickableWorldPipeNet.java

+6-6
Original file line numberDiff line numberDiff line change
@@ -8,37 +8,36 @@
88
import net.minecraft.world.chunk.Chunk;
99
import org.apache.commons.lang3.tuple.Pair;
1010

11-
import java.util.ArrayList;
12-
import java.util.HashMap;
13-
import java.util.List;
14-
import java.util.Map;
11+
import java.util.*;
1512
import java.util.stream.Collectors;
1613

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

1916
private final Map<T, List<ChunkPos>> loadedChunksByPipeNet = new HashMap<>();
20-
private final List<T> tickingPipeNets = new ArrayList<>();
17+
private final Set<T> tickingPipeNets = new HashSet<>();
2118

2219
public TickableWorldPipeNet(String name) {
2320
super(name);
2421
}
2522

2623
private boolean isChunkLoaded(ChunkPos chunkPos) {
2724
WorldServer worldServer = (WorldServer) getWorld();
25+
if (worldServer == null) return false;
2826
return worldServer.getChunkProvider().chunkExists(chunkPos.x, chunkPos.z);
2927
}
3028

3129
protected abstract int getUpdateRate();
3230

3331
public void update() {
3432
if (getWorld().getTotalWorldTime() % getUpdateRate() == 0L) {
35-
tickingPipeNets.forEach(ITickable::update);
33+
tickingPipeNets.forEach(net -> net.update());
3634
}
3735
}
3836

3937
public void onChunkLoaded(Chunk chunk) {
4038
ChunkPos chunkPos = chunk.getPos();
4139
List<T> pipeNetsInThisChunk = this.pipeNetsByChunk.get(chunkPos);
40+
if (pipeNetsInThisChunk == null) return;
4241
for (T pipeNet : pipeNetsInThisChunk) {
4342
List<ChunkPos> loadedChunks = getOrCreateChunkListForPipeNet(pipeNet);
4443
if (loadedChunks.isEmpty()) {
@@ -51,6 +50,7 @@ public void onChunkLoaded(Chunk chunk) {
5150
public void onChunkUnloaded(Chunk chunk) {
5251
ChunkPos chunkPos = chunk.getPos();
5352
List<T> pipeNetsInThisChunk = this.pipeNetsByChunk.get(chunkPos);
53+
if (pipeNetsInThisChunk == null) return;
5454
for (T pipeNet : pipeNetsInThisChunk) {
5555
List<ChunkPos> loadedChunks = this.loadedChunksByPipeNet.get(pipeNet);
5656
if (loadedChunks != null && loadedChunks.contains(chunkPos)) {

0 commit comments

Comments
 (0)