From d97f28141cb6bd4ffd0b232343f2975da8e1915f Mon Sep 17 00:00:00 2001 From: Sara Freimer Date: Mon, 15 Feb 2021 20:08:05 -0500 Subject: [PATCH] Fix a few FluidUtil issues that were causing DispenseFluidContainer to not function properly (#7422) --- .../net/minecraftforge/fluids/FluidUtil.java | 63 +++++--- .../capability/wrappers/BlockWrapper.java | 35 +++- .../wrappers/BucketPickupHandlerWrapper.java | 151 ++++++++++++++++++ .../wrappers/FluidBlockWrapper.java | 120 ++++++++++++++ .../resources/META-INF/accesstransformer.cfg | 2 + .../debug/fluid/NewFluidTest.java | 2 + 6 files changed, 353 insertions(+), 20 deletions(-) create mode 100644 src/main/java/net/minecraftforge/fluids/capability/wrappers/BucketPickupHandlerWrapper.java create mode 100644 src/main/java/net/minecraftforge/fluids/capability/wrappers/FluidBlockWrapper.java diff --git a/src/main/java/net/minecraftforge/fluids/FluidUtil.java b/src/main/java/net/minecraftforge/fluids/FluidUtil.java index 74091ac9e1..e372afe754 100644 --- a/src/main/java/net/minecraftforge/fluids/FluidUtil.java +++ b/src/main/java/net/minecraftforge/fluids/FluidUtil.java @@ -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 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 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 diff --git a/src/main/java/net/minecraftforge/fluids/capability/wrappers/BlockWrapper.java b/src/main/java/net/minecraftforge/fluids/capability/wrappers/BlockWrapper.java index 685148d8bb..682d9d4553 100644 --- a/src/main/java/net/minecraftforge/fluids/capability/wrappers/BlockWrapper.java +++ b/src/main/java/net/minecraftforge/fluids/capability/wrappers/BlockWrapper.java @@ -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; + } + } } diff --git a/src/main/java/net/minecraftforge/fluids/capability/wrappers/BucketPickupHandlerWrapper.java b/src/main/java/net/minecraftforge/fluids/capability/wrappers/BucketPickupHandlerWrapper.java new file mode 100644 index 0000000000..b3b70c0afa --- /dev/null +++ b/src/main/java/net/minecraftforge/fluids/capability/wrappers/BucketPickupHandlerWrapper.java @@ -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; + } +} diff --git a/src/main/java/net/minecraftforge/fluids/capability/wrappers/FluidBlockWrapper.java b/src/main/java/net/minecraftforge/fluids/capability/wrappers/FluidBlockWrapper.java new file mode 100644 index 0000000000..f2ca007783 --- /dev/null +++ b/src/main/java/net/minecraftforge/fluids/capability/wrappers/FluidBlockWrapper.java @@ -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; + } +} diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index 00c903980b..a73317bce4 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -242,7 +242,9 @@ public net.minecraft.inventory.container.ContainerType$IFactory public net.minecraft.inventory.container.RepairContainer field_82856_l #RepairContainer/stackSizeToBeUsedInRepair #group public net.minecraft.item.Item public net.minecraft.item.AxeItem (Lnet/minecraft/item/IItemTier;FFLnet/minecraft/item/Item$Properties;)V +public net.minecraft.item.BlockItemUseContext (Lnet/minecraft/world/World;Lnet/minecraft/entity/player/PlayerEntity;Lnet/minecraft/util/Hand;Lnet/minecraft/item/ItemStack;Lnet/minecraft/util/math/BlockRayTraceResult;)V public net.minecraft.item.HoeItem (Lnet/minecraft/item/IItemTier;IFLnet/minecraft/item/Item$Properties;)V +public net.minecraft.item.ItemUseContext (Lnet/minecraft/world/World;Lnet/minecraft/entity/player/PlayerEntity;Lnet/minecraft/util/Hand;Lnet/minecraft/item/ItemStack;Lnet/minecraft/util/math/BlockRayTraceResult;)V public net.minecraft.item.MusicDiscItem (ILnet/minecraft/util/SoundEvent;Lnet/minecraft/item/Item$Properties;)V public net.minecraft.item.PickaxeItem (Lnet/minecraft/item/IItemTier;IFLnet/minecraft/item/Item$Properties;)V public net.minecraft.item.ToolItem (FFLnet/minecraft/item/IItemTier;Ljava/util/Set;Lnet/minecraft/item/Item$Properties;)V diff --git a/src/test/java/net/minecraftforge/debug/fluid/NewFluidTest.java b/src/test/java/net/minecraftforge/debug/fluid/NewFluidTest.java index 78fa4d21ee..4ba3bd26a7 100644 --- a/src/test/java/net/minecraftforge/debug/fluid/NewFluidTest.java +++ b/src/test/java/net/minecraftforge/debug/fluid/NewFluidTest.java @@ -30,6 +30,7 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.world.IBlockReader; import net.minecraft.world.IWorld; import net.minecraftforge.common.util.Lazy; +import net.minecraftforge.fluids.DispenseFluidContainer; import org.apache.commons.lang3.Validate; import net.minecraft.fluid.FlowingFluid; @@ -108,6 +109,7 @@ public class NewFluidTest Validate.isTrue(state.getBlock() == Blocks.WATER && state2 == state); ItemStack stack = Fluids.WATER.getAttributes().getBucket(new FluidStack(Fluids.WATER, 1)); Validate.isTrue(stack.getItem() == Fluids.WATER.getFilledBucket()); + event.enqueueWork(() -> DispenserBlock.registerDispenseBehavior(test_fluid_bucket.get(), DispenseFluidContainer.getInstance())); } // WARNING: this doesn't allow "any fluid", only the fluid from this test mod!