Skip to content

Commit

Permalink
Improve behavior of tanks and drains
Browse files Browse the repository at this point in the history
Fixes issues with stacked containers (#4342) and hopefully speed (#4361)
  • Loading branch information
KnightMiner committed Apr 25, 2021
1 parent a963cc0 commit 9d951ab
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 109 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package slimeknights.tconstruct.library.fluid;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.fluid.Fluid;
import net.minecraft.fluid.Fluids;
import net.minecraft.item.BucketItem;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.DrinkHelper;
import net.minecraft.util.Hand;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.world.World;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fluids.FluidAttributes;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction;
import net.minecraftforge.fluids.capability.templates.EmptyFluidHandler;
import net.minecraftforge.items.ItemHandlerHelper;
import slimeknights.tconstruct.TConstruct;

/**
* Alternative to {@link net.minecraftforge.fluids.FluidUtil} since no one has time to make the forge util not a buggy mess
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class FluidTransferUtil {
public static boolean tryTransfer(IFluidHandler input, IFluidHandler output, int maxFill) {
// first, figure out how much we can drain
FluidStack simulated = input.drain(maxFill, FluidAction.SIMULATE);
if (!simulated.isEmpty()) {
// next, find out how much we can fill
int simulatedFill = output.fill(simulated, FluidAction.SIMULATE);
if (simulatedFill > 0) {
// actually drain
FluidStack drainedFluid = input.drain(simulatedFill, FluidAction.EXECUTE);
if (!drainedFluid.isEmpty()) {
// acutally fill
int actualFill = output.fill(drainedFluid, FluidAction.EXECUTE);
if (actualFill != drainedFluid.getAmount()) {
TConstruct.log.error("Lost {} fluid during transfer", drainedFluid.getAmount() - actualFill);
}
}
return true;
}
}
return false;
}

/**
* Attempts to interact with a flilled bucket on a fluid tank. This is unique as it handles fish buckets, which don't expose fluid capabilities
* @param world World instance
* @param pos Block position
* @param player Player
* @param hand Hand
* @param hit Hit side
* @param offset Direction to place fish
* @return True if using a bucket
*/
public static boolean interactWithBucket(World world, BlockPos pos, PlayerEntity player, Hand hand, Direction hit, Direction offset) {
ItemStack held = player.getHeldItem(hand);
if (held.getItem() instanceof BucketItem) {
BucketItem bucket = (BucketItem) held.getItem();
Fluid fluid = bucket.getFluid();
if (fluid != Fluids.EMPTY) {
if (!world.isRemote) {
TileEntity te = world.getTileEntity(pos);
if (te != null) {
te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, hit)
.ifPresent(handler -> {
FluidStack fluidStack = new FluidStack(bucket.getFluid(), FluidAttributes.BUCKET_VOLUME);
// must empty the whole bucket
if (handler.fill(fluidStack, FluidAction.SIMULATE) == FluidAttributes.BUCKET_VOLUME) {
handler.fill(fluidStack, FluidAction.EXECUTE);
bucket.onLiquidPlaced(world, held, pos.offset(offset));
world.playSound(null, pos, fluid.getAttributes().getEmptySound(), SoundCategory.BLOCKS, 1.0F, 1.0F);
if (!player.isCreative()) {
player.setHeldItem(hand, held.getContainerItem());
}
}
});
}
}
return true;
}
}
return false;
}

/**
* Base logic to interact with a tank
* @param world World instance
* @param pos Tank position
* @param player Player instance
* @param hand Hand used
* @param hit Hit position
* @return True if further interactions should be blocked, false otherwise
*/
public static boolean interactWithFluidItem(World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult hit) {
// success if the item is a fluid handler, regardless of if fluid moved
ItemStack stack = player.getHeldItem(hand);
Direction face = hit.getFace();
// fetch capability before copying, bit more work when its a fluid handler, but saves copying time when its not
if (stack.getCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY).isPresent()) {
TileEntity te = world.getTileEntity(pos);
if (te != null) {
LazyOptional<IFluidHandler> teCapability = te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, face);
if (teCapability.isPresent()) {
IFluidHandler teHandler = teCapability.orElse(EmptyFluidHandler.INSTANCE);
ItemStack copy = ItemHandlerHelper.copyStackWithSize(stack, 1);
copy.getCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY).ifPresent(itemHandler -> {
// first, try filling the TE from the item
boolean isSuccess = false;
if (tryTransfer(teHandler, itemHandler, Integer.MAX_VALUE)) {
isSuccess = true;
// if that failed, try filling the item handler from the TE
} else if (tryTransfer(itemHandler, teHandler, Integer.MAX_VALUE)) {
isSuccess = true;
}
// if either worked, update the player's inventory
if (isSuccess) {
player.setHeldItem(hand, DrinkHelper.fill(stack, player, itemHandler.getContainer()));
}
});
}
}
return true;
}
return false;
}

/**
* Utility to try fluid item then bucket
* @param world World instance
* @param pos Tank position
* @param player Player instance
* @param hand Hand used
* @param hit Hit position
* @return True if interacted
*/
public static boolean interactWithTank(World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult hit) {
return interactWithFluidItem(world, pos, player, hand, hit)
|| interactWithBucket(world, pos, player, hand, hit.getFace(), hit.getFace());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import slimeknights.tconstruct.common.TinkerTags;
import slimeknights.tconstruct.library.fluid.FluidTransferUtil;
import slimeknights.tconstruct.smeltery.tileentity.ITankTileEntity;
import slimeknights.tconstruct.smeltery.tileentity.MelterTileEntity;

Expand Down Expand Up @@ -101,7 +102,7 @@ public TileEntity createTileEntity(BlockState blockState, IBlockReader iBlockRea
@Deprecated
@Override
public ActionResultType onBlockActivated(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult hit) {
if (ITankTileEntity.interactWithTank(world, pos, player, hand, hit)) {
if (FluidTransferUtil.interactWithTank(world, pos, player, hand, hit)) {
return ActionResultType.SUCCESS;
}
return super.onBlockActivated(state, world, pos, player, hand, hit);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,13 @@

import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.Direction;
import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.world.World;
import net.minecraftforge.fluids.FluidActionResult;
import net.minecraftforge.fluids.FluidUtil;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.items.CapabilityItemHandler;
import slimeknights.tconstruct.library.fluid.FluidTransferUtil;
import slimeknights.tconstruct.smeltery.tileentity.DrainTileEntity;
import slimeknights.tconstruct.smeltery.tileentity.ITankTileEntity;

/** Extenson to include interaction behavior */
public class SearedDrainBlock extends OrientableSmelteryBlock {
Expand All @@ -27,26 +20,9 @@ public SearedDrainBlock(Properties properties) {
@Deprecated
@Override
public ActionResultType onBlockActivated(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult hit) {
// success if the item is a fluid handler, regardless of if fluid moved
ItemStack held = player.getHeldItem(hand);
Direction face = hit.getFace();
if (FluidUtil.getFluidHandler(held).isPresent()) {
if (!world.isRemote()) {
// find the player inventory and the tank fluid handler and interact
TileEntity te = world.getTileEntity(pos);
if (te != null) {
te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, face)
.ifPresent(handler -> player.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY)
.ifPresent(inv -> {
FluidActionResult result = FluidUtil.tryEmptyContainerAndStow(held, handler, inv, Integer.MAX_VALUE, player, true);
if (result.isSuccess()) {
player.setHeldItem(hand, result.getResult());
}
}));
}
}
if (FluidTransferUtil.interactWithFluidItem(world, pos, player, hand, hit)) {
return ActionResultType.SUCCESS;
} else if (ITankTileEntity.interactWithBucket(world, pos, player, hand, face, state.get(FACING).getOpposite())) {
} else if (FluidTransferUtil.interactWithBucket(world, pos, player, hand, hit.getFace(), state.get(FACING).getOpposite())) {
return ActionResultType.SUCCESS;
}
return ActionResultType.PASS;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fluids.FluidStack;
import slimeknights.mantle.util.TileEntityHelper;
import slimeknights.tconstruct.library.fluid.FluidTransferUtil;
import slimeknights.tconstruct.library.materials.MaterialValues;
import slimeknights.tconstruct.library.utils.Tags;
import slimeknights.tconstruct.smeltery.tileentity.ITankTileEntity;
Expand Down Expand Up @@ -55,7 +56,7 @@ public TileEntity createTileEntity(BlockState state, IBlockReader worldIn) {
@Deprecated
@Override
public ActionResultType onBlockActivated(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult hit) {
if (ITankTileEntity.interactWithTank(world, pos, player, hand, hit)) {
if (FluidTransferUtil.interactWithTank(world, pos, player, hand, hit)) {
return ActionResultType.SUCCESS;
}
return super.onBlockActivated(state, world, pos, player, hand, hit);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,12 @@

import net.minecraft.block.BlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.fluid.Fluid;
import net.minecraft.fluid.Fluids;
import net.minecraft.item.BucketItem;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.Hand;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.world.IWorld;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fluids.FluidAttributes;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.FluidUtil;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction;
import net.minecraftforge.fml.DistExecutor;
import slimeknights.mantle.client.model.util.ModelHelper;
import slimeknights.tconstruct.common.config.Config;
Expand Down Expand Up @@ -117,73 +104,6 @@ default TileEntity getTE() {
* Helpers
*/

/**
* Attempts to interact with a flilled bucket on a fluid tank. This is unique as it handles fish buckets, which don't expose fluid capabilities
* @param world World instance
* @param pos Block position
* @param player Player
* @param hand Hand
* @param hit Hit side
* @param offset Direction to place fish
* @return True if using a bucket
*/
static boolean interactWithBucket(World world, BlockPos pos, PlayerEntity player, Hand hand, Direction hit, Direction offset) {
ItemStack held = player.getHeldItem(hand);
if (held.getItem() instanceof BucketItem) {
BucketItem bucket = (BucketItem) held.getItem();
Fluid fluid = bucket.getFluid();
if (fluid != Fluids.EMPTY) {
if (!world.isRemote) {
TileEntity te = world.getTileEntity(pos);
if (te != null) {
te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, hit)
.ifPresent(handler -> {
FluidStack fluidStack = new FluidStack(bucket.getFluid(), FluidAttributes.BUCKET_VOLUME);
// must empty the whole bucket
if (handler.fill(fluidStack, FluidAction.SIMULATE) == FluidAttributes.BUCKET_VOLUME) {
handler.fill(fluidStack, FluidAction.EXECUTE);
bucket.onLiquidPlaced(world, held, pos.offset(offset));
world.playSound(null, pos, fluid.getAttributes().getEmptySound(), SoundCategory.BLOCKS, 1.0F, 1.0F);
if (!player.isCreative()) {
player.setHeldItem(hand, held.getContainerItem());
}
}
});
}
}
return true;
}
}
return false;
}

/**
* Base logic to interact with a tank
* @param world World instance
* @param pos Tank position
* @param player Player instance
* @param hand Hand used
* @param hit Hit position
* @return True if further interactions should be blocked, false otherwise
*/
static boolean interactWithTank(World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult hit) {
// success if the item is a fluid handler, regardless of if fluid moved
ItemStack stack = player.getHeldItem(hand);
Direction face = hit.getFace();
if (FluidUtil.getFluidHandler(stack).isPresent()) {
if (!world.isRemote()) {
TileEntity te = world.getTileEntity(pos);
if (te != null) {
te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, face)
.ifPresent(handler -> FluidUtil.interactWithFluidHandler(player, hand, handler));
}
}
return true;
}
// fall back to buckets for fish buckets
return interactWithBucket(world, pos, player, hand, face, face);
}

/**
* Implements logic for {@link net.minecraft.block.Block#getComparatorInputOverride(BlockState, World, BlockPos)}
* @param world World instance
Expand Down

0 comments on commit 9d951ab

Please sign in to comment.