-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
365 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
7 changes: 7 additions & 0 deletions
7
src/main/generated/assets/oritech/blockstates/pump_block.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"variants": { | ||
"": { | ||
"model": "oritech:block/pump_block" | ||
} | ||
} | ||
} |
6 changes: 6 additions & 0 deletions
6
src/main/generated/assets/oritech/models/block/pump_block.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"parent": "minecraft:block/cube_all", | ||
"textures": { | ||
"all": "oritech:block/pump_block" | ||
} | ||
} |
3 changes: 3 additions & 0 deletions
3
src/main/generated/assets/oritech/models/item/pump_block.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"parent": "oritech:block/pump_block" | ||
} |
51 changes: 51 additions & 0 deletions
51
src/main/java/rearth/oritech/block/blocks/machines/interaction/PumpBlock.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package rearth.oritech.block.blocks.machines.interaction; | ||
|
||
import net.minecraft.block.Block; | ||
import net.minecraft.block.BlockEntityProvider; | ||
import net.minecraft.block.BlockRenderType; | ||
import net.minecraft.block.BlockState; | ||
import net.minecraft.block.entity.BlockEntity; | ||
import net.minecraft.block.entity.BlockEntityTicker; | ||
import net.minecraft.block.entity.BlockEntityType; | ||
import net.minecraft.entity.player.PlayerEntity; | ||
import net.minecraft.state.StateManager; | ||
import net.minecraft.state.property.BooleanProperty; | ||
import net.minecraft.text.Text; | ||
import net.minecraft.util.ActionResult; | ||
import net.minecraft.util.Hand; | ||
import net.minecraft.util.hit.BlockHitResult; | ||
import net.minecraft.util.math.BlockPos; | ||
import net.minecraft.world.World; | ||
import org.jetbrains.annotations.Nullable; | ||
import rearth.oritech.block.entity.machines.interaction.LaserArmBlockEntity; | ||
import rearth.oritech.block.entity.machines.interaction.PumpBlockEntity; | ||
import rearth.oritech.network.NetworkContent; | ||
import rearth.oritech.util.MultiblockMachineController; | ||
|
||
public class PumpBlock extends Block implements BlockEntityProvider { | ||
|
||
public PumpBlock(Settings settings) { | ||
super(settings); | ||
} | ||
|
||
// @Override | ||
// public BlockRenderType getRenderType(BlockState state) { | ||
// return BlockRenderType.ENTITYBLOCK_ANIMATED; | ||
// } | ||
|
||
@Nullable | ||
@Override | ||
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) { | ||
return new PumpBlockEntity(pos, state); | ||
} | ||
|
||
@SuppressWarnings({"rawtypes", "unchecked"}) | ||
@Nullable | ||
@Override | ||
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(World world, BlockState state, BlockEntityType<T> type) { | ||
return (world1, pos, state1, blockEntity) -> { | ||
if (blockEntity instanceof BlockEntityTicker ticker) | ||
ticker.tick(world1, pos, state1, blockEntity); | ||
}; | ||
} | ||
} |
265 changes: 265 additions & 0 deletions
265
src/main/java/rearth/oritech/block/entity/machines/interaction/PumpBlockEntity.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,265 @@ | ||
package rearth.oritech.block.entity.machines.interaction; | ||
|
||
import net.fabricmc.fabric.api.transfer.v1.fluid.FluidConstants; | ||
import net.fabricmc.fabric.api.transfer.v1.fluid.FluidStorage; | ||
import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant; | ||
import net.fabricmc.fabric.api.transfer.v1.item.ItemStorage; | ||
import net.fabricmc.fabric.api.transfer.v1.storage.Storage; | ||
import net.fabricmc.fabric.api.transfer.v1.storage.base.SingleVariantStorage; | ||
import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction; | ||
import net.minecraft.block.BlockState; | ||
import net.minecraft.block.Blocks; | ||
import net.minecraft.block.entity.BlockEntity; | ||
import net.minecraft.block.entity.BlockEntityTicker; | ||
import net.minecraft.fluid.FluidState; | ||
import net.minecraft.fluid.Fluids; | ||
import net.minecraft.nbt.NbtCompound; | ||
import net.minecraft.util.math.BlockPos; | ||
import net.minecraft.util.math.Direction; | ||
import net.minecraft.world.World; | ||
import rearth.oritech.init.BlockContent; | ||
import rearth.oritech.init.BlockEntitiesContent; | ||
import rearth.oritech.util.FluidProvider; | ||
|
||
import java.util.*; | ||
import java.util.stream.Collectors; | ||
|
||
// todo add energy storage / usage | ||
public class PumpBlockEntity extends BlockEntity implements BlockEntityTicker<PumpBlockEntity>, FluidProvider { | ||
|
||
private static final int MAX_SEARCH_COUNT = 10000; | ||
|
||
private boolean initialized = false; | ||
private boolean toolheadLowered = false; | ||
private boolean searchActive = false; | ||
private BlockPos toolheadPosition; | ||
private FloodFillSearch searchInstance; | ||
private Deque<BlockPos> pendingLiquidPositions; | ||
|
||
private final SingleVariantStorage<FluidVariant> fluidStorage = new SingleVariantStorage<>() { | ||
@Override | ||
protected FluidVariant getBlankVariant() { | ||
return FluidVariant.blank(); | ||
} | ||
|
||
@Override | ||
protected long getCapacity(FluidVariant variant) { | ||
return (16 * FluidConstants.BUCKET); | ||
} | ||
|
||
@Override | ||
protected void onFinalCommit() { | ||
super.onFinalCommit(); | ||
PumpBlockEntity.this.markDirty(); | ||
} | ||
}; | ||
|
||
public PumpBlockEntity(BlockPos pos, BlockState state) { | ||
super(BlockEntitiesContent.PUMP_BLOCK, pos, state); | ||
} | ||
|
||
@Override | ||
protected void writeNbt(NbtCompound nbt) { | ||
super.writeNbt(nbt); | ||
nbt.putBoolean("initialized", initialized); | ||
nbt.put("fluidVariant", fluidStorage.variant.toNbt()); | ||
nbt.putLong("amount", fluidStorage.amount); | ||
nbt.putLongArray("pendingTargets", pendingLiquidPositions.stream().mapToLong(BlockPos::asLong).toArray()); | ||
} | ||
|
||
@Override | ||
public void readNbt(NbtCompound nbt) { | ||
super.readNbt(nbt); | ||
initialized = nbt.getBoolean("initialized"); | ||
fluidStorage.variant = FluidVariant.fromNbt(nbt.getCompound("fluidVariant")); | ||
fluidStorage.amount = nbt.getLong("amount"); | ||
pendingLiquidPositions = Arrays.stream(nbt.getLongArray("pendingTargets")).mapToObj(BlockPos::fromLong).collect(Collectors.toCollection(ArrayDeque::new)); | ||
} | ||
|
||
@Override | ||
public void tick(World world, BlockPos pos, BlockState state, PumpBlockEntity blockEntity) { | ||
if (world.isClient) return; | ||
|
||
if (!initialized) { | ||
progressStartup(); | ||
return; | ||
} | ||
|
||
if (world.getTime() % 10 == 0) { | ||
|
||
if (pendingLiquidPositions.isEmpty() || tankIsFull()) return; | ||
|
||
var targetBlock = pendingLiquidPositions.peekLast(); | ||
|
||
if (!world.getBlockState(targetBlock).isLiquid()) { | ||
pendingLiquidPositions.pollLast(); | ||
return; | ||
} | ||
|
||
var targetState = world.getFluidState(targetBlock); | ||
if (!targetState.getFluid().matchesType(Fluids.WATER)) { | ||
drainSourceBlock(targetBlock); | ||
pendingLiquidPositions.pollLast(); | ||
System.out.println("pumped and removed block: " + targetState.getFluid()); | ||
} | ||
|
||
addLiquidToTank(targetState); | ||
this.markDirty(); | ||
} | ||
|
||
} | ||
|
||
private boolean tankIsFull() { | ||
return fluidStorage.amount > fluidStorage.getCapacity() - FluidConstants.BUCKET; | ||
} | ||
|
||
private void addLiquidToTank(FluidState targetState) { | ||
try (var tx = Transaction.openOuter()) { | ||
var amountInserted = fluidStorage.insert(FluidVariant.of(targetState.getFluid()), FluidConstants.BUCKET, tx); | ||
tx.commit(); | ||
} | ||
} | ||
|
||
private void drainSourceBlock(BlockPos targetBlock) { | ||
world.setBlockState(targetBlock, Blocks.AIR.getDefaultState()); | ||
} | ||
|
||
private void progressStartup() { | ||
|
||
// startup sequence is: | ||
// move down until no longer in air | ||
// check if target is liquid | ||
// if liquid is water, consider as infinite | ||
// if liquid, start flood fill to find all liquid blocks. Add all found blocks to queue so that it can be soaked up in reverse | ||
// search all neighbors per tick | ||
// if more than 10000 blocks are found, consider as infinite and stop search | ||
// mark startup as completed | ||
|
||
if (toolheadPosition == null) { | ||
toolheadPosition = pos; | ||
} | ||
|
||
if (!toolheadLowered) { | ||
|
||
if (world.getTime() % 10 != 0) | ||
moveToolheadDown(); | ||
|
||
return; | ||
} | ||
|
||
if (searchActive) { | ||
if (searchInstance.nextGeneration()) { | ||
finishSearch(); | ||
searchActive = false; | ||
} | ||
} | ||
} | ||
|
||
private void moveToolheadDown() { | ||
toolheadLowered = checkToolheadEnd(toolheadPosition); | ||
if (toolheadLowered) { | ||
startLiquidSearch(toolheadPosition.down()); | ||
return; | ||
} | ||
|
||
toolheadPosition = toolheadPosition.down(); | ||
world.setBlockState(toolheadPosition, BlockContent.BANANA_BLOCK.getDefaultState()); | ||
} | ||
|
||
private boolean checkToolheadEnd(BlockPos newPosition) { | ||
|
||
var posBelow = newPosition.down(); | ||
var stateBelow = world.getBlockState(posBelow); | ||
var blockBelow = stateBelow.getBlock(); | ||
|
||
return !(blockBelow.equals(Blocks.AIR) || blockBelow.equals(BlockContent.BANANA_BLOCK)); | ||
} | ||
|
||
private void startLiquidSearch(BlockPos start) { | ||
|
||
var state = world.getFluidState(start); | ||
if (!state.isStill()) return; | ||
|
||
searchInstance = new FloodFillSearch(start, world); | ||
searchActive = true; | ||
|
||
System.out.println("starting search at: " + start + " " + state.getFluid() + " " + state.isStill()); | ||
} | ||
|
||
private void finishSearch() { | ||
System.out.println("search finished, found: " + searchInstance.foundTargets.size()); | ||
pendingLiquidPositions = searchInstance.foundTargets; | ||
initialized = true; | ||
searchInstance = null; | ||
} | ||
|
||
@Override | ||
public Storage<FluidVariant> getFluidStorage(Direction direction) { | ||
return fluidStorage; | ||
} | ||
|
||
private static class FloodFillSearch { | ||
|
||
final HashSet<BlockPos> checkedPositions = new HashSet<>(); | ||
final HashSet<BlockPos> nextTargets = new HashSet<>(); | ||
final Deque<BlockPos> foundTargets = new ArrayDeque<>(); | ||
final World world; | ||
|
||
public FloodFillSearch(BlockPos startPosition, World world) { | ||
this.world = world; | ||
nextTargets.add(startPosition); | ||
} | ||
|
||
// returns true when done | ||
@SuppressWarnings("unchecked") | ||
public boolean nextGeneration() { | ||
|
||
var currentGeneration = (HashSet<BlockPos>) nextTargets.clone(); | ||
|
||
var earlyStop = false; | ||
|
||
for (var target : currentGeneration) { | ||
if (isValidTarget(target)) { | ||
foundTargets.addLast(target); | ||
addNeighborsToQueue(target); | ||
if (checkForEarlyStop(target)) earlyStop = true; | ||
} | ||
|
||
checkedPositions.add(target); | ||
nextTargets.remove(target); | ||
} | ||
|
||
if (cutoffSearch() || earlyStop) nextTargets.clear(); | ||
|
||
return nextTargets.isEmpty(); | ||
} | ||
|
||
private boolean checkForEarlyStop(BlockPos target) { | ||
return world.getFluidState(target).getFluid().matchesType(Fluids.WATER); | ||
} | ||
|
||
private boolean cutoffSearch() { | ||
return foundTargets.size() >= MAX_SEARCH_COUNT; | ||
} | ||
|
||
private boolean isValidTarget(BlockPos target) { | ||
var state = world.getFluidState(target); | ||
return state.isStill(); | ||
} | ||
|
||
private void addNeighborsToQueue(BlockPos self) { | ||
|
||
for (var neighbor : getNeighbors(self)) { | ||
if (checkedPositions.contains(neighbor)) continue; | ||
nextTargets.add(neighbor); | ||
} | ||
|
||
} | ||
|
||
// returns all neighboring positions except up | ||
private List<BlockPos> getNeighbors(BlockPos pos) { | ||
return List.of(pos.down(), pos.north(), pos.east(), pos.south(), pos.west()); | ||
} | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.