Fix a few FluidUtil issues that were causing DispenseFluidContainer to not function properly (#7422)

This commit is contained in:
Sara Freimer authored and GitHub committed 2021-02-15 17:08:05 -08:00
1 parent 0340664dbf
commit d97f28141c
6 files changed
+353 -20

No files matched your search

@@ -24,6 +24,8 @@ import javax.annotation.Nullable;
import com.google.common.base.Preconditions;
import net.minecraft.block.Block;
import net.minecraft.block.IBucketPickupHandler;
import net.minecraft.block.ILiquidContainer;
import net.minecraft.block.material.Material;
import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity;
@@ -31,7 +33,6 @@ import net.minecraft.fluid.Fluid;
import net.minecraft.fluid.Fluids;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ItemUseContext;
import net.minecraft.item.Items;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.*;
@@ -44,6 +45,8 @@ import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandlerItem;
import net.minecraftforge.fluids.capability.wrappers.BlockWrapper;
import net.minecraftforge.fluids.capability.wrappers.BucketPickupHandlerWrapper;
import net.minecraftforge.fluids.capability.wrappers.FluidBlockWrapper;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.ItemHandlerHelper;
@@ -435,8 +438,12 @@ public class FluidUtil
if (!container.isEmpty())
{
container = ItemHandlerHelper.copyStackWithSize(container, 1);
return getFluidHandler(container)
Optional<FluidStack> fluidContained = getFluidHandler(container)
.map(handler -> handler.drain(Integer.MAX_VALUE, IFluidHandler.FluidAction.SIMULATE));
if (fluidContained.isPresent() && !fluidContained.get().isEmpty())
{
return fluidContained;
}
}
return Optional.empty();
}
@@ -481,20 +488,29 @@ public class FluidUtil
BlockState state = worldIn.getBlockState(pos);
Block block = state.getBlock();
IFluidHandler targetFluidHandler;
if (block instanceof IFluidBlock)
{
IFluidHandler targetFluidHandler = getFluidHandler(worldIn, pos, side).orElse(null);
if (targetFluidHandler != null)
{
return tryFillContainer(emptyContainer, targetFluidHandler, Integer.MAX_VALUE, playerIn, true);
}
targetFluidHandler = new FluidBlockWrapper((IFluidBlock) block, worldIn, pos);
}
return FluidActionResult.FAILURE;
else if (block instanceof IBucketPickupHandler)
{
targetFluidHandler = new BucketPickupHandlerWrapper((IBucketPickupHandler) block, worldIn, pos);
}
else
{
Optional<IFluidHandler> fluidHandler = getFluidHandler(worldIn, pos, side).resolve();
if (!fluidHandler.isPresent())
{
return FluidActionResult.FAILURE;
}
targetFluidHandler = fluidHandler.get();
}
return tryFillContainer(emptyContainer, targetFluidHandler, Integer.MAX_VALUE, playerIn, true);
}
/**
* ItemStack version of {@link #tryPlaceFluid(PlayerEntity, World, BlockPos, IFluidHandler, FluidStack)}.
* ItemStack version of {@link #tryPlaceFluid(PlayerEntity, World, Hand, BlockPos, IFluidHandler, FluidStack)}.
* Use the returned {@link FluidActionResult} to update the container ItemStack.
*
* @param player Player who places the fluid. May be null for blocks like dispensers.
@@ -522,7 +538,7 @@ public class FluidUtil
* Honors the amount of fluid contained by the used container.
* Checks if water-like fluids should vaporize like in the nether.
*
* Modeled after {@link ItemBucket#tryPlaceContainedLiquid(PlayerEntity, World, BlockPos)}
* Modeled after {@link net.minecraft.item.BucketItem#tryPlaceContainedLiquid(PlayerEntity, World, BlockPos, BlockRayTraceResult)}
*
* @param player Player who places the fluid. May be null for blocks like dispensers.
* @param world World to place the fluid in
@@ -540,7 +556,7 @@ public class FluidUtil
}
Fluid fluid = resource.getFluid();
if (fluid == null || !fluid.getAttributes().canBePlacedInWorld(world, pos, resource))
if (fluid == Fluids.EMPTY || !fluid.getAttributes().canBePlacedInWorld(world, pos, resource))
{
return false;
}
@@ -550,14 +566,15 @@ public class FluidUtil
return false;
}
BlockItemUseContext context = new BlockItemUseContext(new ItemUseContext(player, hand, new BlockRayTraceResult(Vector3d.ZERO, Direction.UP, pos, false))); //TODO: This neds proper context...
BlockItemUseContext context = new BlockItemUseContext(world, player, hand, player == null ? ItemStack.EMPTY : player.getHeldItem(hand), new BlockRayTraceResult(Vector3d.ZERO, Direction.UP, pos, false));
// check that we can place the fluid at the destination
BlockState destBlockState = world.getBlockState(pos);
Material destMaterial = destBlockState.getMaterial();
boolean isDestNonSolid = !destMaterial.isSolid();
boolean isDestReplaceable = destBlockState.isReplaceable(context);
if (!world.isAirBlock(pos) && !isDestNonSolid && !isDestReplaceable)
boolean canDestContainFluid = destBlockState.getBlock() instanceof ILiquidContainer && ((ILiquidContainer) destBlockState.getBlock()).canContainFluid(world, pos, destBlockState, fluid);
if (!world.isAirBlock(pos) && !isDestNonSolid && !isDestReplaceable && !canDestContainFluid)
{
return false; // Non-air, solid, unreplacable block. We can't put fluid here.
}
@@ -574,7 +591,15 @@ public class FluidUtil
else
{
// This fluid handler places the fluid block when filled
IFluidHandler handler = getFluidBlockHandler(fluid, world, pos);
IFluidHandler handler;
if (canDestContainFluid)
{
handler = new BlockWrapper.LiquidContainerBlockWrapper((ILiquidContainer) destBlockState.getBlock(), world, pos);
}
else
{
handler = getFluidBlockHandler(fluid, world, pos);
}
FluidStack result = tryFluidTransfer(handler, fluidSource, resource, true);
if (!result.isEmpty())
{
@@ -589,8 +614,8 @@ public class FluidUtil
/**
* Internal method for getting a fluid block handler for placing a fluid.
*
* Modders: Instead of this method, use {@link #tryPlaceFluid(PlayerEntity, World, BlockPos, ItemStack, FluidStack)}
* or {@link #tryPlaceFluid(PlayerEntity, World, BlockPos, IFluidHandler, FluidStack)}
* Modders: Instead of this method, use {@link #tryPlaceFluid(PlayerEntity, World, Hand, BlockPos, ItemStack, FluidStack)}
* or {@link #tryPlaceFluid(PlayerEntity, World, Hand, BlockPos, IFluidHandler, FluidStack)}
*/
private static IFluidHandler getFluidBlockHandler(Fluid fluid, World world, BlockPos pos)
{
@@ -600,9 +625,9 @@ public class FluidUtil
/**
* Destroys a block when a fluid is placed in the same position.
* Modeled after {@link ItemBucket#tryPlaceContainedLiquid(PlayerEntity, World, BlockPos)}
* Modeled after {@link net.minecraft.item.BucketItem#tryPlaceContainedLiquid(PlayerEntity, World, BlockPos, BlockRayTraceResult)}
*
* This is a helper method for implementing {@link IFluidBlock#place(World, BlockPos, FluidStack, boolean)}.
* This is a helper method for implementing {@link IFluidBlock#place(World, BlockPos, FluidStack, IFluidHandler.FluidAction)}.
*
* @param world the world that the fluid will be placed in
* @param pos the location that the fluid will be placed
@@ -19,8 +19,8 @@
package net.minecraftforge.fluids.capability.wrappers;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.ILiquidContainer;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.common.util.Constants;
@@ -62,4 +62,37 @@ public class BlockWrapper extends VoidFluidHandler
}
return FluidAttributes.BUCKET_VOLUME;
}
public static class LiquidContainerBlockWrapper extends VoidFluidHandler
{
protected final ILiquidContainer liquidContainer;
protected final World world;
protected final BlockPos blockPos;
public LiquidContainerBlockWrapper(ILiquidContainer liquidContainer, World world, BlockPos blockPos)
{
this.liquidContainer = liquidContainer;
this.world = world;
this.blockPos = blockPos;
}
@Override
public int fill(FluidStack resource, FluidAction action)
{
// NOTE: "Filling" means placement in this context!
if (resource.getAmount() >= FluidAttributes.BUCKET_VOLUME)
{
BlockState state = world.getBlockState(blockPos);
if (liquidContainer.canContainFluid(world, blockPos, state, resource.getFluid()))
{
//If we are executing try to actually fill the container, if it failed return that we failed
if (action.simulate() || liquidContainer.receiveFluid(world, blockPos, state, resource.getFluid().getAttributes().getStateForPlacement(world, blockPos, resource)))
{
return FluidAttributes.BUCKET_VOLUME;
}
}
}
return 0;
}
}
}
@@ -0,0 +1,151 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2020.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.fluids.capability.wrappers;
import javax.annotation.Nonnull;
import net.minecraft.block.IBucketPickupHandler;
import net.minecraft.fluid.Fluid;
import net.minecraft.fluid.FluidState;
import net.minecraft.fluid.Fluids;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.fluids.FluidAttributes;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.IFluidHandler;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class BucketPickupHandlerWrapper implements IFluidHandler
{
private static final Logger LOGGER = LogManager.getLogger();
protected final IBucketPickupHandler bucketPickupHandler;
protected final World world;
protected final BlockPos blockPos;
public BucketPickupHandlerWrapper(IBucketPickupHandler bucketPickupHandler, World world, BlockPos blockPos)
{
this.bucketPickupHandler = bucketPickupHandler;
this.world = world;
this.blockPos = blockPos;
}
@Override
public int getTanks()
{
return 1;
}
@Nonnull
@Override
public FluidStack getFluidInTank(int tank)
{
if (tank == 0)
{
//Best guess at stored fluid
FluidState fluidState = world.getFluidState(blockPos);
if (!fluidState.isEmpty())
{
return new FluidStack(fluidState.getFluid(), FluidAttributes.BUCKET_VOLUME);
}
}
return FluidStack.EMPTY;
}
@Override
public int getTankCapacity(int tank)
{
return FluidAttributes.BUCKET_VOLUME;
}
@Override
public boolean isFluidValid(int tank, @Nonnull FluidStack stack)
{
return true;
}
@Override
public int fill(FluidStack resource, FluidAction action)
{
return 0;
}
@Nonnull
@Override
public FluidStack drain(FluidStack resource, FluidAction action)
{
if (!resource.isEmpty() && FluidAttributes.BUCKET_VOLUME <= resource.getAmount())
{
FluidState fluidState = world.getFluidState(blockPos);
if (!fluidState.isEmpty() && resource.getFluid() == fluidState.getFluid())
{
if (action.execute())
{
Fluid fluid = bucketPickupHandler.pickupFluid(world, blockPos, world.getBlockState(blockPos));
if (fluid != Fluids.EMPTY)
{
FluidStack extracted = new FluidStack(fluid, FluidAttributes.BUCKET_VOLUME);
if (!resource.isFluidEqual(extracted))
{
//Be loud if something went wrong
LOGGER.error("Fluid removed without successfully being picked up. Fluid {} at {} in {} matched requested type, but after performing pickup was {}.",
fluidState.getFluid().getRegistryName(), blockPos, world.getDimensionKey().getLocation(), fluid.getRegistryName());
return FluidStack.EMPTY;
}
return extracted;
}
}
else
{
FluidStack extracted = new FluidStack(fluidState.getFluid(), FluidAttributes.BUCKET_VOLUME);
if (resource.isFluidEqual(extracted))
{
//Validate NBT matches
return extracted;
}
}
}
}
return FluidStack.EMPTY;
}
@Nonnull
@Override
public FluidStack drain(int maxDrain, FluidAction action)
{
if (FluidAttributes.BUCKET_VOLUME <= maxDrain)
{
FluidState fluidState = world.getFluidState(blockPos);
if (!fluidState.isEmpty())
{
if (action.simulate())
{
return new FluidStack(fluidState.getFluid(), FluidAttributes.BUCKET_VOLUME);
}
Fluid fluid = bucketPickupHandler.pickupFluid(world, blockPos, world.getBlockState(blockPos));
if (fluid != Fluids.EMPTY)
{
return new FluidStack(fluid, FluidAttributes.BUCKET_VOLUME);
}
}
}
return FluidStack.EMPTY;
}
}
@@ -0,0 +1,120 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2020.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.fluids.capability.wrappers;
import javax.annotation.Nonnull;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.fluids.FluidAttributes;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.IFluidBlock;
import net.minecraftforge.fluids.capability.IFluidHandler;
public class FluidBlockWrapper implements IFluidHandler
{
protected final IFluidBlock fluidBlock;
protected final World world;
protected final BlockPos blockPos;
public FluidBlockWrapper(IFluidBlock fluidBlock, World world, BlockPos blockPos)
{
this.fluidBlock = fluidBlock;
this.world = world;
this.blockPos = blockPos;
}
@Override
public int getTanks()
{
return 1;
}
@Nonnull
@Override
public FluidStack getFluidInTank(int tank)
{
return tank == 0 ? fluidBlock.drain(world, blockPos, FluidAction.SIMULATE) : FluidStack.EMPTY;
}
@Override
public int getTankCapacity(int tank)
{
FluidStack stored = getFluidInTank(tank);
if (!stored.isEmpty())
{
float filledPercentage = fluidBlock.getFilledPercentage(world, blockPos);
if (filledPercentage > 0)
{
return (int) (stored.getAmount() / filledPercentage);
}
}
return FluidAttributes.BUCKET_VOLUME;
}
@Override
public boolean isFluidValid(int tank, @Nonnull FluidStack stack)
{
return stack.getFluid() == fluidBlock.getFluid();
}
@Override
public int fill(FluidStack resource, FluidAction action)
{
return fluidBlock.place(world, blockPos, resource, action);
}
@Nonnull
@Override
public FluidStack drain(FluidStack resource, FluidAction action)
{
if (!resource.isEmpty() && fluidBlock.canDrain(world, blockPos) && resource.getFluid() == fluidBlock.getFluid())
{
FluidStack simulatedDrained = fluidBlock.drain(world, blockPos, FluidAction.SIMULATE);
if (simulatedDrained.getAmount() <= resource.getAmount() && resource.isFluidEqual(simulatedDrained))
{
if (action.execute())
{
return fluidBlock.drain(world, blockPos, FluidAction.EXECUTE).copy();
}
return simulatedDrained.copy();
}
}
return FluidStack.EMPTY;
}
@Nonnull
@Override
public FluidStack drain(int maxDrain, FluidAction action)
{
if (maxDrain <= 0 && fluidBlock.canDrain(world, blockPos))
{
FluidStack simulatedDrained = fluidBlock.drain(world, blockPos, FluidAction.SIMULATE);
if (simulatedDrained.getAmount() <= maxDrain)
{
if (action.execute())
{
return fluidBlock.drain(world, blockPos, FluidAction.EXECUTE).copy();
}
return simulatedDrained.copy();
}
}
return FluidStack.EMPTY;
}
}