diff --git a/src/main/java/com/simibubi/create/AllBlockPartials.java b/src/main/java/com/simibubi/create/AllBlockPartials.java index 7fdcfb261..ddb43ea20 100644 --- a/src/main/java/com/simibubi/create/AllBlockPartials.java +++ b/src/main/java/com/simibubi/create/AllBlockPartials.java @@ -9,7 +9,7 @@ import java.util.List; import java.util.Map; import com.mojang.blaze3d.matrix.MatrixStack; -import com.simibubi.create.content.contraptions.fluids.FluidPipeAttachmentBehaviour.AttachmentTypes; +import com.simibubi.create.content.contraptions.fluids.FluidTransportBehaviour.AttachmentTypes; import com.simibubi.create.content.contraptions.processing.burner.BlazeBurnerBlock.HeatLevel; import com.simibubi.create.foundation.utility.AngleHelper; import com.simibubi.create.foundation.utility.Iterate; diff --git a/src/main/java/com/simibubi/create/content/contraptions/fluids/FlowSource.java b/src/main/java/com/simibubi/create/content/contraptions/fluids/FlowSource.java new file mode 100644 index 000000000..e2beb3a37 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/fluids/FlowSource.java @@ -0,0 +1,137 @@ +package com.simibubi.create.content.contraptions.fluids; + +import java.lang.ref.WeakReference; +import java.util.function.Predicate; + +import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; +import com.simibubi.create.foundation.utility.BlockFace; + +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.World; +import net.minecraftforge.common.util.LazyOptional; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.capability.CapabilityFluidHandler; +import net.minecraftforge.fluids.capability.IFluidHandler; +import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction; + +public abstract class FlowSource { + + private static final LazyOptional EMPTY = LazyOptional.empty(); + + BlockFace location; + + public FlowSource(BlockFace location) { + this.location = location; + } + + public FluidStack provideFluid(Predicate extractionPredicate) { + IFluidHandler tank = provideHandler().orElse(null); + if (tank == null) + return FluidStack.EMPTY; + FluidStack immediateFluid = tank.drain(1, FluidAction.SIMULATE); + if (extractionPredicate.test(immediateFluid)) + return immediateFluid; + + for (int i = 0; i < tank.getTanks(); i++) { + FluidStack contained = tank.getFluidInTank(i); + if (contained.isEmpty()) + continue; + if (!extractionPredicate.test(contained)) + continue; + FluidStack toExtract = contained.copy(); + toExtract.setAmount(1); + return tank.drain(toExtract, FluidAction.SIMULATE); + } + + return FluidStack.EMPTY; + } + + // Layer III. PFIs need active attention to prevent them from disengaging early + public void keepAlive() {} + + public abstract boolean isEndpoint(); + + public void manageSource(World world) {} + + public void whileFlowPresent(World world, boolean pulling) {} + + public LazyOptional provideHandler() { + return EMPTY; + } + + public static class FluidHandler extends FlowSource { + LazyOptional fluidHandler; + + public FluidHandler(BlockFace location) { + super(location); + fluidHandler = EMPTY; + } + + public void manageSource(World world) { + if (fluidHandler.isPresent()) + return; + TileEntity tileEntity = world.getTileEntity(location.getConnectedPos()); + if (tileEntity != null) + fluidHandler = tileEntity.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, + location.getOppositeFace()); + } + + @Override + public LazyOptional provideHandler() { + return fluidHandler; + } + + @Override + public boolean isEndpoint() { + return true; + } + } + + public static class OtherPipe extends FlowSource { + WeakReference cached; + + public OtherPipe(BlockFace location) { + super(location); + } + + @Override + public void manageSource(World world) { + if (cached != null && cached.get() != null && !cached.get().tileEntity.isRemoved()) + return; + cached = null; + FluidTransportBehaviour fluidTransportBehaviour = + TileEntityBehaviour.get(world, location.getConnectedPos(), FluidTransportBehaviour.TYPE); + if (fluidTransportBehaviour != null) + cached = new WeakReference<>(fluidTransportBehaviour); + } + + @Override + public FluidStack provideFluid(Predicate extractionPredicate) { + if (cached == null || cached.get() == null) + return FluidStack.EMPTY; + FluidTransportBehaviour behaviour = cached.get(); + FluidStack providedOutwardFluid = behaviour.getProvidedOutwardFluid(location.getOppositeFace()); + return extractionPredicate.test(providedOutwardFluid) ? providedOutwardFluid : FluidStack.EMPTY; + } + + @Override + public boolean isEndpoint() { + return false; + } + + } + + public static class Blocked extends FlowSource { + + public Blocked(BlockFace location) { + super(location); + } + + @Override + public boolean isEndpoint() { + return false; + } + + } + +} diff --git a/src/main/java/com/simibubi/create/content/contraptions/fluids/FluidNetwork.java b/src/main/java/com/simibubi/create/content/contraptions/fluids/FluidNetwork.java index 32aad3077..8a3305e74 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/fluids/FluidNetwork.java +++ b/src/main/java/com/simibubi/create/content/contraptions/fluids/FluidNetwork.java @@ -1,359 +1,263 @@ package com.simibubi.create.content.contraptions.fluids; +import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Supplier; -import com.google.common.collect.ImmutableList; +import javax.annotation.Nullable; + +import com.simibubi.create.content.contraptions.fluids.PipeConnection.Flow; +import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; import com.simibubi.create.foundation.utility.BlockFace; +import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.Pair; -import net.minecraft.block.BlockState; -import net.minecraft.tileentity.TileEntity; import net.minecraft.util.Direction; import net.minecraft.util.math.BlockPos; -import net.minecraft.world.IWorld; import net.minecraft.world.World; import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.fluids.FluidStack; -import net.minecraftforge.fluids.capability.CapabilityFluidHandler; import net.minecraftforge.fluids.capability.IFluidHandler; +import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction; public class FluidNetwork { - BlockFace pumpLocation; - Map>> pipeGraph; - List flows; - Set targets; - Set rangeEndpoints; - Map previousFlow; + private static int CYCLES_PER_TICK = 16; + + World world; + BlockFace start; - boolean connectToPumps; - int waitForUnloadedNetwork; + Supplier> sourceSupplier; + LazyOptional source; + int transferSpeed; - public FluidNetwork() { - pipeGraph = new HashMap<>(); - flows = new ArrayList<>(); - targets = new HashSet<>(); - rangeEndpoints = new HashSet<>(); - previousFlow = new HashMap<>(); + int pauseBeforePropagation; + List queued; + Set> frontier; + Set visited; + List>> targets; + Map> cache; + + public FluidNetwork(World world, BlockFace location, Supplier> sourceSupplier) { + this.world = world; + this.start = location; + this.sourceSupplier = sourceSupplier; + this.source = LazyOptional.empty(); + this.frontier = new HashSet<>(); + this.visited = new HashSet<>(); + this.targets = new ArrayList<>(); + this.cache = new HashMap<>(); + this.queued = new ArrayList<>(); + reset(); } - public boolean hasEndpoints() { - for (FluidNetworkFlow pipeFlow : flows) - if (pipeFlow.hasValidTargets()) - return true; - return false; - } - - public Collection getEndpoints(boolean pulling) { - if (!pulling) { - for (FluidNetworkFlow pipeFlow : flows) - return pipeFlow.outputEndpoints; - return Collections.emptySet(); - } - - List list = new ArrayList<>(); - for (FluidNetworkFlow pipeFlow : flows) { - if (!pipeFlow.hasValidTargets()) - continue; - list.add(pipeFlow.source); - } - return list; - } - - public void tick(IWorld world, PumpTileEntity pumpTE) { - if (connectToPumps) { - connectToOtherFNs(world, pumpTE); - connectToPumps = false; - } - } - - public void tickFlows(IWorld world, PumpTileEntity pumpTE, boolean pulling, float speed) { - if (connectToPumps) + public void tick() { + if (pauseBeforePropagation > 0) { + pauseBeforePropagation--; return; - initFlows(pumpTE, pulling); - previousFlow.clear(); - flows.forEach(ep -> ep.tick(world, speed)); - } + } + + for (int cycle = 0; cycle < CYCLES_PER_TICK; cycle++) { + boolean shouldContinue = false; + for (Iterator iterator = queued.iterator(); iterator.hasNext();) { + BlockFace blockFace = iterator.next(); + if (!isPresent(blockFace)) + continue; + PipeConnection pipeConnection = get(blockFace); + if (pipeConnection != null) { + if (blockFace.equals(start)) + transferSpeed = (int) Math.max(1, pipeConnection.pressure.get(true) / 2f); + frontier.add(Pair.of(blockFace, pipeConnection)); + } + iterator.remove(); + } + +// drawDebugOutlines(); + + for (Iterator> iterator = frontier.iterator(); iterator.hasNext();) { + Pair pair = iterator.next(); + BlockFace blockFace = pair.getFirst(); + PipeConnection pipeConnection = pair.getSecond(); + + if (!pipeConnection.hasFlow()) + continue; + Flow flow = pipeConnection.flow.get(); + if (!flow.inbound) { + if (pipeConnection.comparePressure() >= 0) + iterator.remove(); + continue; + } + if (!flow.complete) + continue; + + boolean canRemove = true; + for (Direction side : Iterate.directions) { + if (side == blockFace.getFace()) + continue; + BlockFace adjacentLocation = new BlockFace(blockFace.getPos(), side); + PipeConnection adjacent = get(adjacentLocation); + if (adjacent == null) + continue; + if (!adjacent.hasFlow()) { + // Branch could potentially still appear + if (adjacent.hasPressure() && adjacent.pressure.getSecond() > 0) + canRemove = false; + continue; + } + Flow outFlow = adjacent.flow.get(); + if (outFlow.inbound) { + if (adjacent.comparePressure() > 0) + canRemove = false; + continue; + } + if (!outFlow.complete) { + canRemove = false; + continue; + } + + if (adjacent.source.isPresent() && adjacent.source.get() + .isEndpoint()) { + targets.add(Pair.of(adjacentLocation, adjacent.source.get() + .provideHandler())); + continue; + } + + if (visited.add(adjacentLocation.getConnectedPos())) { + queued.add(adjacentLocation.getOpposite()); + shouldContinue = true; + } + } + if (canRemove) + iterator.remove(); + } + if (!shouldContinue) + break; + } + +// drawDebugOutlines(); - private void initFlows(PumpTileEntity pumpTE, boolean pulling) { + if (!source.isPresent()) + source = sourceSupplier.get(); + if (!source.isPresent()) + return; if (targets.isEmpty()) return; - if (!flows.isEmpty()) - return; - World world = pumpTE.getWorld(); - if (pulling) { - targets.forEach(ne -> flows.add(new FluidNetworkFlow(this, ne, world, pulling))); - } else { - PumpEndpoint pumpEndpoint = new PumpEndpoint(pumpLocation.getOpposite(), pumpTE); - flows.add(new FluidNetworkFlow(this, pumpEndpoint, world, pulling)); - } - } - - public void connectToOtherFNs(IWorld world, PumpTileEntity pump) { - List> frontier = new ArrayList<>(); - Set visited = new HashSet<>(); - int maxDistance = FluidPropagator.getPumpRange() * 2; - frontier.add(Pair.of(-1, pumpLocation.getPos())); - - while (!frontier.isEmpty()) { - Pair entry = frontier.remove(0); - int distance = entry.getFirst(); - BlockPos currentPos = entry.getSecond(); - - if (!world.isAreaLoaded(currentPos, 0)) + for (Pair> pair : targets) { + if (pair.getSecond() + .isPresent()) continue; - if (visited.contains(currentPos)) + PipeConnection pipeConnection = get(pair.getFirst()); + if (pipeConnection == null) continue; - visited.add(currentPos); - - List connections; - if (currentPos.equals(pumpLocation.getPos())) { - connections = ImmutableList.of(pumpLocation.getFace()); - } else { - BlockState currentState = world.getBlockState(currentPos); - FluidPipeBehaviour pipe = FluidPropagator.getPipe(world, currentPos); - if (pipe == null) - continue; - connections = FluidPropagator.getPipeConnections(currentState, pipe); - } - - for (Direction face : connections) { - BlockFace blockFace = new BlockFace(currentPos, face); - BlockPos connectedPos = blockFace.getConnectedPos(); - BlockState connectedState = world.getBlockState(connectedPos); - - if (connectedPos.equals(pumpLocation.getPos())) - continue; - if (!world.isAreaLoaded(connectedPos, 0)) - continue; - if (PumpBlock.isPump(connectedState) && connectedState.get(PumpBlock.FACING) - .getAxis() == face.getAxis()) { - TileEntity tileEntity = world.getTileEntity(connectedPos); - if (tileEntity instanceof PumpTileEntity) { - PumpTileEntity otherPump = (PumpTileEntity) tileEntity; - if (otherPump.networks == null) - continue; - - otherPump.networks.forEach(fn -> { - int nearest = Integer.MAX_VALUE; - BlockFace argNearest = null; - for (BlockFace pumpEndpoint : fn.rangeEndpoints) { - if (pumpEndpoint.isEquivalent(pumpLocation)) { - argNearest = pumpEndpoint; - break; - } - Pair> pair = - pipeGraph.get(pumpEndpoint.getConnectedPos()); - if (pair == null) - continue; - Integer distanceFromPump = pair.getFirst(); - Map pipeConnections = pair.getSecond(); - - if (!pipeConnections.containsKey(pumpEndpoint.getOppositeFace())) - continue; - if (nearest <= distanceFromPump) - continue; - nearest = distanceFromPump; - argNearest = pumpEndpoint; - - } - if (argNearest != null) { - InterPumpEndpoint endpoint = new InterPumpEndpoint(world, argNearest.getOpposite(), - pump, otherPump, pumpLocation, fn.pumpLocation); - targets.add(endpoint); - fn.targets.add(endpoint.opposite(world)); - } - }); - - } - continue; - } - if (visited.contains(connectedPos)) - continue; - if (distance > maxDistance) - continue; - FluidPipeBehaviour targetPipe = FluidPropagator.getPipe(world, connectedPos); - if (targetPipe == null) - continue; - if (targetPipe.isConnectedTo(connectedState, face.getOpposite())) - frontier.add(Pair.of(distance + 1, connectedPos)); - } - } - - } - - public void assemble(IWorld world, PumpTileEntity pumpTE, BlockFace pumpLocation) { - Map openEnds = pumpTE.getOpenEnds(pumpLocation.getFace()); - openEnds.values() - .forEach(OpenEndedPipe::markStale); - - this.pumpLocation = pumpLocation; - if (!collectEndpoint(world, pumpLocation, openEnds, 0)) { - - List> frontier = new ArrayList<>(); - Set visited = new HashSet<>(); - int maxDistance = FluidPropagator.getPumpRange(); - frontier.add(Pair.of(0, pumpLocation.getConnectedPos())); - - while (!frontier.isEmpty()) { - Pair entry = frontier.remove(0); - int distance = entry.getFirst(); - BlockPos currentPos = entry.getSecond(); - - if (!world.isAreaLoaded(currentPos, 0)) - continue; - if (visited.contains(currentPos)) - continue; - visited.add(currentPos); - BlockState currentState = world.getBlockState(currentPos); - FluidPipeBehaviour pipe = FluidPropagator.getPipe(world, currentPos); - if (pipe == null) - continue; - - for (Direction face : FluidPropagator.getPipeConnections(currentState, pipe)) { - if (!pipe.canTransferToward(FluidStack.EMPTY, world.getBlockState(currentPos), face, false)) - continue; - - BlockFace blockFace = new BlockFace(currentPos, face); - BlockPos connectedPos = blockFace.getConnectedPos(); - - if (connectedPos.equals(pumpLocation.getPos())) { - addEntry(blockFace.getPos(), blockFace.getFace(), true, distance); - continue; - } - if (!world.isAreaLoaded(connectedPos, 0)) - continue; - if (collectEndpoint(world, blockFace, openEnds, distance)) - continue; - FluidPipeBehaviour pipeBehaviour = FluidPropagator.getPipe(world, connectedPos); - if (pipeBehaviour == null) - continue; - if (visited.contains(connectedPos)) - continue; - if (distance + 1 >= maxDistance) { - rangeEndpoints.add(blockFace); - addEntry(currentPos, face, false, distance); - FluidPropagator.showBlockFace(blockFace) - .lineWidth(1 / 8f) - .colored(0xff0000); - continue; - } - - addConnection(connectedPos, currentPos, face.getOpposite(), distance); - frontier.add(Pair.of(distance + 1, connectedPos)); - } - } - } - - Set staleEnds = new HashSet<>(); - openEnds.entrySet() - .forEach(e -> { - if (e.getValue() - .isStale()) - staleEnds.add(e.getKey()); + pipeConnection.source.ifPresent(fs -> { + if (fs.isEndpoint()) + pair.setSecond(fs.provideHandler()); }); - staleEnds.forEach(openEnds::remove); - - connectToPumps = true; - } - - private FluidNetworkEndpoint reuseOrCreateOpenEnd(IWorld world, Map openEnds, - BlockFace toCreate) { - OpenEndedPipe openEndedPipe = null; - if (openEnds.containsKey(toCreate)) { - openEndedPipe = openEnds.get(toCreate); - openEndedPipe.unmarkStale(); - } else { - openEndedPipe = new OpenEndedPipe(toCreate); - openEnds.put(toCreate, openEndedPipe); } - return new FluidNetworkEndpoint(world, toCreate, openEndedPipe.getCapability()); - } + int flowSpeed = transferSpeed; + for (boolean simulate : Iterate.trueAndFalse) { + FluidAction action = simulate ? FluidAction.SIMULATE : FluidAction.EXECUTE; - private boolean collectEndpoint(IWorld world, BlockFace blockFace, Map openEnds, - int distance) { - BlockPos connectedPos = blockFace.getConnectedPos(); - BlockState connectedState = world.getBlockState(connectedPos); + IFluidHandler handler = source.orElse(null); + if (handler == null) + return; + FluidStack transfer = handler.drain(flowSpeed, action); + if (transfer.isEmpty()) + return; - // other pipe, no endpoint - FluidPipeBehaviour pipe = FluidPropagator.getPipe(world, connectedPos); - if (pipe != null && pipe.isConnectedTo(connectedState, blockFace.getOppositeFace())) - return false; - TileEntity tileEntity = world.getTileEntity(connectedPos); + List>> availableOutputs = new ArrayList<>(targets); + while (!availableOutputs.isEmpty() && transfer.getAmount() > 0) { + int dividedTransfer = transfer.getAmount() / availableOutputs.size(); + int remainder = transfer.getAmount() % availableOutputs.size(); + + for (Iterator>> iterator = + availableOutputs.iterator(); iterator.hasNext();) { + Pair> pair = iterator.next(); + int toTransfer = dividedTransfer; + if (remainder > 0) { + toTransfer++; + remainder--; + } + + if (transfer.isEmpty()) + break; + IFluidHandler targetHandler = pair.getSecond() + .orElse(null); + if (targetHandler == null) { + iterator.remove(); + continue; + } + + FluidStack divided = transfer.copy(); + divided.setAmount(toTransfer); + int fill = targetHandler.fill(divided, action); + transfer.setAmount(transfer.getAmount() - fill); + if (fill < toTransfer) + iterator.remove(); + } - // fluid handler endpoint - Direction face = blockFace.getFace(); - if (tileEntity != null) { - LazyOptional capability = - tileEntity.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, face.getOpposite()); - if (capability.isPresent()) { - targets.add(new FluidNetworkEndpoint(world, blockFace, capability)); - addEntry(blockFace.getPos(), face, false, distance); - FluidPropagator.showBlockFace(blockFace) - .colored(0x00b7c2) - .lineWidth(1 / 8f); - return true; } + + flowSpeed -= transfer.getAmount(); + transfer = FluidStack.EMPTY; } - - // open endpoint - if (PumpBlock.isPump(connectedState) && connectedState.get(PumpBlock.FACING) - .getAxis() == face.getAxis()) { - rangeEndpoints.add(blockFace); - addEntry(blockFace.getPos(), face, false, distance); - return true; - } - if (!FluidPropagator.isOpenEnd(world, blockFace.getPos(), face)) - return false; - - targets.add(reuseOrCreateOpenEnd(world, openEnds, blockFace)); - addEntry(blockFace.getPos(), face, false, distance); - FluidPropagator.showBlockFace(blockFace) - .colored(0xb700c2) - .lineWidth(1 / 8f); - return true; } - private void addConnection(BlockPos from, BlockPos to, Direction direction, int distance) { - addEntry(from, direction, true, distance); - addEntry(to, direction.getOpposite(), false, distance + 1); - } +// private void drawDebugOutlines() { +// FluidPropagator.showBlockFace(start) +// .lineWidth(1 / 8f) +// .colored(0xff0000); +// for (Pair> pair : targets) +// FluidPropagator.showBlockFace(pair.getFirst()) +// .lineWidth(1 / 8f) +// .colored(0x00ff00); +// for (Pair pair : frontier) +// FluidPropagator.showBlockFace(pair.getFirst()) +// .lineWidth(1 / 4f) +// .colored(0xfaaa33); +// } - private void addEntry(BlockPos pos, Direction direction, boolean outbound, int distance) { - if (!pipeGraph.containsKey(pos)) - pipeGraph.put(pos, Pair.of(distance, new HashMap<>())); - pipeGraph.get(pos) - .getSecond() - .put(direction, outbound); - } - - public void reAssemble(IWorld world, PumpTileEntity pumpTE, BlockFace pumpLocation) { - rangeEndpoints.clear(); + public void reset() { + frontier.clear(); + visited.clear(); targets.clear(); - pipeGraph.clear(); - assemble(world, pumpTE, pumpLocation); + queued.clear(); + queued.add(start); + pauseBeforePropagation = 2; } - public void remove(IWorld world) { - clearFlows(world, false); + @Nullable + private PipeConnection get(BlockFace location) { + BlockPos pos = location.getPos(); + FluidTransportBehaviour fluidTransfer = getFluidTransfer(pos); + if (fluidTransfer == null) + return null; + return fluidTransfer.getConnection(location.getFace()); } - public void clearFlows(IWorld world, boolean saveState) { - for (FluidNetworkFlow networkFlow : flows) { - if (!networkFlow.getFluidStack() - .isEmpty()) - networkFlow.addToSkippedConnections(world); - networkFlow.resetFlow(world); + private boolean isPresent(BlockFace location) { + return world.isAreaLoaded(location.getPos(), 0); + } + + @Nullable + private FluidTransportBehaviour getFluidTransfer(BlockPos pos) { + WeakReference weakReference = cache.get(pos); + FluidTransportBehaviour behaviour = weakReference != null ? weakReference.get() : null; + if (behaviour != null && behaviour.tileEntity.isRemoved()) + behaviour = null; + if (behaviour == null) { + behaviour = TileEntityBehaviour.get(world, pos, FluidTransportBehaviour.TYPE); + if (behaviour != null) + cache.put(pos, new WeakReference<>(behaviour)); } - flows.clear(); + return behaviour; } } diff --git a/src/main/java/com/simibubi/create/content/contraptions/fluids/FluidNetworkEndpoint.java b/src/main/java/com/simibubi/create/content/contraptions/fluids/FluidNetworkEndpoint.java deleted file mode 100644 index e37e0b35c..000000000 --- a/src/main/java/com/simibubi/create/content/contraptions/fluids/FluidNetworkEndpoint.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.simibubi.create.content.contraptions.fluids; - -import com.simibubi.create.foundation.utility.BlockFace; - -import net.minecraft.tileentity.TileEntity; -import net.minecraft.world.IWorld; -import net.minecraftforge.common.util.LazyOptional; -import net.minecraftforge.fluids.FluidStack; -import net.minecraftforge.fluids.capability.CapabilityFluidHandler; -import net.minecraftforge.fluids.capability.IFluidHandler; -import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction; - -public class FluidNetworkEndpoint { - BlockFace location; - protected LazyOptional handler; - - public FluidNetworkEndpoint(IWorld world, BlockFace location, LazyOptional handler) { - this.location = location; - this.handler = handler; - this.handler.addListener($ -> onHandlerInvalidated(world)); - } - - protected void onHandlerInvalidated(IWorld world) { - IFluidHandler tank = handler.orElse(null); - if (tank != null) - return; - TileEntity tileEntity = world.getTileEntity(location.getConnectedPos()); - if (tileEntity == null) - return; - LazyOptional capability = - tileEntity.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, location.getOppositeFace()); - if (capability.isPresent()) { - handler = capability; - handler.addListener($ -> onHandlerInvalidated(world)); - } - } - - public FluidStack provideFluid() { - IFluidHandler tank = provideHandler().orElse(null); - if (tank == null) - return FluidStack.EMPTY; - return tank.drain(1, FluidAction.SIMULATE); - } - - public LazyOptional provideHandler() { - return handler; - } - -} diff --git a/src/main/java/com/simibubi/create/content/contraptions/fluids/FluidNetworkFlow.java b/src/main/java/com/simibubi/create/content/contraptions/fluids/FluidNetworkFlow.java deleted file mode 100644 index 1324ee41c..000000000 --- a/src/main/java/com/simibubi/create/content/contraptions/fluids/FluidNetworkFlow.java +++ /dev/null @@ -1,306 +0,0 @@ -package com.simibubi.create.content.contraptions.fluids; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; -import com.simibubi.create.foundation.utility.BlockFace; -import com.simibubi.create.foundation.utility.Iterate; - -import net.minecraft.block.BlockState; -import net.minecraft.tileentity.TileEntity; -import net.minecraft.util.Direction; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.IWorld; -import net.minecraftforge.fluids.FluidStack; - -class FluidNetworkFlow { - - @FunctionalInterface - static interface PipeFlowConsumer { - void accept(FluidPipeBehaviour pipe, Direction face, boolean inbound); - } - - /** - * - */ - private final FluidNetwork activePipeNetwork; - FluidNetworkEndpoint source; - FluidStack fluidStack; - Set flowPointers; - - Set outputEndpoints; - boolean pumpReached; - - boolean pulling; - float speed; - - public FluidNetworkFlow(FluidNetwork activePipeNetwork, FluidNetworkEndpoint source, IWorld world, - boolean pulling) { - this.activePipeNetwork = activePipeNetwork; - this.source = source; - this.pulling = pulling; - flowPointers = new HashSet<>(); - outputEndpoints = new HashSet<>(); - fluidStack = FluidStack.EMPTY; - tick(world, 0); - } - - void resetFlow(IWorld world) { - fluidStack = FluidStack.EMPTY; - flowPointers.clear(); - outputEndpoints.clear(); - pumpReached = false; - forEachPipeFlow(world, (pipe, face, inbound) -> pipe.removeFlow(this, face, inbound)); - } - - void addToSkippedConnections(IWorld world) { - forEachPipeFlow(world, (pipe, face, inbound) -> { - if (!pipe.getFluid().isFluidEqual(fluidStack)) - return; - BlockFace blockFace = new BlockFace(pipe.getPos(), face); - this.activePipeNetwork.previousFlow.put(blockFace, pipe.getFluid()); - }); - } - - void forEachPipeFlow(IWorld world, FluidNetworkFlow.PipeFlowConsumer consumer) { - Set flowPointers = new HashSet<>(); - flowPointers.add(getSource()); - - // Update all branches of this flow, and create new ones if necessary - while (!flowPointers.isEmpty()) { - List toAdd = new ArrayList<>(); - for (Iterator iterator = flowPointers.iterator(); iterator.hasNext();) { - BlockFace flowPointer = iterator.next(); - BlockPos currentPos = flowPointer.getPos(); - FluidPipeBehaviour pipe = getPipeInTree(world, currentPos); - if (pipe == null) { - iterator.remove(); - continue; - } - Map directions = this.activePipeNetwork.pipeGraph.get(currentPos) - .getSecond(); - for (Entry entry : directions.entrySet()) { - boolean inbound = entry.getValue() != pulling; - Direction face = entry.getKey(); - if (inbound && face != flowPointer.getFace()) - continue; - consumer.accept(pipe, face, inbound); - if (inbound) - continue; - toAdd.add(new BlockFace(currentPos.offset(face), face.getOpposite())); - } - iterator.remove(); - } - flowPointers.addAll(toAdd); - } - } - - void tick(IWorld world, float speed) { - boolean skipping = speed == 0; - Map previousFlow = this.activePipeNetwork.previousFlow; - if (skipping && previousFlow.isEmpty()) - return; - - this.speed = speed; - FluidStack provideFluid = source.provideFluid(); - if (!fluidStack.isEmpty() && !fluidStack.isFluidEqual(provideFluid)) { - resetFlow(world); - return; - } - - fluidStack = provideFluid.copy(); - - // There is currently no unfinished flow being followed - if (flowPointers.isEmpty()) { - - // The fluid source has run out -> reset - if (fluidStack.isEmpty()) { - if (hasValidTargets()) - resetFlow(world); - return; - } - - // Keep the flows if all is well - if (hasValidTargets()) - return; - - // Start a new flow from or towards the pump - BlockFace source = getSource(); - if (tryConnectTo(world, source.getOpposite())) - return; - flowPointers.add(source); - } - - boolean skipped = false; - Set pausedPointers = new HashSet<>(); - - do { - skipped = false; - List toAdd = null; - - // Update all branches of this flow, and create new ones if necessary - for (Iterator iterator = flowPointers.iterator(); iterator.hasNext();) { - BlockFace flowPointer = iterator.next(); - BlockPos currentPos = flowPointer.getPos(); - - if (pausedPointers.contains(flowPointer)) - continue; - - FluidPipeBehaviour pipe = getPipeInTree(world, currentPos); - if (pipe == null) { - iterator.remove(); - continue; - } - - Map directions = this.activePipeNetwork.pipeGraph.get(currentPos) - .getSecond(); - boolean inboundComplete = false; - boolean allFlowsComplete = true; - BlockState state = world.getBlockState(currentPos); - - // First loop only inbound flows of a pipe to see if they have reached the - // center - for (boolean inboundPass : Iterate.trueAndFalse) { - if (!inboundPass && !inboundComplete) - break; - - // For all connections of the pipe tree of the pump - for (Entry entry : directions.entrySet()) { - Boolean awayFromPump = entry.getValue(); - Direction direction = entry.getKey(); - boolean inbound = awayFromPump != pulling; - - if (inboundPass && direction != flowPointer.getFace()) - continue; - if (!inboundPass && inbound) - continue; - if (!pipe.canTransferToward(fluidStack, state, direction, inbound)) - continue; - - BlockFace blockface = new BlockFace(currentPos, direction); - - if (!pipe.hasStartedFlow(this, direction, inbound)) - pipe.addFlow(this, direction, inbound, false); - if (skipping && canSkip(previousFlow, blockface)) { - pipe.skipFlow(direction, inbound); - FluidPropagator.showBlockFace(blockface) - .colored(0x0) - .lineWidth(1 / 8f); - skipped = true; - } - - if (!pipe.hasCompletedFlow(direction, inbound)) { - allFlowsComplete = false; - continue; - } - - if (inboundPass) { - inboundComplete = true; - continue; - } - - // Outward pass, check if any target was reached - tryConnectTo(world, blockface); - } - } - - if (!allFlowsComplete && !skipping) - continue; - - // Create a new flow branch at each outward pipe connection - for (Entry entry : directions.entrySet()) { - if (entry.getValue() != pulling) - continue; - Direction face = entry.getKey(); - if (!pipe.canTransferToward(fluidStack, state, face, false)) - continue; - BlockFace addedBlockFace = new BlockFace(currentPos.offset(face), face.getOpposite()); - if (skipping && !canSkip(previousFlow, addedBlockFace)) { - allFlowsComplete = false; - continue; - } - if (toAdd == null) - toAdd = new ArrayList<>(); - toAdd.add(addedBlockFace); - } - - if (!allFlowsComplete && skipping) { - pausedPointers.add(flowPointer); - continue; - } - - iterator.remove(); - - } // End of branch loop - - if (toAdd != null) - flowPointers.addAll(toAdd); - - } while (skipping && skipped); - } - - private boolean canSkip(Map previousFlow, BlockFace blockface) { - return previousFlow.containsKey(blockface) && previousFlow.get(blockface) - .isFluidEqual(fluidStack); - } - - private boolean tryConnectTo(IWorld world, BlockFace blockface) { - // Pulling flow, target is the pump - if (pulling) { - if (!this.activePipeNetwork.pumpLocation.getOpposite() - .equals(blockface)) - return false; - pumpReached = true; - TileEntity targetTE = world.getTileEntity(this.activePipeNetwork.pumpLocation.getPos()); - if (targetTE instanceof PumpTileEntity) - ((PumpTileEntity) targetTE).setProvidedFluid(fluidStack); - FluidPropagator.showBlockFace(this.activePipeNetwork.pumpLocation) - .colored(0x799351) - .lineWidth(1 / 8f); - return true; - } - - // Pushing flow, targets are the endpoints - for (FluidNetworkEndpoint networkEndpoint : this.activePipeNetwork.targets) { - if (!networkEndpoint.location.isEquivalent(blockface)) - continue; - outputEndpoints.add(networkEndpoint); - FluidPropagator.showBlockFace(blockface) - .colored(0x799351) - .lineWidth(1 / 8f); - return !(networkEndpoint instanceof InterPumpEndpoint); - } - - return false; - } - - private BlockFace getSource() { - return pulling ? source.location : this.activePipeNetwork.pumpLocation.getOpposite(); - } - - private FluidPipeBehaviour getPipeInTree(IWorld world, BlockPos currentPos) { - if (!world.isAreaLoaded(currentPos, 0)) - return null; - if (!this.activePipeNetwork.pipeGraph.containsKey(currentPos)) - return null; - return TileEntityBehaviour.get(world, currentPos, FluidPipeBehaviour.TYPE); - } - - boolean hasValidTargets() { - return pumpReached || !outputEndpoints.isEmpty(); - } - - public float getSpeed() { - return speed; - } - - public FluidStack getFluidStack() { - return fluidStack; - } -} \ No newline at end of file diff --git a/src/main/java/com/simibubi/create/content/contraptions/fluids/FluidPipeAttachmentBehaviour.java b/src/main/java/com/simibubi/create/content/contraptions/fluids/FluidPipeAttachmentBehaviour.java deleted file mode 100644 index 51d064588..000000000 --- a/src/main/java/com/simibubi/create/content/contraptions/fluids/FluidPipeAttachmentBehaviour.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.simibubi.create.content.contraptions.fluids; - -import com.simibubi.create.AllBlocks; -import com.simibubi.create.content.contraptions.fluids.pipes.EncasedPipeBlock; -import com.simibubi.create.content.contraptions.relays.elementary.BracketedTileEntityBehaviour; -import com.simibubi.create.foundation.tileEntity.SmartTileEntity; -import com.simibubi.create.foundation.tileEntity.behaviour.BehaviourType; - -import net.minecraft.block.BlockState; -import net.minecraft.util.Direction; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.ILightReader; - -public class FluidPipeAttachmentBehaviour extends BracketedTileEntityBehaviour { - - public static BehaviourType TYPE = new BehaviourType<>(); - - public AttachmentTypes getAttachment(ILightReader world, BlockPos pos, BlockState state, Direction direction) { - if (!isPipeConnectedTowards(state, direction)) - return AttachmentTypes.NONE; - - BlockPos offsetPos = pos.offset(direction); - BlockState facingState = world.getBlockState(offsetPos); - - if (facingState.getBlock() instanceof PumpBlock && facingState.get(PumpBlock.FACING) - .getAxis() == direction.getAxis()) - return AttachmentTypes.NONE; - - if (AllBlocks.ENCASED_FLUID_PIPE.has(facingState) - && facingState.get(EncasedPipeBlock.FACING_TO_PROPERTY_MAP.get(direction.getOpposite()))) - return AttachmentTypes.NONE; - - if (FluidPropagator.hasFluidCapability(facingState, world, offsetPos, direction) - && !AllBlocks.HOSE_PULLEY.has(facingState)) - return AttachmentTypes.DRAIN; - - return AttachmentTypes.RIM; - } - - public boolean isPipeConnectedTowards(BlockState state, Direction direction) { - FluidPipeBehaviour fluidPipeBehaviour = tileEntity.getBehaviour(FluidPipeBehaviour.TYPE); - if (fluidPipeBehaviour == null) - return false; -// BlockState bracket = getBracket(); -// if (bracket != Blocks.AIR.getDefaultState() && bracket.get(BracketBlock.FACING) == direction) -// return false; - return fluidPipeBehaviour.isConnectedTo(state, direction); - } - - public static enum AttachmentTypes { - NONE, RIM, DRAIN; - - public boolean hasModel() { - return this != NONE; - } - } - - public FluidPipeAttachmentBehaviour(SmartTileEntity te) { - super(te); - } - - @Override - public BehaviourType getType() { - return TYPE; - } - - @Override - public boolean canHaveBracket() { - BlockState blockState = tileEntity.getBlockState(); - if (blockState.getBlock() instanceof PumpBlock) - return false; - if (blockState.getBlock() instanceof EncasedPipeBlock) - return false; - return true; - } - -} diff --git a/src/main/java/com/simibubi/create/content/contraptions/fluids/FluidPipeBehaviour.java b/src/main/java/com/simibubi/create/content/contraptions/fluids/FluidPipeBehaviour.java deleted file mode 100644 index ab4d56bdb..000000000 --- a/src/main/java/com/simibubi/create/content/contraptions/fluids/FluidPipeBehaviour.java +++ /dev/null @@ -1,484 +0,0 @@ -package com.simibubi.create.content.contraptions.fluids; - -import java.util.HashSet; -import java.util.IdentityHashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Random; -import java.util.Set; - -import javax.annotation.Nullable; - -import com.simibubi.create.AllSpecialTextures; -import com.simibubi.create.CreateClient; -import com.simibubi.create.content.contraptions.KineticDebugger; -import com.simibubi.create.foundation.tileEntity.SmartTileEntity; -import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; -import com.simibubi.create.foundation.tileEntity.behaviour.BehaviourType; -import com.simibubi.create.foundation.utility.Couple; -import com.simibubi.create.foundation.utility.Iterate; -import com.simibubi.create.foundation.utility.LerpedFloat; -import com.simibubi.create.foundation.utility.LerpedFloat.Chaser; -import com.simibubi.create.foundation.utility.NBTHelper; -import com.simibubi.create.foundation.utility.Pair; -import com.simibubi.create.foundation.utility.VecHelper; - -import net.minecraft.block.BlockState; -import net.minecraft.client.Minecraft; -import net.minecraft.entity.Entity; -import net.minecraft.fluid.Fluid; -import net.minecraft.fluid.Fluids; -import net.minecraft.nbt.CompoundNBT; -import net.minecraft.nbt.ListNBT; -import net.minecraft.particles.IParticleData; -import net.minecraft.util.Direction; -import net.minecraft.util.math.AxisAlignedBB; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; -import net.minecraft.world.World; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; -import net.minecraftforge.common.util.Constants.NBT; -import net.minecraftforge.fluids.FluidStack; -import net.minecraftforge.fml.DistExecutor; - -public abstract class FluidPipeBehaviour extends TileEntityBehaviour { - - public static BehaviourType TYPE = new BehaviourType<>(); - public static final int MAX_PARTICLE_RENDER_DISTANCE = 20; - public static final int SPLASH_PARTICLE_AMOUNT = 1; - public static final float IDLE_PARTICLE_SPAWN_CHANCE = 1 / 800f; - public static final Random r = new Random(); - - // Direction -> (inboundflows{}, outwardflows{}) - Map> allFlows; - FluidStack fluid; - Couple collision; - - public FluidPipeBehaviour(SmartTileEntity te) { - super(te); - allFlows = new IdentityHashMap<>(); - fluid = FluidStack.EMPTY; - } - - @Override - public BehaviourType getType() { - return TYPE; - } - - public void notifyNetwork() { - FluidPropagator.propagateChangedPipe(this.getWorld(), tileEntity.getPos(), tileEntity.getBlockState()); - } - - public boolean canTransferToward(FluidStack fluid, BlockState state, Direction direction, boolean inbound) { - return isConnectedTo(state, direction); - } - - public abstract boolean isConnectedTo(BlockState state, Direction direction); - - public float getRimRadius(BlockState state, Direction direction) { - return 1 / 4f + 1 / 64f; - } - - public boolean hasStartedFlow(FluidNetworkFlow flow, Direction face, boolean inbound) { - return allFlows.containsKey(face) && allFlows.get(face) - .get(inbound) - .hasFlow(flow); - } - - public boolean hasCompletedFlow(Direction face, boolean inbound) { - return allFlows.containsKey(face) && allFlows.get(face) - .get(inbound) - .isCompleted(); - } - - @Override - public void write(CompoundNBT compound, boolean client) { - compound.put("Fluid", fluid.writeToNBT(new CompoundNBT())); - ListNBT flows = new ListNBT(); - for (Direction face : Iterate.directions) - for (boolean inbound : Iterate.trueAndFalse) { - LerpedFloat flowProgress = getFlowProgress(face, inbound); - if (flowProgress == null) - continue; - CompoundNBT nbt = new CompoundNBT(); - NBTHelper.writeEnum(nbt, "Face", face); - nbt.putBoolean("In", inbound); - PipeFlows pipeFlows = allFlows.get(face) - .get(inbound); - Set participants = pipeFlows.participants; - nbt.putBoolean("Silent", participants == null || participants.isEmpty()); - nbt.put("Progress", flowProgress.writeNBT()); - - if (client) - nbt.putFloat("Strength", pipeFlows.bestFlowStrength); - - flows.add(nbt); - } - compound.put("Flows", flows); - } - - @Override - public void read(CompoundNBT compound, boolean client) { - fluid = FluidStack.loadFluidStackFromNBT(compound.getCompound("Fluid")); - - if (client) { - for (Direction face : Iterate.directions) - if (allFlows.containsKey(face)) - allFlows.get(face) - .forEach(pf -> pf.progress = null); - } - - NBTHelper.iterateCompoundList(compound.getList("Flows", NBT.TAG_COMPOUND), nbt -> { - Direction face = NBTHelper.readEnum(nbt, "Face", Direction.class); - boolean inbound = nbt.getBoolean("In"); - LerpedFloat progress = createFlowProgress(0); - progress.readNBT(nbt.getCompound("Progress"), false); - addFlow(null, face, inbound, nbt.getBoolean("Silent")); - setFlowProgress(face, inbound, progress); - if (client) - setVisualFlowStrength(face, inbound, nbt.getFloat("Strength")); - }); - - if (!client) - return; - - for (Direction face : Iterate.directions) { - if (!allFlows.containsKey(face)) - return; - Couple couple = allFlows.get(face); - if (couple.get(true).progress == null && couple.get(false).progress == null) - allFlows.remove(face); - if (allFlows.isEmpty()) - clear(); - } - } - - public void addFlow(@Nullable FluidNetworkFlow flow, Direction face, boolean inbound, boolean silent) { - if (flow != null) { - FluidStack fluid = flow.getFluidStack(); - if (!this.fluid.isEmpty() && !fluid.isFluidEqual(this.fluid)) { - collision = Couple.create(this.fluid, fluid); - return; - } - this.fluid = fluid; - } - - if (!allFlows.containsKey(face)) { - allFlows.put(face, Couple.create(PipeFlows::new)); - if (inbound && !silent) - spawnSplashOnRim(face); - } - - if (flow != null) { - PipeFlows flows = allFlows.get(face) - .get(inbound); - flows.addFlow(flow); - contentsChanged(); - } - } - - public void removeFlow(FluidNetworkFlow flow, Direction face, boolean inbound) { - if (!allFlows.containsKey(face)) - return; - Couple couple = allFlows.get(face); - couple.get(inbound) - .removeFlow(flow); - contentsChanged(); - if (!couple.get(true) - .isActive() - && !couple.get(false) - .isActive()) - allFlows.remove(face); - if (allFlows.isEmpty()) - clear(); - } - - public void setVisualFlowStrength(Direction face, boolean inbound, float strength) { - if (!allFlows.containsKey(face)) - return; - allFlows.get(face) - .get(inbound).bestFlowStrength = strength; - } - - public void setFlowProgress(Direction face, boolean inbound, LerpedFloat progress) { - if (!allFlows.containsKey(face)) - return; - allFlows.get(face) - .get(inbound).progress = progress; - } - - public LerpedFloat getFlowProgress(Direction face, boolean inbound) { - if (!allFlows.containsKey(face)) - return null; - return allFlows.get(face) - .get(inbound).progress; - } - - public void skipFlow(Direction face, boolean inbound) { - if (!allFlows.containsKey(face)) - return; - Couple couple = allFlows.get(face); - couple.get(inbound) - .skip(); - } - - public void clear() { - allFlows.clear(); - fluid = FluidStack.EMPTY; - contentsChanged(); - } - - public void spawnSplashOnRim(Direction face) { - DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> spawnSplashOnRimInner(face)); - } - - public void spawnParticles() { - DistExecutor.runWhenOn(Dist.CLIENT, () -> this::spawnParticlesInner); - } - - @OnlyIn(Dist.CLIENT) - private void spawnParticlesInner() { - if (!isRenderEntityWithinDistance(tileEntity.getPos())) - return; - if (fluid.isEmpty()) - return; - - World world = Minecraft.getInstance().world; - BlockPos pos = tileEntity.getPos(); - BlockState state = world.getBlockState(pos); - - for (Direction face : Iterate.directions) { - boolean open = FluidPropagator.isOpenEnd(world, pos, face); - if (isConnectedTo(state, face)) { - if (open) { - spawnPouringLiquid(world, state, fluid, face, 1); - continue; - } - if (r.nextFloat() < IDLE_PARTICLE_SPAWN_CHANCE) - spawnRimParticles(world, state, fluid, face, 1); - } - } - } - - @OnlyIn(Dist.CLIENT) - private void spawnSplashOnRimInner(Direction face) { - if (!isRenderEntityWithinDistance(tileEntity.getPos())) - return; - if (fluid.isEmpty()) - return; - World world = Minecraft.getInstance().world; - BlockPos pos = tileEntity.getPos(); - BlockState state = world.getBlockState(pos); - spawnRimParticles(world, state, fluid, face, SPLASH_PARTICLE_AMOUNT); - } - - @OnlyIn(Dist.CLIENT) - private void spawnRimParticles(World world, BlockState state, FluidStack fluid, Direction side, int amount) { - BlockPos pos = tileEntity.getPos(); - if (FluidPropagator.isOpenEnd(world, pos, side)) { - spawnPouringLiquid(world, state, fluid, side, amount); - return; - } - - IParticleData particle = FluidFX.getDrippingParticle(fluid); - float rimRadius = getRimRadius(state, side); - FluidFX.spawnRimParticles(world, pos, side, amount, particle, rimRadius); - } - - @OnlyIn(Dist.CLIENT) - private void spawnPouringLiquid(World world, BlockState state, FluidStack fluid, Direction side, int amount) { - IParticleData particle = FluidFX.getFluidParticle(fluid); - float rimRadius = getRimRadius(state, side); - Vec3d directionVec = new Vec3d(side.getDirectionVec()); - BlockPos pos = tileEntity.getPos(); - - Couple couple = allFlows.get(side); - if (couple == null) - return; - - couple.forEachWithContext((flow, inbound) -> { - if (flow.progress == null) - return; - FluidFX.spawnPouringLiquid(world, pos, amount, particle, rimRadius, directionVec, inbound); - }); - } - - @OnlyIn(Dist.CLIENT) - public static boolean isRenderEntityWithinDistance(BlockPos pos) { - Entity renderViewEntity = Minecraft.getInstance() - .getRenderViewEntity(); - if (renderViewEntity == null) - return false; - Vec3d center = VecHelper.getCenterOf(pos); - if (renderViewEntity.getPositionVec() - .distanceTo(center) > MAX_PARTICLE_RENDER_DISTANCE) - return false; - return true; - } - - static AxisAlignedBB smallCenter = new AxisAlignedBB(BlockPos.ZERO).shrink(.25); - - @Override - public void tick() { - super.tick(); - boolean isRemote = getWorld().isRemote; - - allFlows.values() - .forEach(c -> c.forEach(pf -> pf.tick(isRemote))); - - if (isRemote) { - clientTick(); - return; - } - - if (collision != null) { - FluidReactions.handlePipeFlowCollision(getWorld(), tileEntity.getPos(), collision.getFirst(), - collision.getSecond()); - collision = null; - return; - } - } - - public Pair getStrogestFlow(Direction side) { - Couple couple = allFlows.get(side); - if (couple == null) - return null; - - PipeFlows in = couple.get(true); - PipeFlows out = couple.get(false); - Couple progress = couple.map(pf -> pf.progress); - boolean inboundStronger = false; - - if (in.isCompleted() != out.isCompleted()) { - inboundStronger = in.isCompleted(); - } else if ((progress.get(true) == null) != (progress.get(false) == null)) { - inboundStronger = progress.get(true) != null; - } else { - if (progress.get(true) != null) - inboundStronger = in.bestFlowStrength > out.bestFlowStrength; - } - - return Pair.of(inboundStronger, progress.get(inboundStronger)); - } - - private void clientTick() { - spawnParticles(); - - if (!KineticDebugger.isActive()) - return; - if (fluid.isEmpty()) - return; - for (Entry> entry : allFlows.entrySet()) { - Direction face = entry.getKey(); - Vec3d directionVec = new Vec3d(face.getDirectionVec()); - float size = 1 / 4f; - boolean extended = !isConnectedTo(tileEntity.getBlockState(), face.getOpposite()); - float length = extended ? .75f : .5f; - - entry.getValue() - .forEachWithContext((flow, inbound) -> { - if (flow.progress == null) - return; - float value = flow.progress.getValue(); - Vec3d start = directionVec.scale(inbound ? .5 : .5f - length); - Vec3d offset = directionVec.scale(length * (inbound ? -1 : 1)) - .scale(value); - - Vec3d scale = new Vec3d(1, 1, 1).subtract(directionVec.scale(face.getAxisDirection() - .getOffset())) - .scale(size); - AxisAlignedBB bb = - new AxisAlignedBB(start, start.add(offset)).offset(VecHelper.getCenterOf(tileEntity.getPos())) - .grow(scale.x, scale.y, scale.z); - - int color = 0x7fdbda; - if (!fluid.isEmpty()) { - Fluid fluid2 = fluid.getFluid(); - if (fluid2 == Fluids.WATER) - color = 0x1D4D9B; - if (fluid2 == Fluids.LAVA) - color = 0xFF773D; - } - - CreateClient.outliner.chaseAABB(Pair.of(this, face), bb) - .withFaceTexture(AllSpecialTextures.CUTOUT_CHECKERED) - .colored(color) - .lineWidth(1 / 16f); - }); - } - } - - private void contentsChanged() { - tileEntity.markDirty(); - tileEntity.sendData(); - } - - private LerpedFloat createFlowProgress(double speed) { - return LerpedFloat.linear() - .startWithValue(0) - .chase(1, speed, Chaser.LINEAR); - } - - public FluidStack getFluid() { - return fluid; - } - - class PipeFlows { - LerpedFloat progress; - Set participants; - float bestFlowStrength; - - void addFlow(FluidNetworkFlow flow) { - if (participants == null) - participants = new HashSet<>(); - participants.add(flow); - - if (progress == null) { - progress = createFlowProgress(flow.getSpeed()); - } - } - - boolean hasFlow(FluidNetworkFlow flow) { - return participants != null && participants.contains(flow); - } - - void tick(boolean onClient) { - if (progress == null) - return; - if (!onClient) { - if (participants == null) - return; - bestFlowStrength = 0; - for (FluidNetworkFlow networkFlow : participants) - bestFlowStrength = Math.max(bestFlowStrength, networkFlow.getSpeed()); - if (isCompleted()) - return; - if (progress.updateChaseSpeed(bestFlowStrength)) - contentsChanged(); - } - progress.tickChaser(); - } - - void skip() { - progress = LerpedFloat.linear() - .startWithValue(1); - } - - void removeFlow(FluidNetworkFlow flow) { - if (participants == null) - return; - participants.remove(flow); - } - - boolean isActive() { - return participants != null && !participants.isEmpty(); - } - - boolean isCompleted() { - return progress != null && progress.getValue() == 1; - } - - } - -} diff --git a/src/main/java/com/simibubi/create/content/contraptions/fluids/FluidPropagator.java b/src/main/java/com/simibubi/create/content/contraptions/fluids/FluidPropagator.java index 7c46b4019..b193246c1 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/fluids/FluidPropagator.java +++ b/src/main/java/com/simibubi/create/content/contraptions/fluids/FluidPropagator.java @@ -7,16 +7,14 @@ import java.util.Set; import javax.annotation.Nullable; -import org.apache.commons.lang3.mutable.MutableObject; - import com.simibubi.create.AllBlocks; +import com.simibubi.create.content.contraptions.fluids.PipeConnection.Flow; import com.simibubi.create.content.contraptions.fluids.pipes.AxisPipeBlock; import com.simibubi.create.content.contraptions.fluids.pipes.FluidPipeBlock; import com.simibubi.create.foundation.config.AllConfigs; import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; -import com.simibubi.create.foundation.utility.BlockFace; import com.simibubi.create.foundation.utility.Iterate; -import com.simibubi.create.foundation.utility.outliner.Outline.OutlineParams; +import com.simibubi.create.foundation.utility.Pair; import net.minecraft.block.Block; import net.minecraft.block.BlockState; @@ -26,7 +24,6 @@ import net.minecraft.tileentity.TileEntity; import net.minecraft.util.Direction; import net.minecraft.util.Direction.Axis; import net.minecraft.util.Direction.AxisDirection; -import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.world.IBlockReader; import net.minecraft.world.IWorld; @@ -35,6 +32,94 @@ import net.minecraftforge.fluids.capability.CapabilityFluidHandler; public class FluidPropagator { + public static void propagateChangedPipe(IWorld world, BlockPos pipePos, BlockState pipeState) { + List> frontier = new ArrayList<>(); + Set visited = new HashSet<>(); + Set> discoveredPumps = new HashSet<>(); + + frontier.add(Pair.of(0, pipePos)); + + // Visit all connected pumps to update their network + while (!frontier.isEmpty()) { + Pair pair = frontier.remove(0); + BlockPos currentPos = pair.getSecond(); + if (visited.contains(currentPos)) + continue; + visited.add(currentPos); + BlockState currentState = currentPos.equals(pipePos) ? pipeState : world.getBlockState(currentPos); + FluidTransportBehaviour pipe = getPipe(world, currentPos); + if (pipe == null) + continue; + pipe.wipePressure(); + + for (Direction direction : getPipeConnections(currentState, pipe)) { + BlockPos target = currentPos.offset(direction); + if (!world.isAreaLoaded(target, 0)) + continue; + + TileEntity tileEntity = world.getTileEntity(target); + BlockState targetState = world.getBlockState(target); + if (tileEntity instanceof PumpTileEntity) { + if (!AllBlocks.MECHANICAL_PUMP.has(targetState) || targetState.get(PumpBlock.FACING) + .getAxis() != direction.getAxis()) + continue; + discoveredPumps.add(Pair.of((PumpTileEntity) tileEntity, direction.getOpposite())); + continue; + } + if (visited.contains(target)) + continue; + FluidTransportBehaviour targetPipe = getPipe(world, target); + if (targetPipe == null) + continue; + Integer distance = pair.getFirst(); + if (distance >= getPumpRange() && !targetPipe.hasAnyPressure()) + continue; + if (targetPipe.canHaveFlowToward(targetState, direction.getOpposite())) + frontier.add(Pair.of(distance + 1, target)); + } + } + + discoveredPumps.forEach(pair -> pair.getFirst() + .updatePipesOnSide(pair.getSecond())); + } + + public static void resetAffectedFluidNetworks(World world, BlockPos start, Direction side) { + List frontier = new ArrayList<>(); + Set visited = new HashSet<>(); + frontier.add(start); + + while (!frontier.isEmpty()) { + BlockPos pos = frontier.remove(0); + if (visited.contains(pos)) + continue; + visited.add(pos); + FluidTransportBehaviour pipe = getPipe(world, pos); + if (pipe == null) + continue; + + for (Direction d : Iterate.directions) { + if (pos.equals(start) && d != side) + continue; + BlockPos target = pos.offset(d); + if (visited.contains(target)) + continue; + + PipeConnection connection = pipe.getConnection(d); + if (connection == null) + continue; + if (!connection.hasFlow()) + continue; + + Flow flow = connection.flow.get(); + if (!flow.inbound) + continue; + + connection.resetNetwork(); + frontier.add(target); + } + } + } + public static Direction validateNeighbourChange(BlockState state, World world, BlockPos pos, Block otherBlock, BlockPos neighborPos, boolean isMoving) { if (world.isRemote) @@ -58,15 +143,15 @@ public class FluidPropagator { return null; } - public static FluidPipeBehaviour getPipe(IBlockReader reader, BlockPos pos) { - return TileEntityBehaviour.get(reader, pos, FluidPipeBehaviour.TYPE); + public static FluidTransportBehaviour getPipe(IBlockReader reader, BlockPos pos) { + return TileEntityBehaviour.get(reader, pos, FluidTransportBehaviour.TYPE); } public static boolean isOpenEnd(IBlockReader reader, BlockPos pos, Direction side) { BlockPos connectedPos = pos.offset(side); BlockState connectedState = reader.getBlockState(connectedPos); - FluidPipeBehaviour pipe = FluidPropagator.getPipe(reader, connectedPos); - if (pipe != null && pipe.isConnectedTo(connectedState, side.getOpposite())) + FluidTransportBehaviour pipe = FluidPropagator.getPipe(reader, connectedPos); + if (pipe != null && pipe.canHaveFlowToward(connectedState, side.getOpposite())) return false; if (PumpBlock.isPump(connectedState) && connectedState.get(PumpBlock.FACING) .getAxis() == side.getAxis()) @@ -80,52 +165,10 @@ public class FluidPropagator { return true; } - public static void propagateChangedPipe(IWorld world, BlockPos pipePos, BlockState pipeState) { - List frontier = new ArrayList<>(); - Set visited = new HashSet<>(); - - frontier.add(pipePos); - - // Visit all connected pumps to update their network - while (!frontier.isEmpty()) { - BlockPos currentPos = frontier.remove(0); - if (visited.contains(currentPos)) - continue; - visited.add(currentPos); - BlockState currentState = currentPos.equals(pipePos) ? pipeState : world.getBlockState(currentPos); - FluidPipeBehaviour pipe = getPipe(world, currentPos); - if (pipe == null) - continue; - for (Direction direction : getPipeConnections(currentState, pipe)) { - BlockPos target = currentPos.offset(direction); - if (!world.isAreaLoaded(target, 0)) - continue; - - TileEntity tileEntity = world.getTileEntity(target); - BlockState targetState = world.getBlockState(target); - if (tileEntity instanceof PumpTileEntity) { - if (!AllBlocks.MECHANICAL_PUMP.has(targetState) || targetState.get(PumpBlock.FACING) - .getAxis() != direction.getAxis()) - continue; - PumpTileEntity pump = (PumpTileEntity) tileEntity; - pump.updatePipesOnSide(direction.getOpposite()); - continue; - } - if (visited.contains(target)) - continue; - FluidPipeBehaviour targetPipe = getPipe(world, target); - if (targetPipe == null) - continue; - if (targetPipe.isConnectedTo(targetState, direction.getOpposite())) - frontier.add(target); - } - } - } - - public static List getPipeConnections(BlockState state, FluidPipeBehaviour pipe) { + public static List getPipeConnections(BlockState state, FluidTransportBehaviour pipe) { List list = new ArrayList<>(); for (Direction d : Iterate.directions) - if (pipe.isConnectedTo(state, d)) + if (pipe.canHaveFlowToward(state, d)) list.add(d); return list; } @@ -134,37 +177,38 @@ public class FluidPropagator { return AllConfigs.SERVER.fluids.mechanicalPumpRange.get(); } - @Deprecated // Remove after pipes are fixed; comment out for production - public static OutlineParams showBlockFace(BlockFace face) { - MutableObject params = new MutableObject<>(new OutlineParams()); +// static AxisAlignedBB smallCenter = new AxisAlignedBB(BlockPos.ZERO).shrink(.25); +// +// @Deprecated +// public static OutlineParams showBlockFace(BlockFace face) { +// MutableObject params = new MutableObject<>(new OutlineParams()); // DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> { // Vec3d directionVec = new Vec3d(face.getFace() // .getDirectionVec()); // Vec3d scaleVec = directionVec.scale(-.25f * face.getFace() // .getAxisDirection() // .getOffset()); -// directionVec = directionVec.scale(.5f); +// directionVec = directionVec.scale(.45f); // params.setValue(CreateClient.outliner.showAABB(face, // FluidPropagator.smallCenter.offset(directionVec.add(new Vec3d(face.getPos()))) // .grow(scaleVec.x, scaleVec.y, scaleVec.z) // .grow(1 / 16f))); // }); - return params.getValue(); - } +// return params.getValue() +// .lineWidth(1 / 16f); +// } - static AxisAlignedBB smallCenter = new AxisAlignedBB(BlockPos.ZERO).shrink(.25); - - public static boolean hasFluidCapability(BlockState state, IBlockReader world, BlockPos pos, Direction blockFace) { - if (!state.hasTileEntity()) - return false; + public static boolean hasFluidCapability(IBlockReader world, BlockPos pos, Direction side) { TileEntity tileEntity = world.getTileEntity(pos); - return tileEntity != null - && tileEntity.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, blockFace.getOpposite()) - .isPresent(); + return tileEntity != null && tileEntity.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, side) + .isPresent(); } @Nullable public static Axis getStraightPipeAxis(BlockState state) { + if (state.getBlock() instanceof PumpBlock) + return state.get(PumpBlock.FACING) + .getAxis(); if (state.getBlock() instanceof AxisPipeBlock) return state.get(AxisPipeBlock.AXIS); if (!FluidPipeBlock.isPipe(state)) diff --git a/src/main/java/com/simibubi/create/content/contraptions/fluids/FluidReactions.java b/src/main/java/com/simibubi/create/content/contraptions/fluids/FluidReactions.java index c15d2a40a..7c81fb594 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/fluids/FluidReactions.java +++ b/src/main/java/com/simibubi/create/content/contraptions/fluids/FluidReactions.java @@ -1,12 +1,15 @@ package com.simibubi.create.content.contraptions.fluids; +import com.simibubi.create.AllFluids; import com.simibubi.create.foundation.fluid.FluidHelper; import com.simibubi.create.foundation.utility.BlockHelper; +import net.minecraft.block.BlockState; import net.minecraft.block.Blocks; import net.minecraft.fluid.Fluid; import net.minecraft.fluid.Fluids; import net.minecraft.fluid.IFluidState; +import net.minecraft.tags.FluidTags; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraftforge.fluids.FluidStack; @@ -19,19 +22,41 @@ public class FluidReactions { BlockHelper.destroyBlock(world, pos, 1); if (f1 == Fluids.WATER && f2 == Fluids.LAVA || f2 == Fluids.WATER && f1 == Fluids.LAVA) world.setBlockState(pos, Blocks.COBBLESTONE.getDefaultState()); + else if (f1 == Fluids.LAVA && FluidHelper.hasBlockState(f2)) { + BlockState lavaInteraction = AllFluids.getLavaInteraction(FluidHelper.convertToFlowing(f2) + .getDefaultState()); + if (lavaInteraction != null) + world.setBlockState(pos, lavaInteraction); + } else if (f2 == Fluids.LAVA && FluidHelper.hasBlockState(f1)) { + BlockState lavaInteraction = AllFluids.getLavaInteraction(FluidHelper.convertToFlowing(f1) + .getDefaultState()); + if (lavaInteraction != null) + world.setBlockState(pos, lavaInteraction); + } } public static void handlePipeSpillCollision(World world, BlockPos pos, Fluid pipeFluid, IFluidState worldFluid) { Fluid pf = FluidHelper.convertToStill(pipeFluid); Fluid wf = worldFluid.getFluid(); - if (pf == Fluids.WATER && wf == Fluids.LAVA) + if (pf.isIn(FluidTags.WATER) && wf == Fluids.LAVA) world.setBlockState(pos, Blocks.OBSIDIAN.getDefaultState()); - if (pf == Fluids.WATER && wf == Fluids.FLOWING_LAVA) + else if (pf == Fluids.WATER && wf == Fluids.FLOWING_LAVA) world.setBlockState(pos, Blocks.COBBLESTONE.getDefaultState()); else if (pf == Fluids.LAVA && wf == Fluids.WATER) world.setBlockState(pos, Blocks.STONE.getDefaultState()); else if (pf == Fluids.LAVA && wf == Fluids.FLOWING_WATER) world.setBlockState(pos, Blocks.COBBLESTONE.getDefaultState()); + + if (pf == Fluids.LAVA) { + BlockState lavaInteraction = AllFluids.getLavaInteraction(worldFluid); + if (lavaInteraction != null) + world.setBlockState(pos, lavaInteraction); + } else if (wf == Fluids.FLOWING_LAVA && FluidHelper.hasBlockState(pf)) { + BlockState lavaInteraction = AllFluids.getLavaInteraction(FluidHelper.convertToFlowing(pf) + .getDefaultState()); + if (lavaInteraction != null) + world.setBlockState(pos, lavaInteraction); + } } } diff --git a/src/main/java/com/simibubi/create/content/contraptions/fluids/FluidTransportBehaviour.java b/src/main/java/com/simibubi/create/content/contraptions/fluids/FluidTransportBehaviour.java new file mode 100644 index 000000000..2e76257ff --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/fluids/FluidTransportBehaviour.java @@ -0,0 +1,266 @@ +package com.simibubi.create.content.contraptions.fluids; + +import java.util.Collection; +import java.util.IdentityHashMap; +import java.util.Map; +import java.util.function.Predicate; + +import javax.annotation.Nullable; + +import com.simibubi.create.AllBlocks; +import com.simibubi.create.content.contraptions.fluids.pipes.EncasedPipeBlock; +import com.simibubi.create.foundation.tileEntity.SmartTileEntity; +import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; +import com.simibubi.create.foundation.tileEntity.behaviour.BehaviourType; +import com.simibubi.create.foundation.utility.Iterate; + +import net.minecraft.block.BlockState; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.util.Direction; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.ILightReader; +import net.minecraft.world.World; +import net.minecraftforge.fluids.FluidStack; + +public abstract class FluidTransportBehaviour extends TileEntityBehaviour { + + public static BehaviourType TYPE = new BehaviourType<>(); + + enum UpdatePhase { + WAIT_FOR_PUMPS, // Do not run Layer II logic while pumps could still be distributing pressure + FLIP_FLOWS, // Do not cut any flows until all pipes had a chance to reverse them + IDLE; // Operate normally + } + + Map interfaces; + UpdatePhase phase; + + public FluidTransportBehaviour(SmartTileEntity te) { + super(te); + phase = UpdatePhase.WAIT_FOR_PUMPS; + } + + public boolean canPullFluidFrom(FluidStack fluid, BlockState state, Direction direction) { + return true; + } + + public abstract boolean canHaveFlowToward(BlockState state, Direction direction); + + @Override + public void initialize() { + super.initialize(); + createConnectionData(); + } + + @Override + public void tick() { + super.tick(); + World world = getWorld(); + BlockPos pos = getPos(); + boolean onClient = world.isRemote; + Collection connections = interfaces.values(); + + // Do not provide a lone pipe connection with its own flow input + PipeConnection singleSource = null; + +// if (onClient) { +// connections.forEach(connection -> { +// connection.visualizeFlow(pos); +// connection.visualizePressure(pos); +// }); +// } + + if (phase == UpdatePhase.WAIT_FOR_PUMPS) { + phase = UpdatePhase.FLIP_FLOWS; + return; + } + + if (!onClient) { + boolean sendUpdate = false; + for (PipeConnection connection : connections) { + sendUpdate |= connection.flipFlowsIfPressureReversed(); + connection.manageSource(world, pos); + } + if (sendUpdate) + tileEntity.notifyUpdate(); + } + + if (phase == UpdatePhase.FLIP_FLOWS) { + phase = UpdatePhase.IDLE; + return; + } + + if (!onClient) { + FluidStack availableFlow = FluidStack.EMPTY; + FluidStack collidingFlow = FluidStack.EMPTY; + + for (PipeConnection connection : connections) { + FluidStack fluidInFlow = connection.getProvidedFluid(); + if (fluidInFlow.isEmpty()) + continue; + if (availableFlow.isEmpty()) { + singleSource = connection; + availableFlow = fluidInFlow; + continue; + } + if (availableFlow.isFluidEqual(fluidInFlow)) { + singleSource = null; + availableFlow = fluidInFlow; + continue; + } + collidingFlow = fluidInFlow; + break; + } + + if (!collidingFlow.isEmpty()) { + FluidReactions.handlePipeFlowCollision(world, pos, availableFlow, collidingFlow); + return; + } + + boolean sendUpdate = false; + for (PipeConnection connection : connections) { + FluidStack internalFluid = singleSource != connection ? availableFlow : FluidStack.EMPTY; + Predicate extractionPredicate = + extracted -> canPullFluidFrom(extracted, tileEntity.getBlockState(), connection.side); + sendUpdate |= connection.manageFlows(world, pos, internalFluid, extractionPredicate); + } + + if (sendUpdate) + tileEntity.notifyUpdate(); + } + + for (PipeConnection connection : connections) + connection.tickFlowProgress(world, pos); + } + + @Override + public void read(CompoundNBT nbt, boolean clientPacket) { + super.read(nbt, clientPacket); + if (interfaces == null) + interfaces = new IdentityHashMap<>(); + for (Direction face : Iterate.directions) + if (nbt.contains(face.getName())) + interfaces.computeIfAbsent(face, d -> new PipeConnection(d)); + + // Invalid data (missing/outdated). Defer init to runtime + if (interfaces.isEmpty()) { + interfaces = null; + return; + } + + interfaces.values() + .forEach(connection -> connection.deserializeNBT(nbt, clientPacket)); + } + + @Override + public void write(CompoundNBT nbt, boolean clientPacket) { + super.write(nbt, clientPacket); + if (clientPacket) + createConnectionData(); + if (interfaces == null) + return; + + interfaces.values() + .forEach(connection -> connection.serializeNBT(nbt, clientPacket)); + } + + public FluidStack getProvidedOutwardFluid(Direction side) { + createConnectionData(); + if (!interfaces.containsKey(side)) + return FluidStack.EMPTY; + return interfaces.get(side) + .provideOutboundFlow(); + } + + @Nullable + public PipeConnection getConnection(Direction side) { + createConnectionData(); + return interfaces.get(side); + } + + public boolean hasAnyPressure() { + createConnectionData(); + for (PipeConnection pipeConnection : interfaces.values()) + if (pipeConnection.hasPressure()) + return true; + return false; + } + + @Nullable + public PipeConnection.Flow getFlow(Direction side) { + createConnectionData(); + if (!interfaces.containsKey(side)) + return null; + return interfaces.get(side).flow.orElse(null); + } + + public void addPressure(Direction side, boolean inbound, float pressure) { + createConnectionData(); + if (!interfaces.containsKey(side)) + return; + interfaces.get(side) + .addPressure(inbound, pressure); + tileEntity.sendData(); + } + + public void wipePressure() { + if (interfaces != null) + for (Direction d : Iterate.directions) { + if (!canHaveFlowToward(tileEntity.getBlockState(), d)) + interfaces.remove(d); + else + interfaces.computeIfAbsent(d, PipeConnection::new); + } + phase = UpdatePhase.WAIT_FOR_PUMPS; + createConnectionData(); + interfaces.values() + .forEach(PipeConnection::wipePressure); + tileEntity.sendData(); + } + + private void createConnectionData() { + if (interfaces != null) + return; + interfaces = new IdentityHashMap<>(); + for (Direction d : Iterate.directions) + if (canHaveFlowToward(tileEntity.getBlockState(), d)) + interfaces.put(d, new PipeConnection(d)); + } + + public AttachmentTypes getRenderedRimAttachment(ILightReader world, BlockPos pos, BlockState state, + Direction direction) { + if (!canHaveFlowToward(state, direction)) + return AttachmentTypes.NONE; + + BlockPos offsetPos = pos.offset(direction); + BlockState facingState = world.getBlockState(offsetPos); + + if (facingState.getBlock() instanceof PumpBlock && facingState.get(PumpBlock.FACING) + .getAxis() == direction.getAxis()) + return AttachmentTypes.NONE; + + if (AllBlocks.ENCASED_FLUID_PIPE.has(facingState) + && facingState.get(EncasedPipeBlock.FACING_TO_PROPERTY_MAP.get(direction.getOpposite()))) + return AttachmentTypes.NONE; + + if (FluidPropagator.hasFluidCapability(world, offsetPos, direction.getOpposite()) + && !AllBlocks.HOSE_PULLEY.has(facingState)) + return AttachmentTypes.DRAIN; + + return AttachmentTypes.RIM; + } + + public static enum AttachmentTypes { + NONE, RIM, DRAIN; + + public boolean hasModel() { + return this != NONE; + } + } + + @Override + public BehaviourType getType() { + return TYPE; + } + +} diff --git a/src/main/java/com/simibubi/create/content/contraptions/fluids/InterPumpEndpoint.java b/src/main/java/com/simibubi/create/content/contraptions/fluids/InterPumpEndpoint.java deleted file mode 100644 index 62904f74b..000000000 --- a/src/main/java/com/simibubi/create/content/contraptions/fluids/InterPumpEndpoint.java +++ /dev/null @@ -1,108 +0,0 @@ -package com.simibubi.create.content.contraptions.fluids; - -import java.lang.ref.WeakReference; - -import com.simibubi.create.foundation.utility.BlockFace; -import com.simibubi.create.foundation.utility.Couple; -import com.simibubi.create.foundation.utility.Iterate; -import com.simibubi.create.foundation.utility.Pair; - -import net.minecraft.world.IWorld; -import net.minecraftforge.common.util.LazyOptional; -import net.minecraftforge.fluids.FluidStack; -import net.minecraftforge.fluids.capability.IFluidHandler; - -public class InterPumpEndpoint extends FluidNetworkEndpoint { - - Couple>> pumps; - - private InterPumpEndpoint(IWorld world, BlockFace location, LazyOptional handler) { - super(world, location, handler); - } - - public InterPumpEndpoint(IWorld world, BlockFace location, PumpTileEntity source, PumpTileEntity interfaced, - BlockFace sourcePos, BlockFace interfacedPos) { - this(world, location, LazyOptional.empty()); - handler = LazyOptional.of(() -> new InterPumpFluidHandler(this)); - pumps = Couple.create(Pair.of(sourcePos, new WeakReference<>(source)), - Pair.of(interfacedPos, new WeakReference<>(interfaced))); - } - - public InterPumpEndpoint opposite(IWorld world) { - InterPumpEndpoint interPumpEndpoint = new InterPumpEndpoint(world, this.location.getOpposite(), handler); - interPumpEndpoint.pumps = pumps.copy(); - return interPumpEndpoint; - } - - public Couple>> getPumps() { - return pumps; - } - - public boolean isPulling(boolean first) { - Pair> pair = getPumps().get(first); - PumpTileEntity pumpTileEntity = pair.getSecond() - .get(); - if (pumpTileEntity == null || pumpTileEntity.isRemoved()) - return false; - return pumpTileEntity.isPullingOnSide(pumpTileEntity.isFront(pair.getFirst() - .getFace())); - } - - public int getTransferSpeed(boolean first) { - PumpTileEntity pumpTileEntity = getPumps().get(first) - .getSecond() - .get(); - if (pumpTileEntity == null || pumpTileEntity.isRemoved()) - return 0; - return pumpTileEntity.getFluidTransferSpeed(); - } - - @Override - public LazyOptional provideHandler() { - if (isPulling(true) == isPulling(false)) - return LazyOptional.empty(); - if (getTransferSpeed(true) > getTransferSpeed(false)) - return LazyOptional.empty(); - return super.provideHandler(); - } - - @Override - public FluidStack provideFluid() { - if (!provideHandler().isPresent()) - return FluidStack.EMPTY; - - Couple>> pumps = getPumps(); - for (boolean current : Iterate.trueAndFalse) { - if (isPulling(current)) - continue; - - Pair> pair = pumps.get(current); - BlockFace blockFace = pair.getFirst(); - PumpTileEntity pumpTileEntity = pair.getSecond() - .get(); - if (pumpTileEntity == null) - continue; - if (pumpTileEntity.networks == null) - continue; - FluidNetwork fluidNetwork = pumpTileEntity.networks.get(pumpTileEntity.isFront(blockFace.getFace())); - for (FluidNetworkFlow fluidNetworkFlow : fluidNetwork.flows) { - for (FluidNetworkEndpoint fne : fluidNetworkFlow.outputEndpoints) { - if (!(fne instanceof InterPumpEndpoint)) - continue; - InterPumpEndpoint ipe = (InterPumpEndpoint) fne; - if (!ipe.location.isEquivalent(location)) - continue; - - FluidStack heldFluid = fluidNetworkFlow.fluidStack; - if (heldFluid.isEmpty()) - return heldFluid; - FluidStack copy = heldFluid.copy(); - copy.setAmount(1); - return heldFluid; - } - } - } - return FluidStack.EMPTY; - } - -} \ No newline at end of file diff --git a/src/main/java/com/simibubi/create/content/contraptions/fluids/InterPumpFluidHandler.java b/src/main/java/com/simibubi/create/content/contraptions/fluids/InterPumpFluidHandler.java deleted file mode 100644 index 34e0bd8e1..000000000 --- a/src/main/java/com/simibubi/create/content/contraptions/fluids/InterPumpFluidHandler.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.simibubi.create.content.contraptions.fluids; - -import net.minecraftforge.fluids.FluidStack; -import net.minecraftforge.fluids.capability.templates.FluidTank; - -public class InterPumpFluidHandler extends FluidTank { - - InterPumpEndpoint endpoint; - - public InterPumpFluidHandler(InterPumpEndpoint endpoint) { - super(Integer.MAX_VALUE); - this.endpoint = endpoint; - } - - @Override - public int fill(FluidStack resource, FluidAction action) { - if (resource.isEmpty()) - return 0; - int maxInput = Math.min(resource.getAmount(), Math.max(getTransferCapacity() - getFluidAmount(), 0)); - FluidStack toInsert = resource.copy(); - toInsert.setAmount(maxInput); - FluidPropagator.showBlockFace(endpoint.location).colored(0x77d196).lineWidth(1/4f); - return super.fill(toInsert, action); - } - - @Override - public FluidStack drain(int maxDrain, FluidAction action) { - return super.drain(maxDrain, action); - } - - public FluidStack provide() { - FluidStack heldFluid = getFluid(); - if (heldFluid.isEmpty()) - return heldFluid; - FluidStack copy = heldFluid.copy(); - copy.setAmount(1); - return copy; - } - - private int getTransferCapacity() { - return Math.min(endpoint.getTransferSpeed(true), endpoint.getTransferSpeed(false)); - } - -} diff --git a/src/main/java/com/simibubi/create/content/contraptions/fluids/OpenEndedPipe.java b/src/main/java/com/simibubi/create/content/contraptions/fluids/OpenEndedPipe.java index 4c7e415d1..501abf7da 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/fluids/OpenEndedPipe.java +++ b/src/main/java/com/simibubi/create/content/contraptions/fluids/OpenEndedPipe.java @@ -8,12 +8,10 @@ import com.simibubi.create.AllFluids; import com.simibubi.create.content.contraptions.fluids.potion.PotionFluidHandler; import com.simibubi.create.foundation.fluid.FluidHelper; import com.simibubi.create.foundation.utility.BlockFace; -import com.simibubi.create.foundation.utility.Iterate; import net.minecraft.block.BlockState; import net.minecraft.block.FlowingFluidBlock; import net.minecraft.entity.LivingEntity; -import net.minecraft.fluid.Fluid; import net.minecraft.fluid.Fluids; import net.minecraft.fluid.IFluidState; import net.minecraft.item.ItemStack; @@ -24,7 +22,6 @@ import net.minecraft.potion.EffectInstance; import net.minecraft.potion.PotionUtils; import net.minecraft.state.properties.BlockStateProperties; import net.minecraft.tags.FluidTags; -import net.minecraft.tileentity.TileEntity; import net.minecraft.util.Direction; import net.minecraft.util.SoundCategory; import net.minecraft.util.SoundEvents; @@ -34,10 +31,9 @@ import net.minecraft.world.World; import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.capability.IFluidHandler; -import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction; import net.minecraftforge.fluids.capability.templates.FluidTank; -public class OpenEndedPipe { +public class OpenEndedPipe extends FlowSource { World world; BlockPos pos; @@ -46,12 +42,12 @@ public class OpenEndedPipe { private OpenEndFluidHandler fluidHandler; private BlockPos outputPos; private boolean wasPulling; - private boolean stale; private FluidStack cachedFluid; private List cachedEffects; public OpenEndedPipe(BlockFace face) { + super(face); fluidHandler = new OpenEndFluidHandler(); outputPos = face.getConnectedPos(); pos = face.getPos(); @@ -60,15 +56,17 @@ public class OpenEndedPipe { aoe = aoe.expand(0, -1, 0); } - public void tick(World world, boolean pulling) { + @Override + public void manageSource(World world) { this.world = world; + } + + private FluidStack removeFluidFromSpace(boolean simulate) { + FluidStack empty = FluidStack.EMPTY; + if (world == null) + return empty; if (!world.isAreaLoaded(outputPos, 0)) - return; - if (pulling != wasPulling) { - if (pulling) - fluidHandler.clear(); - wasPulling = pulling; - } + return empty; BlockState state = world.getBlockState(outputPos); IFluidState fluidState = state.getFluidState(); @@ -76,71 +74,88 @@ public class OpenEndedPipe { if (!waterlog && !state.getMaterial() .isReplaceable()) - return; + return empty; + if (fluidState.isEmpty() || !fluidState.isSource()) + return empty; - if (pulling) { - if (fluidState.isEmpty() || !fluidState.isSource()) - return; - if (!fluidHandler.tryCollectFluid(fluidState.getFluid())) - return; - if (waterlog) { - world.setBlockState(outputPos, state.with(BlockStateProperties.WATERLOGGED, false), 3); - world.getPendingFluidTicks() - .scheduleTick(outputPos, Fluids.WATER, 1); - return; - } - world.setBlockState(outputPos, fluidState.getBlockState() - .with(FlowingFluidBlock.LEVEL, 14), 3); - return; + FluidStack stack = new FluidStack(fluidState.getFluid(), 1000); + + if (simulate) + return stack; + + if (waterlog) { + world.setBlockState(outputPos, state.with(BlockStateProperties.WATERLOGGED, false), 3); + world.getPendingFluidTicks() + .scheduleTick(outputPos, Fluids.WATER, 1); + return stack; } + world.setBlockState(outputPos, fluidState.getBlockState() + .with(FlowingFluidBlock.LEVEL, 14), 3); + return stack; + } - FluidStack fluid = fluidHandler.getFluid(); + private boolean provideFluidToSpace(FluidStack fluid, boolean simulate) { + if (world == null) + return false; + if (!world.isAreaLoaded(outputPos, 0)) + return false; + + BlockState state = world.getBlockState(outputPos); + IFluidState fluidState = state.getFluidState(); + boolean waterlog = state.has(BlockStateProperties.WATERLOGGED); + + if (!waterlog && !state.getMaterial() + .isReplaceable()) + return false; if (fluid.isEmpty()) - return; + return false; if (!FluidHelper.hasBlockState(fluid.getFluid())) { - fluidHandler.drain(fluid.getAmount() > 1 ? fluid.getAmount() - 1 : 1, FluidAction.EXECUTE); - if (fluidHandler.isEmpty()) - updatePumpIfNecessary(); - if (!fluid.getFluid() - .isEquivalentTo(AllFluids.POTION.get())) - return; - applyPotionEffects(world, fluid); - return; + if (!simulate) + applyEffects(world, fluid); + return true; } - Fluid providedFluid = fluidHandler.tryProvidingFluid(); - if (providedFluid == null) - return; - if (!fluidState.isEmpty() && fluidState.getFluid() != providedFluid) { - FluidReactions.handlePipeSpillCollision(world, outputPos, providedFluid, fluidState); - return; + if (!fluidState.isEmpty() && fluidState.getFluid() != fluid.getFluid()) { + FluidReactions.handlePipeSpillCollision(world, outputPos, fluid.getFluid(), fluidState); + return false; } + if (fluidState.isSource()) - return; + return false; + if (waterlog && fluid.getFluid() != Fluids.WATER) + return false; + if (simulate) + return true; - if (world.dimension.doesWaterVaporize() && providedFluid.getFluid() + if (world.dimension.doesWaterVaporize() && fluid.getFluid() .isIn(FluidTags.WATER)) { int i = outputPos.getX(); int j = outputPos.getY(); int k = outputPos.getZ(); world.playSound(null, i, j, k, SoundEvents.BLOCK_FIRE_EXTINGUISH, SoundCategory.BLOCKS, 0.5F, 2.6F + (world.rand.nextFloat() - world.rand.nextFloat()) * 0.8F); - return; + return true; } if (waterlog) { - if (providedFluid.getFluid() != Fluids.WATER) - return; world.setBlockState(outputPos, state.with(BlockStateProperties.WATERLOGGED, true), 3); world.getPendingFluidTicks() .scheduleTick(outputPos, Fluids.WATER, 1); - return; + return true; } - world.setBlockState(outputPos, providedFluid.getDefaultState() + world.setBlockState(outputPos, fluid.getFluid() + .getDefaultState() .getBlockState(), 3); + return true; } - private void applyPotionEffects(World world, FluidStack fluid) { + private void applyEffects(World world, FluidStack fluid) { + if (!fluid.getFluid() + .isEquivalentTo(AllFluids.POTION.get())) { + // other fx + return; + } + if (cachedFluid == null || cachedEffects == null || !fluid.isFluidEqual(cachedFluid)) { FluidStack copy = fluid.copy(); copy.setAmount(250); @@ -166,47 +181,30 @@ public class OpenEndedPipe { } - public LazyOptional getCapability() { + @Override + public LazyOptional provideHandler() { return LazyOptional.of(() -> fluidHandler); } - public CompoundNBT writeToNBT(CompoundNBT compound) { + public CompoundNBT serializeNBT() { + CompoundNBT compound = new CompoundNBT(); fluidHandler.writeToNBT(compound); compound.putBoolean("Pulling", wasPulling); + compound.put("Location", location.serializeNBT()); return compound; } - public void readNBT(CompoundNBT compound) { - fluidHandler.readFromNBT(compound); - wasPulling = compound.getBoolean("Pulling"); - } - - public void markStale() { - stale = true; - } - - public void unmarkStale() { - stale = false; - } - - public boolean isStale() { - return stale; - } - - private void updatePumpIfNecessary() { - if (world == null) - return; - if (!PumpBlock.isPump(world.getBlockState(pos))) - return; - TileEntity tileEntity = world.getTileEntity(pos); - if (tileEntity instanceof PumpTileEntity) - ((PumpTileEntity) tileEntity).sendData(); + public static OpenEndedPipe fromNBT(CompoundNBT compound) { + OpenEndedPipe oep = new OpenEndedPipe(BlockFace.fromNBT(compound.getCompound("Location"))); + oep.fluidHandler.readFromNBT(compound); + oep.wasPulling = compound.getBoolean("Pulling"); + return oep; } private class OpenEndFluidHandler extends FluidTank { public OpenEndFluidHandler() { - super(1500); + super(1000); } @Override @@ -218,76 +216,76 @@ public class OpenEndedPipe { return 0; if (resource.isEmpty()) return 0; - - FluidStack prevFluid = getFluid(); - BlockState state = world.getBlockState(outputPos); - IFluidState fluidState = state.getFluidState(); - if (!fluidState.isEmpty() && fluidState.getFluid() != resource.getFluid()) { - FluidReactions.handlePipeSpillCollision(world, outputPos, resource.getFluid(), fluidState); - return 0; - } - if (fluidState.isSource()) - return 0; - if (!(state.has(BlockStateProperties.WATERLOGGED) && resource.getFluid() == Fluids.WATER) - && !state.getMaterial() - .isReplaceable()) + if (!provideFluidToSpace(resource, true)) return 0; - // Never allow being filled above 1000 - FluidStack insertable = resource.copy(); - insertable.setAmount(Math.min(insertable.getAmount(), Math.max(1000 - getFluidAmount(), 0))); - int fill = super.fill(insertable, action); - - if (!getFluid().isFluidEqual(prevFluid)) - updatePumpIfNecessary(); + if (!getFluid().isEmpty() && !getFluid().isFluidEqual(resource)) + setFluid(FluidStack.EMPTY); + if (wasPulling) + wasPulling = false; + int fill = super.fill(resource, action); + if (action.execute() && (getFluidAmount() == 1000 || !FluidHelper.hasBlockState(getFluid().getFluid())) + && provideFluidToSpace(getFluid(), false)) + setFluid(FluidStack.EMPTY); return fill; } @Override public FluidStack drain(FluidStack resource, FluidAction action) { - boolean wasEmpty = isEmpty(); - FluidStack drain = super.drain(resource, action); - if (action.execute() && !wasEmpty && isEmpty()) - updatePumpIfNecessary(); - return drain; + return drainInner(resource.getAmount(), resource, action); } @Override public FluidStack drain(int maxDrain, FluidAction action) { - boolean wasEmpty = isEmpty(); - FluidStack drain = super.drain(maxDrain, action); - if (action.execute() && !wasEmpty && isEmpty()) - updatePumpIfNecessary(); - return drain; + return drainInner(maxDrain, null, action); } - public boolean tryCollectFluid(Fluid fluid) { - for (boolean simulate : Iterate.trueAndFalse) - if (super.fill(new FluidStack(fluid, 1000), - simulate ? FluidAction.SIMULATE : FluidAction.EXECUTE) != 1000) - return false; - updatePumpIfNecessary(); - return true; - } + private FluidStack drainInner(int amount, @Nullable FluidStack filter, FluidAction action) { + FluidStack empty = FluidStack.EMPTY; + boolean filterPresent = filter != null; - @Nullable - public Fluid tryProvidingFluid() { - Fluid fluid = getFluid().getFluid(); - for (boolean simulate : Iterate.trueAndFalse) - if (drain(1000, simulate ? FluidAction.SIMULATE : FluidAction.EXECUTE).getAmount() != 1000) - return null; - updatePumpIfNecessary(); - return fluid; - } + if (world == null) + return empty; + if (!world.isAreaLoaded(outputPos, 0)) + return empty; + if (amount == 0) + return empty; + if (amount > 1000) { + amount = 1000; + if (filterPresent) + filter = FluidHelper.copyStackWithAmount(filter, amount); + } - public void clear() { - boolean wasEmpty = isEmpty(); - setFluid(FluidStack.EMPTY); - if (!wasEmpty) - updatePumpIfNecessary(); + if (!wasPulling) + wasPulling = true; + + FluidStack drainedFromInternal = filterPresent ? super.drain(filter, action) : super.drain(amount, action); + if (!drainedFromInternal.isEmpty()) + return drainedFromInternal; + + FluidStack drainedFromWorld = removeFluidFromSpace(action.simulate()); + if (drainedFromWorld.isEmpty()) + return FluidStack.EMPTY; + if (filterPresent && !drainedFromWorld.isFluidEqual(filter)) + return FluidStack.EMPTY; + + int remainder = drainedFromWorld.getAmount() - amount; + drainedFromWorld.setAmount(amount); + + if (!action.simulate() && remainder > 0) { + if (!getFluid().isEmpty() && !getFluid().isFluidEqual(drainedFromWorld)) + setFluid(FluidStack.EMPTY); + super.fill(FluidHelper.copyStackWithAmount(drainedFromWorld, remainder), FluidAction.EXECUTE); + } + return drainedFromWorld; } } + @Override + public boolean isEndpoint() { + return true; + } + } diff --git a/src/main/java/com/simibubi/create/content/contraptions/fluids/PipeAttachmentModel.java b/src/main/java/com/simibubi/create/content/contraptions/fluids/PipeAttachmentModel.java index 1e7834771..554275530 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/fluids/PipeAttachmentModel.java +++ b/src/main/java/com/simibubi/create/content/contraptions/fluids/PipeAttachmentModel.java @@ -6,8 +6,9 @@ import java.util.List; import java.util.Random; import com.simibubi.create.AllBlockPartials; -import com.simibubi.create.content.contraptions.fluids.FluidPipeAttachmentBehaviour.AttachmentTypes; +import com.simibubi.create.content.contraptions.fluids.FluidTransportBehaviour.AttachmentTypes; import com.simibubi.create.content.contraptions.fluids.pipes.FluidPipeBlock; +import com.simibubi.create.content.contraptions.relays.elementary.BracketedTileEntityBehaviour; import com.simibubi.create.foundation.block.connected.BakedModelWrapperWithData; import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; import com.simibubi.create.foundation.utility.Iterate; @@ -35,16 +36,16 @@ public class PipeAttachmentModel extends BakedModelWrapperWithData { @Override protected Builder gatherModelData(Builder builder, ILightReader world, BlockPos pos, BlockState state) { PipeModelData data = new PipeModelData(); - FluidPipeAttachmentBehaviour attachmentBehaviour = - TileEntityBehaviour.get(world, pos, FluidPipeAttachmentBehaviour.TYPE); + FluidTransportBehaviour transport = TileEntityBehaviour.get(world, pos, FluidTransportBehaviour.TYPE); + BracketedTileEntityBehaviour bracket = TileEntityBehaviour.get(world, pos, BracketedTileEntityBehaviour.TYPE); - if (attachmentBehaviour != null) { + if (transport != null) for (Direction d : Iterate.directions) - data.putRim(d, attachmentBehaviour.getAttachment(world, pos, state, d)); - data.putBracket(attachmentBehaviour.getBracket()); - } - data.setEncased(FluidPipeBlock.shouldDrawCasing(world, pos, state)); + data.putRim(d, transport.getRenderedRimAttachment(world, pos, state, d)); + if (bracket != null) + data.putBracket(bracket.getBracket()); + data.setEncased(FluidPipeBlock.shouldDrawCasing(world, pos, state)); return builder.withInitial(PIPE_PROPERTY, data); } diff --git a/src/main/java/com/simibubi/create/content/contraptions/fluids/PipeConnection.java b/src/main/java/com/simibubi/create/content/contraptions/fluids/PipeConnection.java new file mode 100644 index 000000000..1dd1ed66c --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/fluids/PipeConnection.java @@ -0,0 +1,476 @@ +package com.simibubi.create.content.contraptions.fluids; + +import java.util.Optional; +import java.util.Random; +import java.util.function.Predicate; + +import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; +import com.simibubi.create.foundation.utility.BlockFace; +import com.simibubi.create.foundation.utility.Couple; +import com.simibubi.create.foundation.utility.Iterate; +import com.simibubi.create.foundation.utility.LerpedFloat; +import com.simibubi.create.foundation.utility.VecHelper; + +import net.minecraft.client.Minecraft; +import net.minecraft.entity.Entity; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.FloatNBT; +import net.minecraft.nbt.ListNBT; +import net.minecraft.particles.IParticleData; +import net.minecraft.util.Direction; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.common.util.Constants.NBT; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fml.DistExecutor; + +public class PipeConnection { + + Direction side; + + // Layer I + Couple pressure; // [inbound, outward] + Optional source; + Optional previousSource; + + // Layer II + Optional flow; + boolean particleSplashNextTick; + + // Layer III + Optional network; // not serialized + + public PipeConnection(Direction side) { + this.side = side; + pressure = Couple.create(() -> 0f); + flow = Optional.empty(); + previousSource = Optional.empty(); + source = Optional.empty(); + network = Optional.empty(); + particleSplashNextTick = false; + } + + public FluidStack getProvidedFluid() { + FluidStack empty = FluidStack.EMPTY; + if (!hasFlow()) + return empty; + Flow flow = this.flow.get(); + if (!flow.inbound) + return empty; + if (!flow.complete) + return empty; + return flow.fluid; + } + + public boolean flipFlowsIfPressureReversed() { + if (!hasFlow()) + return false; + boolean singlePressure = comparePressure() != 0 && (getInboundPressure() == 0 || getOutwardPressure() == 0); + Flow flow = this.flow.get(); + if (!singlePressure || comparePressure() < 0 == flow.inbound) + return false; + flow.inbound = !flow.inbound; + if (!flow.complete) + this.flow = Optional.empty(); + return true; + } + + public void manageSource(World world, BlockPos pos) { + if (!source.isPresent() && !determineSource(world, pos)) + return; + FlowSource flowSource = source.get(); + flowSource.manageSource(world); + } + + public boolean manageFlows(World world, BlockPos pos, FluidStack internalFluid, + Predicate extractionPredicate) { + + // Only keep network if still valid + Optional retainedNetwork = network; + network = Optional.empty(); + + // chunk border + if (!source.isPresent() && !determineSource(world, pos)) + return false; + FlowSource flowSource = source.get(); + + if (!hasFlow()) { + if (!hasPressure()) + return false; + + // Try starting a new flow + boolean prioritizeInbound = comparePressure() < 0; + for (boolean trueFalse : Iterate.trueAndFalse) { + boolean inbound = prioritizeInbound == trueFalse; + if (pressure.get(inbound) == 0) + continue; + if (tryStartingNewFlow(inbound, inbound ? flowSource.provideFluid(extractionPredicate) : internalFluid)) + return true; + } + return false; + } + + // Manage existing flow + Flow flow = this.flow.get(); + FluidStack provided = flow.inbound ? flowSource.provideFluid(extractionPredicate) : internalFluid; + if (!hasPressure() || provided.isEmpty() || !provided.isFluidEqual(flow.fluid)) { + this.flow = Optional.empty(); + return true; + } + + // Overwrite existing flow + if (flow.inbound != comparePressure() < 0) { + boolean inbound = !flow.inbound; + if (inbound && !provided.isEmpty() || !inbound && !internalFluid.isEmpty()) { + FluidPropagator.resetAffectedFluidNetworks(world, pos, side); + tryStartingNewFlow(inbound, inbound ? flowSource.provideFluid(extractionPredicate) : internalFluid); + return true; + } + } + + flowSource.whileFlowPresent(world, flow.inbound); + + if (!flowSource.isEndpoint()) + return false; + if (!flow.inbound) + return false; + + // Layer III + network = retainedNetwork; + if (!hasNetwork()) + network = Optional.of(new FluidNetwork(world, new BlockFace(pos, side), flowSource::provideHandler)); + network.get() + .tick(); + + return false; + } + + private boolean tryStartingNewFlow(boolean inbound, FluidStack providedFluid) { + if (providedFluid.isEmpty()) + return false; + Flow flow = new Flow(inbound, providedFluid); + this.flow = Optional.of(flow); + return true; + } + + private boolean determineSource(World world, BlockPos pos) { + if (!world.isAreaLoaded(pos, 1)) + return false; + BlockFace location = new BlockFace(pos, side); + + if (FluidPropagator.isOpenEnd(world, pos, side)) { + if (previousSource.orElse(null) instanceof OpenEndedPipe) + source = previousSource; + else + source = Optional.of(new OpenEndedPipe(location)); + return true; + } + + if (FluidPropagator.hasFluidCapability(world, location.getConnectedPos(), side.getOpposite())) { + source = Optional.of(new FlowSource.FluidHandler(location)); + return true; + } + + FluidTransportBehaviour behaviour = + TileEntityBehaviour.get(world, pos.offset(side), FluidTransportBehaviour.TYPE); + source = Optional.of(behaviour == null ? new FlowSource.Blocked(location) : new FlowSource.OtherPipe(location)); + return true; + } + + public void tickFlowProgress(World world, BlockPos pos) { + if (!hasFlow()) + return; + Flow flow = this.flow.get(); + if (flow.fluid.isEmpty()) + return; + + if (world.isRemote) { + if (!source.isPresent()) + determineSource(world, pos); + + spawnParticles(world, pos, flow.fluid); + if (particleSplashNextTick) + spawnSplashOnRim(world, pos, flow.fluid); + particleSplashNextTick = false; + } + + float flowSpeed = 1 / 32f + MathHelper.clamp(pressure.get(flow.inbound) / 512f, 0, 1) * 31 / 32f; + flow.progress.setValue(Math.min(flow.progress.getValue() + flowSpeed, 1)); + if (flow.progress.getValue() >= 1) + flow.complete = true; + } + + public void serializeNBT(CompoundNBT tag, boolean clientPacket) { + CompoundNBT connectionData = new CompoundNBT(); + tag.put(side.getName(), connectionData); + + if (hasPressure()) { + ListNBT pressureData = new ListNBT(); + pressureData.add(FloatNBT.of(getInboundPressure())); + pressureData.add(FloatNBT.of(getOutwardPressure())); + connectionData.put("Pressure", pressureData); + } + + if (hasOpenEnd()) + connectionData.put("OpenEnd", ((OpenEndedPipe) source.get()).serializeNBT()); + + if (hasFlow()) { + CompoundNBT flowData = new CompoundNBT(); + Flow flow = this.flow.get(); + flow.fluid.writeToNBT(flowData); + flowData.putBoolean("In", flow.inbound); + if (!flow.complete) + flowData.put("Progress", flow.progress.writeNBT()); + connectionData.put("Flow", flowData); + } + + } + + private boolean hasOpenEnd() { + return source.orElse(null) instanceof OpenEndedPipe; + } + + public void deserializeNBT(CompoundNBT tag, boolean clientPacket) { + CompoundNBT connectionData = tag.getCompound(side.getName()); + + if (connectionData.contains("Pressure")) { + ListNBT pressureData = connectionData.getList("Pressure", NBT.TAG_FLOAT); + pressure = Couple.create(pressureData.getFloat(0), pressureData.getFloat(1)); + } else + pressure.replace(f -> 0f); + + source = Optional.empty(); + if (connectionData.contains("OpenEnd")) + source = Optional.of(OpenEndedPipe.fromNBT(connectionData.getCompound("OpenEnd"))); + + if (connectionData.contains("Flow")) { + CompoundNBT flowData = connectionData.getCompound("Flow"); + FluidStack fluid = FluidStack.loadFluidStackFromNBT(flowData); + boolean inbound = flowData.getBoolean("In"); + if (!flow.isPresent()) { + flow = Optional.of(new Flow(inbound, fluid)); + if (clientPacket) + particleSplashNextTick = true; + } + Flow flow = this.flow.get(); + + flow.fluid = fluid; + flow.inbound = inbound; + flow.complete = !flowData.contains("Progress"); + + if (!flow.complete) + flow.progress.readNBT(flowData.getCompound("Progress"), clientPacket); + else { + if (flow.progress.getValue() == 0) + flow.progress.startWithValue(1); + flow.progress.setValue(1); + } + + } else + flow = Optional.empty(); + + } + + /** + * @return zero if outward == inbound
+ * positive if outward > inbound
+ * negative if outward < inbound + */ + public float comparePressure() { + return getOutwardPressure() - getInboundPressure(); + } + + public void wipePressure() { + this.pressure.replace(f -> 0f); + if (this.source.isPresent()) + this.previousSource = this.source; + this.source = Optional.empty(); + resetNetwork(); + } + + public FluidStack provideOutboundFlow() { + if (!hasFlow()) + return FluidStack.EMPTY; + Flow flow = this.flow.get(); + if (!flow.complete || flow.inbound) + return FluidStack.EMPTY; + return flow.fluid; + } + + public void addPressure(boolean inbound, float pressure) { + this.pressure = this.pressure.mapWithContext((f, in) -> in == inbound ? f + pressure : f); + } + + public boolean hasPressure() { + return getInboundPressure() != 0 || getOutwardPressure() != 0; + } + + private float getOutwardPressure() { + return pressure.getSecond(); + } + + private float getInboundPressure() { + return pressure.getFirst(); + } + + public boolean hasFlow() { + return flow.isPresent(); + } + + public boolean hasNetwork() { + return network.isPresent(); + } + + public void resetNetwork() { + network.ifPresent(FluidNetwork::reset); + } + + public class Flow { + + public boolean complete; + public boolean inbound; + public LerpedFloat progress; + public FluidStack fluid; + + public Flow(boolean inbound, FluidStack fluid) { + this.inbound = inbound; + this.fluid = fluid; + this.progress = LerpedFloat.linear() + .startWithValue(0); + this.complete = false; + } + + } + + public static final int MAX_PARTICLE_RENDER_DISTANCE = 20; + public static final int SPLASH_PARTICLE_AMOUNT = 1; + public static final float IDLE_PARTICLE_SPAWN_CHANCE = 1 / 1000f; + public static final float RIM_RADIUS = 1 / 4f + 1 / 64f; + public static final Random r = new Random(); + + public void spawnSplashOnRim(World world, BlockPos pos, FluidStack fluid) { + DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> spawnSplashOnRimInner(world, pos, fluid)); + } + + public void spawnParticles(World world, BlockPos pos, FluidStack fluid) { + DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> spawnParticlesInner(world, pos, fluid)); + } + + @OnlyIn(Dist.CLIENT) + private void spawnParticlesInner(World world, BlockPos pos, FluidStack fluid) { + if (!isRenderEntityWithinDistance(pos)) + return; + if (hasOpenEnd()) + spawnPouringLiquid(world, pos, fluid, 1); + else if (r.nextFloat() < IDLE_PARTICLE_SPAWN_CHANCE) + spawnRimParticles(world, pos, fluid, 1); + } + + @OnlyIn(Dist.CLIENT) + private void spawnSplashOnRimInner(World world, BlockPos pos, FluidStack fluid) { + if (!isRenderEntityWithinDistance(pos)) + return; + spawnRimParticles(world, pos, fluid, SPLASH_PARTICLE_AMOUNT); + } + + @OnlyIn(Dist.CLIENT) + private void spawnRimParticles(World world, BlockPos pos, FluidStack fluid, int amount) { + if (hasOpenEnd()) { + spawnPouringLiquid(world, pos, fluid, amount); + return; + } + + IParticleData particle = FluidFX.getDrippingParticle(fluid); + FluidFX.spawnRimParticles(world, pos, side, amount, particle, RIM_RADIUS); + } + + @OnlyIn(Dist.CLIENT) + private void spawnPouringLiquid(World world, BlockPos pos, FluidStack fluid, int amount) { + IParticleData particle = FluidFX.getFluidParticle(fluid); + Vec3d directionVec = new Vec3d(side.getDirectionVec()); + if (!hasFlow()) + return; + Flow flow = this.flow.get(); + FluidFX.spawnPouringLiquid(world, pos, amount, particle, RIM_RADIUS, directionVec, flow.inbound); + } + + @OnlyIn(Dist.CLIENT) + public static boolean isRenderEntityWithinDistance(BlockPos pos) { + Entity renderViewEntity = Minecraft.getInstance() + .getRenderViewEntity(); + if (renderViewEntity == null) + return false; + Vec3d center = VecHelper.getCenterOf(pos); + if (renderViewEntity.getPositionVec() + .distanceTo(center) > MAX_PARTICLE_RENDER_DISTANCE) + return false; + return true; + } + +// void visualizePressure(BlockPos pos) { +// if (!hasPressure()) +// return; +// +// pressure.forEachWithContext((pressure, inbound) -> { +// if (inbound) +// return; +// +// Vec3d directionVec = new Vec3d(side.getDirectionVec()); +// Vec3d scaleVec = directionVec.scale(-.25f * side.getAxisDirection() +// .getOffset()); +// directionVec = directionVec.scale(inbound ? .35f : .45f); +// CreateClient.outliner.chaseAABB("pressure" + pos.toShortString() + side.getName() + String.valueOf(inbound), +// FluidPropagator.smallCenter.offset(directionVec.add(new Vec3d(pos))) +// .grow(scaleVec.x, scaleVec.y, scaleVec.z) +// .expand(0, pressure / 64f, 0) +// .grow(1 / 64f)); +// }); +// } +// +// void visualizeFlow(BlockPos pos) { +// if (!hasFlow()) +// return; +// +// Vec3d directionVec = new Vec3d(side.getDirectionVec()); +// float size = 1 / 4f; +// float length = .5f; +// Flow flow = this.flow.get(); +// boolean inbound = flow.inbound; +// FluidStack fluid = flow.fluid; +// +// if (flow.progress == null) +// return; +// float value = flow.progress.getValue(); +// Vec3d start = directionVec.scale(inbound ? .5 : .5f - length); +// Vec3d offset = directionVec.scale(length * (inbound ? -1 : 1)) +// .scale(value); +// +// Vec3d scale = new Vec3d(1, 1, 1).subtract(directionVec.scale(side.getAxisDirection() +// .getOffset())) +// .scale(size); +// AxisAlignedBB bb = new AxisAlignedBB(start, start.add(offset)).offset(VecHelper.getCenterOf(pos)) +// .grow(scale.x, scale.y, scale.z); +// +// int color = 0x7fdbda; +// if (!fluid.isEmpty()) { +// Fluid fluid2 = fluid.getFluid(); +// if (fluid2 == Fluids.WATER) +// color = 0x1D4D9B; +// else if (fluid2 == Fluids.LAVA) +// color = 0xFF773D; +// else +// color = fluid2.getAttributes() +// .getColor(fluid); +// } +// +// CreateClient.outliner.chaseAABB(this, bb) +// .withFaceTexture(AllSpecialTextures.SELECTION) +// .colored(color) +// .lineWidth(0); +// } + +} diff --git a/src/main/java/com/simibubi/create/content/contraptions/fluids/PumpBlock.java b/src/main/java/com/simibubi/create/content/contraptions/fluids/PumpBlock.java index 9e707518a..fa8313f88 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/fluids/PumpBlock.java +++ b/src/main/java/com/simibubi/create/content/contraptions/fluids/PumpBlock.java @@ -1,15 +1,12 @@ package com.simibubi.create.content.contraptions.fluids; -import java.util.Map; +import java.util.Random; import org.apache.commons.lang3.mutable.MutableBoolean; import com.simibubi.create.AllShapes; import com.simibubi.create.AllTileEntities; import com.simibubi.create.content.contraptions.base.DirectionalKineticBlock; -import com.simibubi.create.content.contraptions.fluids.pipes.FluidPipeBlock; -import com.simibubi.create.foundation.utility.BlockFace; -import com.simibubi.create.foundation.utility.Iterate; import net.minecraft.block.Block; import net.minecraft.block.BlockState; @@ -30,8 +27,9 @@ import net.minecraft.util.math.shapes.VoxelShape; import net.minecraft.world.IBlockReader; import net.minecraft.world.IWorld; import net.minecraft.world.IWorldReader; +import net.minecraft.world.TickPriority; import net.minecraft.world.World; -import net.minecraftforge.fluids.FluidStack; +import net.minecraft.world.server.ServerWorld; public class PumpBlock extends DirectionalKineticBlock implements IWaterLoggable { @@ -67,26 +65,8 @@ public class PumpBlock extends DirectionalKineticBlock implements IWaterLoggable if (!(tileEntity instanceof PumpTileEntity)) return state; PumpTileEntity pump = (PumpTileEntity) tileEntity; - if (pump.networks == null) - return state; - - FluidNetwork apn1 = pump.networks.get(true); - FluidNetwork apn2 = pump.networks.get(false); - - // Collect pipes that can be skipped - apn1.clearFlows(world, true); - apn2.clearFlows(world, true); - - // Swap skipsets as the networks change sides - Map skippedConnections = apn1.previousFlow; - apn1.previousFlow = apn2.previousFlow; - apn2.previousFlow = skippedConnections; - - // Init networks next tick - pump.networksToUpdate.forEach(MutableBoolean::setTrue); - pump.networks.swap(); + pump.sidesToUpdate.forEach(MutableBoolean::setTrue); pump.reversed = !pump.reversed; - return state; } @@ -111,22 +91,29 @@ public class PumpBlock extends DirectionalKineticBlock implements IWaterLoggable public void neighborChanged(BlockState state, World world, BlockPos pos, Block otherBlock, BlockPos neighborPos, boolean isMoving) { DebugPacketSender.func_218806_a(world, pos); - if (world.isRemote) + Direction d = FluidPropagator.validateNeighbourChange(state, world, pos, otherBlock, neighborPos, isMoving); + if (d == null) return; - if (otherBlock instanceof FluidPipeBlock) + if (!isOpenAt(state, d)) return; - TileEntity tileEntity = world.getTileEntity(pos); - if (!(tileEntity instanceof PumpTileEntity)) - return; - PumpTileEntity pump = (PumpTileEntity) tileEntity; - Direction facing = state.get(FACING); - for (boolean front : Iterate.trueAndFalse) { - Direction side = front ? facing : facing.getOpposite(); - if (!pos.offset(side) - .equals(neighborPos)) - continue; - pump.updatePipesOnSide(side); - } + world.getPendingBlockTicks() + .scheduleTick(pos, this, 1, TickPriority.HIGH); +// if (world.isRemote) +// return; +// if (otherBlock instanceof FluidPipeBlock) +// return; +// TileEntity tileEntity = world.getTileEntity(pos); +// if (!(tileEntity instanceof PumpTileEntity)) +// return; +// PumpTileEntity pump = (PumpTileEntity) tileEntity; +// Direction facing = state.get(FACING); +// for (boolean front : Iterate.trueAndFalse) { +// Direction side = front ? facing : facing.getOpposite(); +// if (!pos.offset(side) +// .equals(neighborPos)) +// continue; +// pump.updatePipesOnSide(side); +// } } @Override @@ -163,4 +150,32 @@ public class PumpBlock extends DirectionalKineticBlock implements IWaterLoggable return state.getBlock() instanceof PumpBlock; } + @Override + public void onBlockAdded(BlockState state, World world, BlockPos pos, BlockState oldState, boolean isMoving) { + if (world.isRemote) + return; + if (state != oldState) + world.getPendingBlockTicks() + .scheduleTick(pos, this, 1, TickPriority.HIGH); + } + + public static boolean isOpenAt(BlockState state, Direction d) { + return d.getAxis() == state.get(FACING) + .getAxis(); + } + + @Override + public void scheduledTick(BlockState state, ServerWorld world, BlockPos pos, Random r) { + FluidPropagator.propagateChangedPipe(world, pos, state); + } + + @Override + public void onReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean isMoving) { + boolean blockTypeChanged = state.getBlock() != newState.getBlock(); + if (blockTypeChanged && !world.isRemote) + FluidPropagator.propagateChangedPipe(world, pos, state); + if (state.hasTileEntity() && (blockTypeChanged || !newState.hasTileEntity())) + world.removeTileEntity(pos); + } + } diff --git a/src/main/java/com/simibubi/create/content/contraptions/fluids/PumpEndpoint.java b/src/main/java/com/simibubi/create/content/contraptions/fluids/PumpEndpoint.java deleted file mode 100644 index eb0d0f18c..000000000 --- a/src/main/java/com/simibubi/create/content/contraptions/fluids/PumpEndpoint.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.simibubi.create.content.contraptions.fluids; - -import com.simibubi.create.foundation.utility.BlockFace; - -import net.minecraft.world.IWorld; -import net.minecraftforge.common.util.LazyOptional; -import net.minecraftforge.fluids.FluidStack; - -public class PumpEndpoint extends FluidNetworkEndpoint { - - PumpTileEntity pumpTE; - - public PumpEndpoint(BlockFace location, PumpTileEntity pumpTE) { - super(pumpTE.getWorld(), location, LazyOptional.empty()); - this.pumpTE = pumpTE; - } - - @Override - protected void onHandlerInvalidated(IWorld world) {} - - @Override - public FluidStack provideFluid() { - return pumpTE.providedFluid; - } - -} \ No newline at end of file diff --git a/src/main/java/com/simibubi/create/content/contraptions/fluids/PumpTileEntity.java b/src/main/java/com/simibubi/create/content/contraptions/fluids/PumpTileEntity.java index 4c36b15e9..901059d71 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/fluids/PumpTileEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/fluids/PumpTileEntity.java @@ -1,11 +1,13 @@ package com.simibubi.create.content.contraptions.fluids; import java.util.ArrayList; -import java.util.Collection; import java.util.HashMap; -import java.util.Iterator; +import java.util.HashSet; +import java.util.IdentityHashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; import javax.annotation.Nullable; @@ -19,48 +21,37 @@ import com.simibubi.create.foundation.utility.Couple; import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.LerpedFloat; import com.simibubi.create.foundation.utility.LerpedFloat.Chaser; -import com.simibubi.create.foundation.utility.NBTHelper; +import com.simibubi.create.foundation.utility.Pair; import net.minecraft.block.BlockState; import net.minecraft.nbt.CompoundNBT; -import net.minecraft.nbt.ListNBT; -import net.minecraft.particles.IParticleData; +import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntityType; import net.minecraft.util.Direction; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; import net.minecraft.world.ILightReader; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; -import net.minecraftforge.common.util.Constants.NBT; -import net.minecraftforge.fluids.FluidStack; +import net.minecraft.world.IWorld; +import net.minecraftforge.common.util.LazyOptional; +import net.minecraftforge.fluids.capability.CapabilityFluidHandler; import net.minecraftforge.fluids.capability.IFluidHandler; -import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction; -import net.minecraftforge.fml.DistExecutor; public class PumpTileEntity extends KineticTileEntity { LerpedFloat arrowDirection; - Couple networks; - Couple> openEnds; - Couple networksToUpdate; - + Couple sidesToUpdate; boolean reversed; - FluidStack providedFluid; public PumpTileEntity(TileEntityType typeIn) { super(typeIn); arrowDirection = LerpedFloat.linear() .startWithValue(1); - networksToUpdate = Couple.create(MutableBoolean::new); - openEnds = Couple.create(HashMap::new); - setProvidedFluid(FluidStack.EMPTY); + sidesToUpdate = Couple.create(MutableBoolean::new); } @Override public void addBehaviours(List behaviours) { super.addBehaviours(behaviours); - behaviours.add(new PumpAttachmentBehaviour(this)); + behaviours.add(new PumpFluidTransferBehaviour(this)); } @Override @@ -77,196 +68,235 @@ public class PumpTileEntity extends KineticTileEntity { if (world.isRemote) { if (speed == 0) return; - spawnParticles(); arrowDirection.chase(speed >= 0 ? 1 : -1, .5f, Chaser.EXP); arrowDirection.tickChaser(); return; } - BlockState blockState = getBlockState(); - if (!(blockState.getBlock() instanceof PumpBlock)) - return; - Direction face = blockState.get(PumpBlock.FACING); - MutableBoolean networkUpdated = new MutableBoolean(false); - - if (networks == null) { - networks = Couple.create(new FluidNetwork(), new FluidNetwork()); - networks.forEachWithContext((fn, front) -> { - BlockFace blockFace = new BlockFace(pos, front ? face : face.getOpposite()); - fn.assemble(world, this, blockFace); - FluidPropagator.showBlockFace(blockFace) - .lineWidth(1 / 8f); - }); - networkUpdated.setTrue(); - } - - networksToUpdate.forEachWithContext((update, front) -> { + sidesToUpdate.forEachWithContext((update, isFront) -> { if (update.isFalse()) return; - FluidNetwork activePipeNetwork = networks.get(front); - if (activePipeNetwork == null) - return; - BlockFace blockFace = new BlockFace(pos, front ? face : face.getOpposite()); - activePipeNetwork.reAssemble(world, this, blockFace); - FluidPropagator.showBlockFace(blockFace) - .lineWidth(1 / 8f); update.setFalse(); - networkUpdated.setTrue(); + distributePressureTo(isFront ? getFront() : getFront().getOpposite()); }); - if (networkUpdated.isTrue()) - return; - - networks.forEach(fn -> fn.tick(world, this)); - if (speed == 0) return; if (speed < 0 != reversed) { - networks.forEachWithContext((fn, current) -> fn.clearFlows(world, true)); reversed = speed < 0; return; } - - boolean pullingSide = isPullingOnSide(true); - float flowSpeed = Math.abs(speed) / 256f; - - networks.forEachWithContext((fn, front) -> { - boolean pulling = isPullingOnSide(front); - fn.tickFlows(world, this, pulling, flowSpeed); - openEnds.get(front) - .values() - .forEach(oep -> oep.tick(world, pulling)); - }); - - if (!networks.get(pullingSide) - .hasEndpoints()) { - setProvidedFluid(FluidStack.EMPTY); - return; - } - - if (networks.getFirst() - .hasEndpoints() - && networks.getSecond() - .hasEndpoints()) { - performTransfer(); - } - } @Override - public void remove() { - super.remove(); - if (networks != null) - networks.forEachWithContext((fn, current) -> fn.clearFlows(world, false)); + public void onSpeedChanged(float previousSpeed) { + super.onSpeedChanged(previousSpeed); + + if (previousSpeed == getSpeed()) + return; + if (speed != 0) + reversed = speed < 0; + if (world.isRemote) + return; + + BlockPos frontPos = pos.offset(getFront()); + BlockPos backPos = pos.offset(getFront().getOpposite()); + FluidPropagator.propagateChangedPipe(world, frontPos, world.getBlockState(frontPos)); + FluidPropagator.propagateChangedPipe(world, backPos, world.getBlockState(backPos)); } - private void performTransfer() { - boolean input = isPullingOnSide(true); - Collection inputs = networks.get(input) - .getEndpoints(true); - Collection outputs = networks.get(!input) - .getEndpoints(false); + protected void distributePressureTo(Direction side) { + if (getSpeed() == 0) + return; - int flowSpeed = getFluidTransferSpeed(); - FluidStack transfer = FluidStack.EMPTY; - for (boolean simulate : Iterate.trueAndFalse) { - FluidAction action = simulate ? FluidAction.SIMULATE : FluidAction.EXECUTE; + BlockFace start = new BlockFace(pos, side); + boolean pull = isPullingOnSide(isFront(side)); + Set targets = new HashSet<>(); + Map>> pipeGraph = new HashMap<>(); - List availableInputs = new ArrayList<>(inputs); - while (!availableInputs.isEmpty() && transfer.getAmount() < flowSpeed) { - int diff = flowSpeed - transfer.getAmount(); - int dividedTransfer = diff / availableInputs.size(); - int remainder = diff % availableInputs.size(); + if (!pull) + FluidPropagator.resetAffectedFluidNetworks(world, pos, side.getOpposite()); - for (Iterator iterator = availableInputs.iterator(); iterator.hasNext();) { - int toTransfer = dividedTransfer; - if (remainder > 0) { - toTransfer++; - remainder--; - } + if (!hasReachedValidEndpoint(world, start, pull)) { - FluidNetworkEndpoint ne = iterator.next(); - IFluidHandler handler = ne.provideHandler() - .orElse(null); - if (handler == null) { - iterator.remove(); - continue; - } - FluidStack drained = handler.drain(toTransfer, action); - if (drained.isEmpty()) { - iterator.remove(); - continue; - } - if (transfer.isFluidEqual(drained) || transfer.isEmpty()) { - if (drained.getAmount() < toTransfer) - iterator.remove(); - FluidStack copy = drained.copy(); - copy.setAmount(drained.getAmount() + transfer.getAmount()); - transfer = copy; - continue; - } - iterator.remove(); + pipeGraph.computeIfAbsent(pos, $ -> Pair.of(0, new IdentityHashMap<>())) + .getSecond() + .put(side, pull); + pipeGraph.computeIfAbsent(start.getConnectedPos(), $ -> Pair.of(1, new IdentityHashMap<>())) + .getSecond() + .put(side.getOpposite(), !pull); + + List> frontier = new ArrayList<>(); + Set visited = new HashSet<>(); + int maxDistance = FluidPropagator.getPumpRange(); + frontier.add(Pair.of(1, start.getConnectedPos())); + + while (!frontier.isEmpty()) { + Pair entry = frontier.remove(0); + int distance = entry.getFirst(); + BlockPos currentPos = entry.getSecond(); + + if (!world.isAreaLoaded(currentPos, 0)) + continue; + if (visited.contains(currentPos)) + continue; + visited.add(currentPos); + BlockState currentState = world.getBlockState(currentPos); + FluidTransportBehaviour pipe = FluidPropagator.getPipe(world, currentPos); + if (pipe == null) continue; - } - } + for (Direction face : FluidPropagator.getPipeConnections(currentState, pipe)) { + BlockFace blockFace = new BlockFace(currentPos, face); + BlockPos connectedPos = blockFace.getConnectedPos(); - List availableOutputs = new ArrayList<>(outputs); - while (!availableOutputs.isEmpty() && transfer.getAmount() > 0) { - int dividedTransfer = transfer.getAmount() / availableOutputs.size(); - int remainder = transfer.getAmount() % availableOutputs.size(); - - for (Iterator iterator = availableOutputs.iterator(); iterator.hasNext();) { - FluidNetworkEndpoint ne = iterator.next(); - int toTransfer = dividedTransfer; - if (remainder > 0) { - toTransfer++; - remainder--; - } - - if (transfer.isEmpty()) - break; - IFluidHandler handler = ne.provideHandler() - .orElse(null); - if (handler == null) { - iterator.remove(); + if (!world.isAreaLoaded(connectedPos, 0)) + continue; + if (blockFace.isEquivalent(start)) + continue; + if (hasReachedValidEndpoint(world, blockFace, pull)) { + pipeGraph.computeIfAbsent(currentPos, $ -> Pair.of(distance, new IdentityHashMap<>())) + .getSecond() + .put(face, pull); + targets.add(blockFace); continue; } - FluidStack divided = transfer.copy(); - divided.setAmount(toTransfer); - int fill = handler.fill(divided, action); - transfer.setAmount(transfer.getAmount() - fill); - if (fill < toTransfer) - iterator.remove(); + FluidTransportBehaviour pipeBehaviour = FluidPropagator.getPipe(world, connectedPos); + if (pipeBehaviour == null) + continue; + if (pipeBehaviour instanceof PumpFluidTransferBehaviour) + continue; + if (visited.contains(connectedPos)) + continue; + if (distance + 1 >= maxDistance) { + pipeGraph.computeIfAbsent(currentPos, $ -> Pair.of(distance, new IdentityHashMap<>())) + .getSecond() + .put(face, pull); + targets.add(blockFace); + continue; + } + + pipeGraph.computeIfAbsent(currentPos, $ -> Pair.of(distance, new IdentityHashMap<>())) + .getSecond() + .put(face, pull); + pipeGraph.computeIfAbsent(connectedPos, $ -> Pair.of(distance + 1, new IdentityHashMap<>())) + .getSecond() + .put(face.getOpposite(), !pull); + frontier.add(Pair.of(distance + 1, connectedPos)); } - } - - flowSpeed -= transfer.getAmount(); - transfer = FluidStack.EMPTY; } + + // DFS + Map> validFaces = new HashMap<>(); + searchForEndpointRecursively(pipeGraph, targets, validFaces, + new BlockFace(start.getPos(), start.getOppositeFace()), pull); + + float pressure = Math.abs(getSpeed()); + for (Set set : validFaces.values()) { + int parallelBranches = set.size(); + for (BlockFace face : set) { + BlockPos pipePos = face.getPos(); + Direction pipeSide = face.getFace(); + + if (pipePos.equals(pos)) + continue; + + boolean inbound = pipeGraph.get(pipePos) + .getSecond() + .get(pipeSide); + FluidTransportBehaviour pipeBehaviour = FluidPropagator.getPipe(world, pipePos); + if (pipeBehaviour == null) + continue; + + pipeBehaviour.addPressure(pipeSide, inbound, pressure / parallelBranches); + } + } + } - public int getFluidTransferSpeed() { - float rotationSpeed = Math.abs(getSpeed()); - int flowSpeed = (int) (rotationSpeed / 2f); - if (rotationSpeed != 0 && flowSpeed == 0) - flowSpeed = 1; - return flowSpeed; + protected boolean searchForEndpointRecursively(Map>> pipeGraph, + Set targets, Map> validFaces, BlockFace currentFace, boolean pull) { + BlockPos currentPos = currentFace.getPos(); + if (!pipeGraph.containsKey(currentPos)) + return false; + Pair> pair = pipeGraph.get(currentPos); + int distance = pair.getFirst(); + + boolean atLeastOneBranchSuccessful = false; + for (Direction nextFacing : Iterate.directions) { + if (nextFacing == currentFace.getFace()) + continue; + Map map = pair.getSecond(); + if (!map.containsKey(nextFacing)) + continue; + + BlockFace localTarget = new BlockFace(currentPos, nextFacing); + if (targets.contains(localTarget)) { + validFaces.computeIfAbsent(distance, $ -> new HashSet<>()) + .add(localTarget); + atLeastOneBranchSuccessful = true; + continue; + } + + if (map.get(nextFacing) != pull) + continue; + if (!searchForEndpointRecursively(pipeGraph, targets, validFaces, + new BlockFace(currentPos.offset(nextFacing), nextFacing.getOpposite()), pull)) + continue; + + validFaces.computeIfAbsent(distance, $ -> new HashSet<>()) + .add(localTarget); + atLeastOneBranchSuccessful = true; + } + + if (atLeastOneBranchSuccessful) + validFaces.computeIfAbsent(distance, $ -> new HashSet<>()) + .add(currentFace); + + return atLeastOneBranchSuccessful; + } + + private boolean hasReachedValidEndpoint(IWorld world, BlockFace blockFace, boolean pull) { + BlockPos connectedPos = blockFace.getConnectedPos(); + BlockState connectedState = world.getBlockState(connectedPos); + TileEntity tileEntity = world.getTileEntity(connectedPos); + Direction face = blockFace.getFace(); + + // facing a pump + if (PumpBlock.isPump(connectedState) && connectedState.get(PumpBlock.FACING) + .getAxis() == face.getAxis() && tileEntity instanceof PumpTileEntity) { + PumpTileEntity pumpTE = (PumpTileEntity) tileEntity; + return pumpTE.isPullingOnSide(pumpTE.isFront(blockFace.getOppositeFace())) != pull; + } + + // other pipe, no endpoint + FluidTransportBehaviour pipe = FluidPropagator.getPipe(world, connectedPos); + if (pipe != null && pipe.canHaveFlowToward(connectedState, blockFace.getOppositeFace())) + return false; + + // fluid handler endpoint + if (tileEntity != null) { + LazyOptional capability = + tileEntity.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, face.getOpposite()); + if (capability.isPresent()) + return true; + } + + // open endpoint + return FluidPropagator.isOpenEnd(world, blockFace.getPos(), face); } @Override public void write(CompoundNBT compound, boolean clientPacket) { compound.putBoolean("Reversed", reversed); - serializeOpenEnds(compound); super.write(compound, clientPacket); } @Override protected void read(CompoundNBT compound, boolean clientPacket) { reversed = compound.getBoolean("Reversed"); - deserializeOpenEnds(compound); super.read(compound, clientPacket); } @@ -274,11 +304,10 @@ public class PumpTileEntity extends KineticTileEntity { if (!isSideAccessible(side)) return; updatePipeNetwork(isFront(side)); + getBehaviour(FluidTransportBehaviour.TYPE).wipePressure(); } protected boolean isFront(Direction side) { - if (networks == null) - return false; BlockState blockState = getBlockState(); if (!(blockState.getBlock() instanceof PumpBlock)) return false; @@ -296,13 +325,8 @@ public class PumpTileEntity extends KineticTileEntity { } protected void updatePipeNetwork(boolean front) { - if (networks != null) - networks.get(front) - .clearFlows(world, true); - networksToUpdate.get(front) + sidesToUpdate.get(front) .setTrue(); - if (getSpeed() == 0 || (isPullingOnSide(front)) && networks != null) - setProvidedFluid(FluidStack.EMPTY); } public boolean isSideAccessible(Direction side) { @@ -317,113 +341,32 @@ public class PumpTileEntity extends KineticTileEntity { return front == reversed; } - public void spawnParticles() { - DistExecutor.runWhenOn(Dist.CLIENT, () -> this::spawnParticlesInner); - } + class PumpFluidTransferBehaviour extends FluidTransportBehaviour { - @OnlyIn(Dist.CLIENT) - private void spawnParticlesInner() { - if (!FluidPipeBehaviour.isRenderEntityWithinDistance(pos)) - return; - for (boolean front : Iterate.trueAndFalse) { - Direction side = getFront(); - if (side == null) - return; - if (!front) - side = side.getOpposite(); - if (!FluidPropagator.isOpenEnd(world, pos, side)) - continue; - BlockFace key = new BlockFace(pos, side); - Map map = openEnds.get(front); - if (map.containsKey(key)) { - FluidStack fluidStack = map.get(key) - .getCapability() - .map(fh -> fh.getFluidInTank(0)) - .orElse(FluidStack.EMPTY); - if (!fluidStack.isEmpty()) - spawnPouringLiquid(fluidStack, side, 1); - } - } - } - - @OnlyIn(Dist.CLIENT) - private void spawnPouringLiquid(FluidStack fluid, Direction side, int amount) { - IParticleData particle = FluidFX.getFluidParticle(fluid); - float rimRadius = 1 / 4f + 1 / 64f; - boolean inbound = isPullingOnSide(getFront() == side); - Vec3d directionVec = new Vec3d(side.getDirectionVec()); - FluidFX.spawnPouringLiquid(world, pos, amount, particle, rimRadius, directionVec, inbound); - } - - public Map getOpenEnds(Direction side) { - return openEnds.get(isFront(side)); - } - - private void serializeOpenEnds(CompoundNBT compound) { - compound.put("OpenEnds", openEnds.serializeEach(m -> { - CompoundNBT compoundNBT = new CompoundNBT(); - ListNBT entries = new ListNBT(); - m.entrySet() - .forEach(e -> { - CompoundNBT innerCompound = new CompoundNBT(); - innerCompound.put("Pos", e.getKey() - .serializeNBT()); - e.getValue() - .writeToNBT(innerCompound); - entries.add(innerCompound); - }); - compoundNBT.put("Entries", entries); - return compoundNBT; - })); - } - - private void deserializeOpenEnds(CompoundNBT compound) { - openEnds = Couple.deserializeEach(compound.getList("OpenEnds", NBT.TAG_COMPOUND), c -> { - Map map = new HashMap<>(); - NBTHelper.iterateCompoundList(c.getList("Entries", NBT.TAG_COMPOUND), innerCompound -> { - BlockFace key = BlockFace.fromNBT(innerCompound.getCompound("Pos")); - OpenEndedPipe value = new OpenEndedPipe(key); - value.readNBT(innerCompound); - map.put(key, value); - }); - return map; - }); - - compound.put("OpenEnds", openEnds.serializeEach(m -> { - CompoundNBT compoundNBT = new CompoundNBT(); - ListNBT entries = new ListNBT(); - m.entrySet() - .forEach(e -> { - CompoundNBT innerCompound = new CompoundNBT(); - innerCompound.put("Pos", e.getKey() - .serializeNBT()); - e.getValue() - .writeToNBT(innerCompound); - entries.add(innerCompound); - }); - compoundNBT.put("Entries", entries); - return compoundNBT; - })); - } - - public void setProvidedFluid(FluidStack providedFluid) { - this.providedFluid = providedFluid; - } - - class PumpAttachmentBehaviour extends FluidPipeAttachmentBehaviour { - - public PumpAttachmentBehaviour(SmartTileEntity te) { + public PumpFluidTransferBehaviour(SmartTileEntity te) { super(te); } @Override - public boolean isPipeConnectedTowards(BlockState state, Direction direction) { + public void tick() { + super.tick(); + for (Entry entry : interfaces.entrySet()) { + boolean pull = isPullingOnSide(isFront(entry.getKey())); + Couple pressure = entry.getValue().pressure; + pressure.set(pull, Math.abs(getSpeed())); + pressure.set(!pull, 0f); + } + } + + @Override + public boolean canHaveFlowToward(BlockState state, Direction direction) { return isSideAccessible(direction); } @Override - public AttachmentTypes getAttachment(ILightReader world, BlockPos pos, BlockState state, Direction direction) { - AttachmentTypes attachment = super.getAttachment(world, pos, state, direction); + public AttachmentTypes getRenderedRimAttachment(ILightReader world, BlockPos pos, BlockState state, + Direction direction) { + AttachmentTypes attachment = super.getRenderedRimAttachment(world, pos, state, direction); if (attachment == AttachmentTypes.RIM) return AttachmentTypes.NONE; return attachment; diff --git a/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/AxisPipeBlock.java b/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/AxisPipeBlock.java index a3570ee9a..d93f79610 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/AxisPipeBlock.java +++ b/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/AxisPipeBlock.java @@ -6,8 +6,8 @@ import java.util.Random; import com.simibubi.create.AllBlocks; import com.simibubi.create.AllShapes; -import com.simibubi.create.content.contraptions.fluids.FluidPipeAttachmentBehaviour; import com.simibubi.create.content.contraptions.fluids.FluidPropagator; +import com.simibubi.create.content.contraptions.relays.elementary.BracketedTileEntityBehaviour; import com.simibubi.create.content.contraptions.wrench.IWrenchableWithBracket; import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; import com.simibubi.create.foundation.utility.Iterate; @@ -22,9 +22,9 @@ import net.minecraft.network.DebugPacketSender; import net.minecraft.state.BooleanProperty; import net.minecraft.util.ActionResultType; import net.minecraft.util.Direction; -import net.minecraft.util.Hand; import net.minecraft.util.Direction.Axis; import net.minecraft.util.Direction.AxisDirection; +import net.minecraft.util.Hand; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockRayTraceResult; import net.minecraft.util.math.RayTraceResult; @@ -126,7 +126,7 @@ public class AxisPipeBlock extends RotatedPillarBlock implements IWrenchableWith @Override public Optional removeBracket(IBlockReader world, BlockPos pos) { - FluidPipeAttachmentBehaviour behaviour = TileEntityBehaviour.get(world, pos, FluidPipeAttachmentBehaviour.TYPE); + BracketedTileEntityBehaviour behaviour = TileEntityBehaviour.get(world, pos, BracketedTileEntityBehaviour.TYPE); if (behaviour == null) return Optional.empty(); BlockState bracket = behaviour.getBracket(); diff --git a/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/BracketBlockItem.java b/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/BracketBlockItem.java index 98e563b1f..2dfa57d2c 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/BracketBlockItem.java +++ b/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/BracketBlockItem.java @@ -2,7 +2,6 @@ package com.simibubi.create.content.contraptions.fluids.pipes; import java.util.Optional; -import com.simibubi.create.content.contraptions.fluids.FluidPipeAttachmentBehaviour; import com.simibubi.create.content.contraptions.relays.elementary.BracketedTileEntityBehaviour; import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; @@ -32,38 +31,36 @@ public class BracketBlockItem extends BlockItem { BracketBlock bracketBlock = getBracketBlock(); PlayerEntity player = context.getPlayer(); - BracketedTileEntityBehaviour behaviour = TileEntityBehaviour.get(world, pos, FluidPipeAttachmentBehaviour.TYPE); + BracketedTileEntityBehaviour behaviour = TileEntityBehaviour.get(world, pos, BracketedTileEntityBehaviour.TYPE); + if (behaviour == null) - behaviour = TileEntityBehaviour.get(world, pos, BracketedTileEntityBehaviour.TYPE); - - if (behaviour != null && behaviour.canHaveBracket()) { - if (world.isRemote) - return ActionResultType.SUCCESS; - - Optional suitableBracket = bracketBlock.getSuitableBracket(state, context.getFace()); - if (!suitableBracket.isPresent() && player != null) - suitableBracket = - bracketBlock.getSuitableBracket(state, Direction.getFacingDirections(player)[0].getOpposite()); - if (!suitableBracket.isPresent()) - return ActionResultType.SUCCESS; - - BlockState bracket = behaviour.getBracket(); - behaviour.applyBracket(suitableBracket.get()); - if (player == null || !player.isCreative()) { - context.getItem() - .shrink(1); - if (bracket != Blocks.AIR.getDefaultState()) { - ItemStack returnedStack = new ItemStack(bracket.getBlock()); - if (player == null) - Block.spawnAsEntity(world, pos, returnedStack); - else - player.inventory.placeItemBackInInventory(world, returnedStack); - } - } + return ActionResultType.FAIL; + if (!behaviour.canHaveBracket()) + return ActionResultType.FAIL; + if (world.isRemote) return ActionResultType.SUCCESS; - } - return ActionResultType.FAIL; + Optional suitableBracket = bracketBlock.getSuitableBracket(state, context.getFace()); + if (!suitableBracket.isPresent() && player != null) + suitableBracket = + bracketBlock.getSuitableBracket(state, Direction.getFacingDirections(player)[0].getOpposite()); + if (!suitableBracket.isPresent()) + return ActionResultType.SUCCESS; + + BlockState bracket = behaviour.getBracket(); + behaviour.applyBracket(suitableBracket.get()); + if (player == null || !player.isCreative()) { + context.getItem() + .shrink(1); + if (bracket != Blocks.AIR.getDefaultState()) { + ItemStack returnedStack = new ItemStack(bracket.getBlock()); + if (player == null) + Block.spawnAsEntity(world, pos, returnedStack); + else + player.inventory.placeItemBackInInventory(world, returnedStack); + } + } + return ActionResultType.SUCCESS; } private BracketBlock getBracketBlock() { diff --git a/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/FluidPipeBlock.java b/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/FluidPipeBlock.java index 67a895eb2..8d32ddac6 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/FluidPipeBlock.java +++ b/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/FluidPipeBlock.java @@ -7,8 +7,9 @@ import javax.annotation.Nullable; import com.simibubi.create.AllBlocks; import com.simibubi.create.AllTileEntities; -import com.simibubi.create.content.contraptions.fluids.FluidPipeAttachmentBehaviour; import com.simibubi.create.content.contraptions.fluids.FluidPropagator; +import com.simibubi.create.content.contraptions.fluids.FluidTransportBehaviour; +import com.simibubi.create.content.contraptions.relays.elementary.BracketedTileEntityBehaviour; import com.simibubi.create.content.contraptions.wrench.IWrenchableWithBracket; import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; import com.simibubi.create.foundation.utility.Iterate; @@ -136,17 +137,17 @@ public class FluidPipeBlock extends SixWayBlock implements IWaterLoggable, IWren return state.getBlock() instanceof FluidPipeBlock; } - public static boolean canConnectTo(ILightReader world, BlockPos pos, BlockState neighbour, Direction blockFace) { - if (FluidPropagator.hasFluidCapability(neighbour, world, pos, blockFace)) + public static boolean canConnectTo(ILightReader world, BlockPos neighbourPos, BlockState neighbour, Direction direction) { + if (FluidPropagator.hasFluidCapability(world, neighbourPos, direction.getOpposite())) return true; - FluidPipeAttachmentBehaviour attachmentBehaviour = - TileEntityBehaviour.get(world, pos, FluidPipeAttachmentBehaviour.TYPE); + FluidTransportBehaviour transport = TileEntityBehaviour.get(world, neighbourPos, FluidTransportBehaviour.TYPE); + BracketedTileEntityBehaviour bracket = TileEntityBehaviour.get(world, neighbourPos, BracketedTileEntityBehaviour.TYPE); if (isPipe(neighbour)) - return attachmentBehaviour == null || attachmentBehaviour.getBracket() == Blocks.AIR.getDefaultState() - || FluidPropagator.getStraightPipeAxis(neighbour) == blockFace.getAxis(); - if (attachmentBehaviour == null) + return bracket == null || !bracket.isBacketPresent() + || FluidPropagator.getStraightPipeAxis(neighbour) == direction.getAxis(); + if (transport == null) return false; - return attachmentBehaviour.isPipeConnectedTowards(neighbour, blockFace.getOpposite()); + return transport.canHaveFlowToward(neighbour, direction.getOpposite()); } public static boolean shouldDrawRim(ILightReader world, BlockPos pos, BlockState state, Direction direction) { @@ -220,8 +221,8 @@ public class FluidPipeBlock extends SixWayBlock implements IWaterLoggable, IWren public BlockState updateBlockState(BlockState state, Direction preferredDirection, @Nullable Direction ignore, ILightReader world, BlockPos pos) { - FluidPipeAttachmentBehaviour behaviour = TileEntityBehaviour.get(world, pos, FluidPipeAttachmentBehaviour.TYPE); - if (behaviour != null && behaviour.getBracket() != Blocks.AIR.getDefaultState()) + BracketedTileEntityBehaviour bracket = TileEntityBehaviour.get(world, pos, BracketedTileEntityBehaviour.TYPE); + if (bracket != null && bracket.isBacketPresent()) return state; // Update sides that are not ignored @@ -258,7 +259,8 @@ public class FluidPipeBlock extends SixWayBlock implements IWaterLoggable, IWren @Override public Optional removeBracket(IBlockReader world, BlockPos pos) { - FluidPipeAttachmentBehaviour behaviour = TileEntityBehaviour.get(world, pos, FluidPipeAttachmentBehaviour.TYPE); + BracketedTileEntityBehaviour behaviour = + BracketedTileEntityBehaviour.get(world, pos, BracketedTileEntityBehaviour.TYPE); if (behaviour == null) return Optional.empty(); BlockState bracket = behaviour.getBracket(); diff --git a/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/FluidPipeTileEntity.java b/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/FluidPipeTileEntity.java index a90f4c92d..3b5cf2cfe 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/FluidPipeTileEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/FluidPipeTileEntity.java @@ -3,8 +3,8 @@ package com.simibubi.create.content.contraptions.fluids.pipes; import java.util.List; import com.simibubi.create.AllBlocks; -import com.simibubi.create.content.contraptions.fluids.FluidPipeAttachmentBehaviour; -import com.simibubi.create.content.contraptions.fluids.FluidPipeBehaviour; +import com.simibubi.create.content.contraptions.fluids.FluidTransportBehaviour; +import com.simibubi.create.content.contraptions.relays.elementary.BracketedTileEntityBehaviour; import com.simibubi.create.foundation.tileEntity.SmartTileEntity; import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; @@ -22,43 +22,40 @@ public class FluidPipeTileEntity extends SmartTileEntity { @Override public void addBehaviours(List behaviours) { - behaviours.add(new StandardPipeBehaviour(this)); - behaviours.add(new StandardPipeAttachmentBehaviour(this)); + behaviours.add(new StandardPipeFluidTransportBehaviour(this)); + behaviours.add(new BracketedTileEntityBehaviour(this, this::canHaveBracket)); } - class StandardPipeBehaviour extends FluidPipeBehaviour { + private boolean canHaveBracket(BlockState state) { + return !(state.getBlock() instanceof EncasedPipeBlock); + } - public StandardPipeBehaviour(SmartTileEntity te) { + class StandardPipeFluidTransportBehaviour extends FluidTransportBehaviour { + + public StandardPipeFluidTransportBehaviour(SmartTileEntity te) { super(te); } @Override - public boolean isConnectedTo(BlockState state, Direction direction) { + public boolean canHaveFlowToward(BlockState state, Direction direction) { return (FluidPipeBlock.isPipe(state) || state.getBlock() instanceof EncasedPipeBlock) && state.get(FluidPipeBlock.FACING_TO_PROPERTY_MAP.get(direction)); } - } - - class StandardPipeAttachmentBehaviour extends FluidPipeAttachmentBehaviour { - - public StandardPipeAttachmentBehaviour(SmartTileEntity te) { - super(te); - } - @Override - public AttachmentTypes getAttachment(ILightReader world, BlockPos pos, BlockState state, Direction direction) { - AttachmentTypes attachment = super.getAttachment(world, pos, state, direction); + public AttachmentTypes getRenderedRimAttachment(ILightReader world, BlockPos pos, BlockState state, + Direction direction) { + AttachmentTypes attachment = super.getRenderedRimAttachment(world, pos, state, direction); if (attachment == AttachmentTypes.RIM && AllBlocks.ENCASED_FLUID_PIPE.has(state)) return AttachmentTypes.RIM; BlockPos offsetPos = pos.offset(direction); if (!FluidPipeBlock.isPipe(world.getBlockState(offsetPos))) { - FluidPipeAttachmentBehaviour attachmentBehaviour = - TileEntityBehaviour.get(world, offsetPos, FluidPipeAttachmentBehaviour.TYPE); - if (attachmentBehaviour != null && attachmentBehaviour - .isPipeConnectedTowards(world.getBlockState(offsetPos), direction.getOpposite())) + FluidTransportBehaviour pipeBehaviour = + TileEntityBehaviour.get(world, offsetPos, FluidTransportBehaviour.TYPE); + if (pipeBehaviour != null + && pipeBehaviour.canHaveFlowToward(world.getBlockState(offsetPos), direction.getOpposite())) return AttachmentTypes.NONE; } diff --git a/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/FluidValveBlock.java b/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/FluidValveBlock.java index 4b30fa022..aa93364de 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/FluidValveBlock.java +++ b/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/FluidValveBlock.java @@ -1,12 +1,16 @@ package com.simibubi.create.content.contraptions.fluids.pipes; +import java.util.Random; + import com.simibubi.create.AllShapes; import com.simibubi.create.AllTileEntities; import com.simibubi.create.content.contraptions.base.DirectionalAxisKineticBlock; +import com.simibubi.create.content.contraptions.fluids.FluidPropagator; import com.simibubi.create.foundation.utility.Iterate; import net.minecraft.block.Block; import net.minecraft.block.BlockState; +import net.minecraft.network.DebugPacketSender; import net.minecraft.state.BooleanProperty; import net.minecraft.state.StateContainer.Builder; import net.minecraft.tileentity.TileEntity; @@ -17,6 +21,9 @@ import net.minecraft.util.math.shapes.ISelectionContext; import net.minecraft.util.math.shapes.VoxelShape; import net.minecraft.world.IBlockReader; import net.minecraft.world.IWorldReader; +import net.minecraft.world.TickPriority; +import net.minecraft.world.World; +import net.minecraft.world.server.ServerWorld; public class FluidValveBlock extends DirectionalAxisKineticBlock implements IAxisPipe { @@ -75,4 +82,49 @@ public class FluidValveBlock extends DirectionalAxisKineticBlock implements IAxi return getPipeAxis(state); } + @Override + public void onReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean isMoving) { + boolean blockTypeChanged = state.getBlock() != newState.getBlock(); + if (blockTypeChanged && !world.isRemote) + FluidPropagator.propagateChangedPipe(world, pos, state); + if (state.hasTileEntity() && (blockTypeChanged || !newState.hasTileEntity())) + world.removeTileEntity(pos); + } + + @Override + public boolean isValidPosition(BlockState p_196260_1_, IWorldReader p_196260_2_, BlockPos p_196260_3_) { + return true; + } + + @Override + public void onBlockAdded(BlockState state, World world, BlockPos pos, BlockState oldState, boolean isMoving) { + if (world.isRemote) + return; + if (state != oldState) + world.getPendingBlockTicks() + .scheduleTick(pos, this, 1, TickPriority.HIGH); + } + + @Override + public void neighborChanged(BlockState state, World world, BlockPos pos, Block otherBlock, BlockPos neighborPos, + boolean isMoving) { + DebugPacketSender.func_218806_a(world, pos); + Direction d = FluidPropagator.validateNeighbourChange(state, world, pos, otherBlock, neighborPos, isMoving); + if (d == null) + return; + if (!isOpenAt(state, d)) + return; + world.getPendingBlockTicks() + .scheduleTick(pos, this, 1, TickPriority.HIGH); + } + + public static boolean isOpenAt(BlockState state, Direction d) { + return d.getAxis() == getPipeAxis(state); + } + + @Override + public void scheduledTick(BlockState state, ServerWorld world, BlockPos pos, Random r) { + FluidPropagator.propagateChangedPipe(world, pos, state); + } + } diff --git a/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/FluidValveTileEntity.java b/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/FluidValveTileEntity.java index 1eb5a71b6..1f964928e 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/FluidValveTileEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/FluidValveTileEntity.java @@ -3,8 +3,7 @@ package com.simibubi.create.content.contraptions.fluids.pipes; import java.util.List; import com.simibubi.create.content.contraptions.base.KineticTileEntity; -import com.simibubi.create.content.contraptions.fluids.FluidPipeBehaviour; -import com.simibubi.create.content.contraptions.fluids.pipes.StraightPipeTileEntity.StraightPipeAttachmentBehaviour; +import com.simibubi.create.content.contraptions.fluids.pipes.StraightPipeTileEntity.StraightPipeFluidTransportBehaviour; import com.simibubi.create.foundation.tileEntity.SmartTileEntity; import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; import com.simibubi.create.foundation.utility.LerpedFloat; @@ -24,7 +23,8 @@ public class FluidValveTileEntity extends KineticTileEntity { public FluidValveTileEntity(TileEntityType tileEntityTypeIn) { super(tileEntityTypeIn); pointer = LerpedFloat.linear() - .startWithValue(0).chase(0, 0, Chaser.LINEAR); + .startWithValue(0) + .chase(0, 0, Chaser.LINEAR); } @Override @@ -34,20 +34,20 @@ public class FluidValveTileEntity extends KineticTileEntity { pointer.chase(speed > 0 ? 1 : 0, getChaseSpeed(), Chaser.LINEAR); sendData(); } - + @Override public void tick() { super.tick(); pointer.tickChaser(); - + if (world.isRemote) return; - + BlockState blockState = getBlockState(); if (!(blockState.getBlock() instanceof FluidValveBlock)) return; boolean stateOpen = blockState.get(FluidValveBlock.ENABLED); - + if (stateOpen && pointer.getValue() == 0) { switchToBlockState(world, pos, blockState.with(FluidValveBlock.ENABLED, false)); return; @@ -77,27 +77,26 @@ public class FluidValveTileEntity extends KineticTileEntity { @Override public void addBehaviours(List behaviours) { behaviours.add(new ValvePipeBehaviour(this)); - behaviours.add(new StraightPipeAttachmentBehaviour(this)); } - class ValvePipeBehaviour extends FluidPipeBehaviour { + class ValvePipeBehaviour extends StraightPipeFluidTransportBehaviour { public ValvePipeBehaviour(SmartTileEntity te) { super(te); } @Override - public boolean isConnectedTo(BlockState state, Direction direction) { + public boolean canHaveFlowToward(BlockState state, Direction direction) { return FluidValveBlock.getPipeAxis(state) == direction.getAxis(); } @Override - public boolean canTransferToward(FluidStack fluid, BlockState state, Direction direction, boolean inbound) { + public boolean canPullFluidFrom(FluidStack fluid, BlockState state, Direction direction) { if (state.has(FluidValveBlock.ENABLED) && state.get(FluidValveBlock.ENABLED)) - return super.canTransferToward(fluid, state, direction, inbound); + return super.canPullFluidFrom(fluid, state, direction); return false; } - + } } diff --git a/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/SmartFluidPipeBlock.java b/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/SmartFluidPipeBlock.java index de3964bc9..70ce6c977 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/SmartFluidPipeBlock.java +++ b/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/SmartFluidPipeBlock.java @@ -117,6 +117,11 @@ public class SmartFluidPipeBlock extends HorizontalFaceBlock implements IAxisPip public static boolean isOpenAt(BlockState state, Direction d) { return d.getAxis() == getPipeAxis(state); } + + @Override + public void scheduledTick(BlockState state, ServerWorld world, BlockPos pos, Random r) { + FluidPropagator.propagateChangedPipe(world, pos, state); + } protected static Axis getPipeAxis(BlockState state) { return state.get(FACE) == AttachFace.WALL ? Axis.Y @@ -134,11 +139,6 @@ public class SmartFluidPipeBlock extends HorizontalFaceBlock implements IAxisPip return AllTileEntities.SMART_FLUID_PIPE.create(); } - @Override - public void scheduledTick(BlockState state, ServerWorld world, BlockPos pos, Random r) { - FluidPropagator.propagateChangedPipe(world, pos, state); - } - @Override public VoxelShape getShape(BlockState state, IBlockReader p_220053_2_, BlockPos p_220053_3_, ISelectionContext p_220053_4_) { diff --git a/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/SmartFluidPipeTileEntity.java b/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/SmartFluidPipeTileEntity.java index 9b5378b88..dd6b7978a 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/SmartFluidPipeTileEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/SmartFluidPipeTileEntity.java @@ -3,9 +3,8 @@ package com.simibubi.create.content.contraptions.fluids.pipes; import java.util.List; import com.mojang.blaze3d.matrix.MatrixStack; -import com.simibubi.create.content.contraptions.fluids.FluidPipeBehaviour; import com.simibubi.create.content.contraptions.fluids.FluidPropagator; -import com.simibubi.create.content.contraptions.fluids.pipes.StraightPipeTileEntity.StraightPipeAttachmentBehaviour; +import com.simibubi.create.content.contraptions.fluids.pipes.StraightPipeTileEntity.StraightPipeFluidTransportBehaviour; import com.simibubi.create.foundation.tileEntity.SmartTileEntity; import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; import com.simibubi.create.foundation.tileEntity.behaviour.ValueBoxTransform; @@ -34,7 +33,6 @@ public class SmartFluidPipeTileEntity extends SmartTileEntity { @Override public void addBehaviours(List behaviours) { behaviours.add(new SmartPipeBehaviour(this)); - behaviours.add(new StraightPipeAttachmentBehaviour(this)); behaviours.add(filter = new FilteringBehaviour(this, new SmartPipeFilterSlot()).forFluids() .withCallback(this::onFilterChanged)); } @@ -45,21 +43,21 @@ public class SmartFluidPipeTileEntity extends SmartTileEntity { FluidPropagator.propagateChangedPipe(world, pos, getBlockState()); } - class SmartPipeBehaviour extends FluidPipeBehaviour { + class SmartPipeBehaviour extends StraightPipeFluidTransportBehaviour { public SmartPipeBehaviour(SmartTileEntity te) { super(te); } @Override - public boolean canTransferToward(FluidStack fluid, BlockState state, Direction direction, boolean inbound) { + public boolean canPullFluidFrom(FluidStack fluid, BlockState state, Direction direction) { if (fluid.isEmpty() || filter != null && filter.test(fluid)) - return super.canTransferToward(fluid, state, direction, inbound); + return super.canPullFluidFrom(fluid, state, direction); return false; } @Override - public boolean isConnectedTo(BlockState state, Direction direction) { + public boolean canHaveFlowToward(BlockState state, Direction direction) { return state.getBlock() instanceof SmartFluidPipeBlock && SmartFluidPipeBlock.getPipeAxis(state) == direction.getAxis(); } diff --git a/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/StraightPipeTileEntity.java b/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/StraightPipeTileEntity.java index ff405c1f8..c905dcfe9 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/StraightPipeTileEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/StraightPipeTileEntity.java @@ -2,8 +2,8 @@ package com.simibubi.create.content.contraptions.fluids.pipes; import java.util.List; -import com.simibubi.create.content.contraptions.fluids.FluidPipeAttachmentBehaviour; -import com.simibubi.create.content.contraptions.fluids.FluidPipeBehaviour; +import com.simibubi.create.content.contraptions.fluids.FluidTransportBehaviour; +import com.simibubi.create.content.contraptions.relays.elementary.BracketedTileEntityBehaviour; import com.simibubi.create.foundation.tileEntity.SmartTileEntity; import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; @@ -23,32 +23,25 @@ public class StraightPipeTileEntity extends SmartTileEntity { @Override public void addBehaviours(List behaviours) { - behaviours.add(new StraightPipeBehaviour(this)); - behaviours.add(new StraightPipeAttachmentBehaviour(this)); + behaviours.add(new StraightPipeFluidTransportBehaviour(this)); + behaviours.add(new BracketedTileEntityBehaviour(this)); } - class StraightPipeBehaviour extends FluidPipeBehaviour { + static class StraightPipeFluidTransportBehaviour extends FluidTransportBehaviour { - public StraightPipeBehaviour(SmartTileEntity te) { + public StraightPipeFluidTransportBehaviour(SmartTileEntity te) { super(te); } - + @Override - public boolean isConnectedTo(BlockState state, Direction direction) { + public boolean canHaveFlowToward(BlockState state, Direction direction) { return state.get(AxisPipeBlock.AXIS) == direction.getAxis(); } - } - - static class StraightPipeAttachmentBehaviour extends FluidPipeAttachmentBehaviour { - - public StraightPipeAttachmentBehaviour(SmartTileEntity te) { - super(te); - } - @Override - public AttachmentTypes getAttachment(ILightReader world, BlockPos pos, BlockState state, Direction direction) { - AttachmentTypes attachment = super.getAttachment(world, pos, state, direction); + public AttachmentTypes getRenderedRimAttachment(ILightReader world, BlockPos pos, BlockState state, + Direction direction) { + AttachmentTypes attachment = super.getRenderedRimAttachment(world, pos, state, direction); BlockState otherState = world.getBlockState(pos.offset(direction)); Axis axis = IAxisPipe.getAxisOf(state); @@ -57,7 +50,7 @@ public class StraightPipeTileEntity extends SmartTileEntity { if (axis == otherAxis && axis != null) if (state.getBlock() == otherState.getBlock() || direction.getAxisDirection() == AxisDirection.POSITIVE) return AttachmentTypes.NONE; - + if (otherState.getBlock() instanceof FluidValveBlock && FluidValveBlock.getPipeAxis(otherState) == direction.getAxis()) return AttachmentTypes.NONE; diff --git a/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/TransparentStraightPipeRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/TransparentStraightPipeRenderer.java index c5d446bb6..243cc52d7 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/TransparentStraightPipeRenderer.java +++ b/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/TransparentStraightPipeRenderer.java @@ -1,13 +1,13 @@ package com.simibubi.create.content.contraptions.fluids.pipes; import com.mojang.blaze3d.matrix.MatrixStack; -import com.simibubi.create.content.contraptions.fluids.FluidPipeBehaviour; +import com.simibubi.create.content.contraptions.fluids.FluidTransportBehaviour; +import com.simibubi.create.content.contraptions.fluids.PipeConnection.Flow; import com.simibubi.create.foundation.fluid.FluidRenderer; import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; import com.simibubi.create.foundation.tileEntity.renderer.SafeTileEntityRenderer; import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.LerpedFloat; -import com.simibubi.create.foundation.utility.Pair; import net.minecraft.client.renderer.IRenderTypeBuffer; import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher; @@ -23,32 +23,40 @@ public class TransparentStraightPipeRenderer extends SafeTileEntityRenderer strogestFlow = pipe.getStrogestFlow(side); - if (strogestFlow == null) + FluidStack fluidStack = flow.fluid; + if (fluidStack.isEmpty()) continue; - LerpedFloat second = strogestFlow.getSecond(); - if (second == null) + LerpedFloat progress = flow.progress; + if (progress == null) continue; - float value = second.getValue(partialTicks); - Boolean inbound = strogestFlow.getFirst(); - if (value == 1 && !inbound) { - FluidPipeBehaviour adjacent = TileEntityBehaviour.get(te.getWorld(), te.getPos() - .offset(side), FluidPipeBehaviour.TYPE); - - if (adjacent != null && adjacent.getFluid() - .isEmpty()) - value -= 1e-6f; + float value = progress.getValue(partialTicks); + boolean inbound = flow.inbound; + if (value == 1) { + if (inbound) { + Flow opposite = pipe.getFlow(side.getOpposite()); + if (opposite == null) + value -= 1e-6f; + } else { + FluidTransportBehaviour adjacent = TileEntityBehaviour.get(te.getWorld(), te.getPos() + .offset(side), FluidTransportBehaviour.TYPE); + if (adjacent == null) + value -= 1e-6f; + else { + Flow other = adjacent.getFlow(side.getOpposite()); + if (other == null || !other.inbound && !other.complete) + value -= 1e-6f; + } + } } FluidRenderer.renderFluidStream(fluidStack, side, 3 / 16f, value, inbound, buffer, ms, light); diff --git a/src/main/java/com/simibubi/create/content/contraptions/relays/elementary/BracketedTileEntityBehaviour.java b/src/main/java/com/simibubi/create/content/contraptions/relays/elementary/BracketedTileEntityBehaviour.java index 4ee03298d..6710dbb0b 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/relays/elementary/BracketedTileEntityBehaviour.java +++ b/src/main/java/com/simibubi/create/content/contraptions/relays/elementary/BracketedTileEntityBehaviour.java @@ -1,26 +1,37 @@ package com.simibubi.create.content.contraptions.relays.elementary; import java.util.Optional; +import java.util.function.Predicate; +import com.google.common.base.Predicates; import com.simibubi.create.foundation.tileEntity.SmartTileEntity; import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; import com.simibubi.create.foundation.tileEntity.behaviour.BehaviourType; import com.simibubi.create.foundation.utility.NBTHelper; +import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.block.Blocks; import net.minecraft.nbt.CompoundNBT; import net.minecraft.nbt.NBTUtil; +import net.minecraft.world.World; public class BracketedTileEntityBehaviour extends TileEntityBehaviour { - + public static BehaviourType TYPE = new BehaviourType<>(); private Optional bracket; private boolean reRender; - + + private Predicate pred; + public BracketedTileEntityBehaviour(SmartTileEntity te) { + this(te, Predicates.alwaysTrue()); + } + + public BracketedTileEntityBehaviour(SmartTileEntity te, Predicate pred) { super(te); + this.pred = pred; bracket = Optional.empty(); } @@ -28,23 +39,30 @@ public class BracketedTileEntityBehaviour extends TileEntityBehaviour { public BehaviourType getType() { return TYPE; } - + public void applyBracket(BlockState state) { this.bracket = Optional.of(state); reRender = true; tileEntity.notifyUpdate(); } - + public void removeBracket() { + World world = getWorld(); + if (!world.isRemote) + world.playEvent(2001, getPos(), Block.getStateId(getBracket())); this.bracket = Optional.empty(); reRender = true; tileEntity.notifyUpdate(); } + public boolean isBacketPresent() { + return getBracket() != Blocks.AIR.getDefaultState(); + } + public BlockState getBracket() { return bracket.orElse(Blocks.AIR.getDefaultState()); } - + @Override public void write(CompoundNBT nbt, boolean clientPacket) { bracket.ifPresent(p -> nbt.put("Bracket", NBTUtil.writeBlockState(p))); @@ -64,12 +82,9 @@ public class BracketedTileEntityBehaviour extends TileEntityBehaviour { getWorld().notifyBlockUpdate(getPos(), tileEntity.getBlockState(), tileEntity.getBlockState(), 16); super.read(nbt, clientPacket); } - + public boolean canHaveBracket() { - BlockState blockState = tileEntity.getBlockState(); - if (blockState.getBlock() instanceof AbstractShaftBlock) - return true; - return false; + return pred.test(tileEntity.getBlockState()); } } diff --git a/src/main/java/com/simibubi/create/content/contraptions/relays/elementary/SimpleKineticTileEntity.java b/src/main/java/com/simibubi/create/content/contraptions/relays/elementary/SimpleKineticTileEntity.java index fbe11f3bb..a7cda5e6c 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/relays/elementary/SimpleKineticTileEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/relays/elementary/SimpleKineticTileEntity.java @@ -9,20 +9,20 @@ import net.minecraft.tileentity.TileEntityType; import net.minecraft.util.math.AxisAlignedBB; public class SimpleKineticTileEntity extends KineticTileEntity { - + public SimpleKineticTileEntity(TileEntityType type) { super(type); } - + @Override public void addBehaviours(List behaviours) { - behaviours.add(new BracketedTileEntityBehaviour(this)); + behaviours.add(new BracketedTileEntityBehaviour(this, state -> state.getBlock() instanceof AbstractShaftBlock)); super.addBehaviours(behaviours); } - + @Override public AxisAlignedBB getRenderBoundingBox() { return new AxisAlignedBB(pos).grow(1); } - + } diff --git a/src/main/java/com/simibubi/create/foundation/fluid/FluidHelper.java b/src/main/java/com/simibubi/create/foundation/fluid/FluidHelper.java index 27f9e9f78..b5bdb822c 100644 --- a/src/main/java/com/simibubi/create/foundation/fluid/FluidHelper.java +++ b/src/main/java/com/simibubi/create/foundation/fluid/FluidHelper.java @@ -50,6 +50,14 @@ public class FluidHelper { BlockState blockState = fluid.getDefaultState().getBlockState(); return blockState != null && blockState != Blocks.AIR.getDefaultState(); } + + public static FluidStack copyStackWithAmount(FluidStack fs, int amount) { + if (fs.isEmpty()) + return FluidStack.EMPTY; + FluidStack copy = fs.copy(); + copy.setAmount(amount); + return copy; + } public static Fluid convertToFlowing(Fluid fluid) { if (fluid == Fluids.WATER) diff --git a/src/main/java/com/simibubi/create/foundation/utility/outliner/Outline.java b/src/main/java/com/simibubi/create/foundation/utility/outliner/Outline.java index d7ad3d190..c91099558 100644 --- a/src/main/java/com/simibubi/create/foundation/utility/outliner/Outline.java +++ b/src/main/java/com/simibubi/create/foundation/utility/outliner/Outline.java @@ -48,6 +48,10 @@ public abstract class Outline { } public void renderAACuboidLine(MatrixStack ms, SuperRenderTypeBuffer buffer, Vec3d start, Vec3d end) { + float lineWidth = params.getLineWidth(); + if (lineWidth == 0) + return; + IVertexBuilder builder = buffer.getBuffer(RenderTypes.getOutlineSolid()); Vec3d diff = end.subtract(start); @@ -58,7 +62,6 @@ public abstract class Outline { diff = diff.scale(-1); } - float lineWidth = params.getLineWidth(); Vec3d extension = diff.normalize() .scale(lineWidth / 2); Vec3d plane = VecHelper.axisAlingedPlaneOf(diff);