2016-06-23 03:49:47 +00:00
/ *
* Minecraft Forge
2020-07-02 17:49:11 +00:00
* Copyright ( c ) 2016 - 2020 .
2016-06-23 03:49:47 +00:00
*
* 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
* /
2016-01-02 13:38:18 +00:00
package net.minecraftforge.fluids ;
2016-11-13 22:09:54 +00:00
import javax.annotation.Nonnull ;
2016-06-04 01:26:41 +00:00
import javax.annotation.Nullable ;
2017-05-02 00:25:45 +00:00
import com.google.common.base.Preconditions ;
2016-05-16 20:20:31 +00:00
import net.minecraft.block.Block ;
import net.minecraft.block.material.Material ;
2019-05-23 23:02:15 +00:00
import net.minecraft.block.BlockState ;
import net.minecraft.entity.player.PlayerEntity ;
2019-07-28 19:35:30 +00:00
import net.minecraft.fluid.Fluid ;
2019-08-29 01:01:11 +00:00
import net.minecraft.fluid.Fluids ;
2018-09-21 06:50:50 +00:00
import net.minecraft.item.BlockItemUseContext ;
2016-01-02 13:38:18 +00:00
import net.minecraft.item.ItemStack ;
2019-05-23 23:02:15 +00:00
import net.minecraft.item.ItemUseContext ;
2019-08-29 01:01:11 +00:00
import net.minecraft.item.Items ;
2016-06-04 01:26:41 +00:00
import net.minecraft.tileentity.TileEntity ;
2019-08-29 01:01:11 +00:00
import net.minecraft.util.* ;
2016-06-04 01:26:41 +00:00
import net.minecraft.util.math.BlockPos ;
2019-05-23 23:02:15 +00:00
import net.minecraft.util.math.BlockRayTraceResult ;
2020-06-19 14:58:45 +00:00
import net.minecraft.util.math.vector.Vector3d ;
2016-06-04 01:26:41 +00:00
import net.minecraft.world.World ;
2019-01-27 06:38:53 +00:00
import net.minecraftforge.common.util.LazyOptional ;
2016-06-04 01:26:41 +00:00
import net.minecraftforge.fluids.capability.CapabilityFluidHandler ;
import net.minecraftforge.fluids.capability.IFluidHandler ;
2016-10-10 07:44:50 +00:00
import net.minecraftforge.fluids.capability.IFluidHandlerItem ;
2016-12-06 04:17:47 +00:00
import net.minecraftforge.fluids.capability.wrappers.BlockWrapper ;
2017-05-02 00:25:45 +00:00
import net.minecraftforge.items.CapabilityItemHandler ;
2016-01-02 13:38:18 +00:00
import net.minecraftforge.items.IItemHandler ;
import net.minecraftforge.items.ItemHandlerHelper ;
2020-08-25 01:11:28 +00:00
import java.util.Optional ;
2016-01-02 13:38:18 +00:00
public class FluidUtil
{
private FluidUtil ( )
{
}
2017-05-02 00:25:45 +00:00
/ * *
* Used to handle the common case of a player holding a fluid item and right - clicking on a fluid handler block .
* First it tries to fill the item from the block ,
* if that action fails then it tries to drain the item into the block .
* Automatically updates the item in the player ' s hand and stashes any extra items created .
*
* @param player The player doing the interaction between the item and fluid handler block .
* @param hand The player ' s hand that is holding an item that should interact with the fluid handler block .
* @param world The world that contains the fluid handler block .
* @param pos The position of the fluid handler block in the world .
* @param side The side of the block to interact with . May be null .
* @return true if the interaction succeeded and updated the item held by the player , false otherwise .
* /
2019-05-23 23:02:15 +00:00
public static boolean interactWithFluidHandler ( @Nonnull PlayerEntity player , @Nonnull Hand hand , @Nonnull World world , @Nonnull BlockPos pos , @Nullable Direction side )
2017-05-02 00:25:45 +00:00
{
Preconditions . checkNotNull ( world ) ;
Preconditions . checkNotNull ( pos ) ;
2018-09-09 21:27:15 +00:00
return getFluidHandler ( world , pos , side ) . map ( handler - > interactWithFluidHandler ( player , hand , handler ) ) . orElse ( false ) ;
2017-06-26 01:22:52 +00:00
}
/ * *
* Used to handle the common case of a player holding a fluid item and right - clicking on a fluid handler .
* First it tries to fill the item from the handler ,
* if that action fails then it tries to drain the item into the handler .
* Automatically updates the item in the player ' s hand and stashes any extra items created .
*
* @param player The player doing the interaction between the item and fluid handler .
* @param hand The player ' s hand that is holding an item that should interact with the fluid handler .
* @param handler The fluid handler .
* @return true if the interaction succeeded and updated the item held by the player , false otherwise .
* /
2019-05-23 23:02:15 +00:00
public static boolean interactWithFluidHandler ( @Nonnull PlayerEntity player , @Nonnull Hand hand , @Nonnull IFluidHandler handler )
2017-06-26 01:22:52 +00:00
{
Preconditions . checkNotNull ( player ) ;
Preconditions . checkNotNull ( hand ) ;
Preconditions . checkNotNull ( handler ) ;
2017-05-02 00:25:45 +00:00
ItemStack heldItem = player . getHeldItem ( hand ) ;
if ( ! heldItem . isEmpty ( ) )
{
2018-09-09 21:27:15 +00:00
return player . getCapability ( CapabilityItemHandler . ITEM_HANDLER_CAPABILITY )
. map ( playerInventory - > {
2018-09-21 06:50:50 +00:00
2018-09-09 21:27:15 +00:00
FluidActionResult fluidActionResult = tryFillContainerAndStow ( heldItem , handler , playerInventory , Integer . MAX_VALUE , player , true ) ;
if ( ! fluidActionResult . isSuccess ( ) )
{
fluidActionResult = tryEmptyContainerAndStow ( heldItem , handler , playerInventory , Integer . MAX_VALUE , player , true ) ;
}
2018-09-21 06:50:50 +00:00
2018-09-09 21:27:15 +00:00
if ( fluidActionResult . isSuccess ( ) )
{
player . setHeldItem ( hand , fluidActionResult . getResult ( ) ) ;
return true ;
}
return false ;
} )
. orElse ( false ) ;
2017-05-02 00:25:45 +00:00
}
return false ;
}
2016-06-04 01:26:41 +00:00
/ * *
* Fill a container from the given fluidSource .
*
* @param container The container to be filled . Will not be modified .
2016-10-10 07:44:50 +00:00
* Separate handling must be done to reduce the stack size , stow containers , etc , on success .
2019-07-28 19:35:30 +00:00
* See { @link # tryFillContainerAndStow ( ItemStack , IFluidHandler , IItemHandler , int , PlayerEntity , boolean ) } .
2016-06-04 01:26:41 +00:00
* @param fluidSource The fluid handler to be drained .
* @param maxAmount The largest amount of fluid that should be transferred .
* @param player The player to make the filling noise . Pass null for no noise .
* @param doFill true if the container should actually be filled , false if it should be simulated .
2016-10-10 07:44:50 +00:00
* @return a { @link FluidActionResult } holding the filled container if successful .
2016-06-04 01:26:41 +00:00
* /
2016-11-13 22:09:54 +00:00
@Nonnull
2019-05-23 23:02:15 +00:00
public static FluidActionResult tryFillContainer ( @Nonnull ItemStack container , IFluidHandler fluidSource , int maxAmount , @Nullable PlayerEntity player , boolean doFill )
2016-06-04 01:26:41 +00:00
{
2017-05-02 00:25:45 +00:00
ItemStack containerCopy = ItemHandlerHelper . copyStackWithSize ( container , 1 ) ; // do not modify the input
2018-09-09 21:27:15 +00:00
return getFluidHandler ( containerCopy )
. map ( containerFluidHandler - > {
FluidStack simulatedTransfer = tryFluidTransfer ( containerFluidHandler , fluidSource , maxAmount , false ) ;
2019-08-29 01:01:11 +00:00
if ( ! simulatedTransfer . isEmpty ( ) )
2016-06-04 01:26:41 +00:00
{
2018-09-09 21:27:15 +00:00
if ( doFill )
{
tryFluidTransfer ( containerFluidHandler , fluidSource , maxAmount , true ) ;
if ( player ! = null )
{
2019-07-28 19:35:30 +00:00
SoundEvent soundevent = simulatedTransfer . getFluid ( ) . getAttributes ( ) . getFillSound ( simulatedTransfer ) ;
2020-02-26 03:45:14 +00:00
player . world . playSound ( null , player . getPosX ( ) , player . getPosY ( ) + 0 . 5 , player . getPosZ ( ) , soundevent , SoundCategory . BLOCKS , 1 . 0F , 1 . 0F ) ;
2018-09-09 21:27:15 +00:00
}
}
else
{
2019-07-28 19:35:30 +00:00
containerFluidHandler . fill ( simulatedTransfer , IFluidHandler . FluidAction . SIMULATE ) ;
2018-09-09 21:27:15 +00:00
}
2018-09-21 06:50:50 +00:00
2018-09-09 21:27:15 +00:00
ItemStack resultContainer = containerFluidHandler . getContainer ( ) ;
return new FluidActionResult ( resultContainer ) ;
2016-06-04 01:26:41 +00:00
}
2018-09-09 21:27:15 +00:00
return FluidActionResult . FAILURE ;
} )
. orElse ( FluidActionResult . FAILURE ) ;
2016-06-04 01:26:41 +00:00
}
/ * *
* Takes a filled container and tries to empty it into the given tank .
*
* @param container The filled container . Will not be modified .
2016-10-10 07:44:50 +00:00
* Separate handling must be done to reduce the stack size , stow containers , etc , on success .
2019-07-28 19:35:30 +00:00
* See { @link # tryEmptyContainerAndStow ( ItemStack , IFluidHandler , IItemHandler , int , PlayerEntity , boolean ) } .
2016-06-04 01:26:41 +00:00
* @param fluidDestination The fluid handler to be filled by the container .
* @param maxAmount The largest amount of fluid that should be transferred .
* @param player Player for making the bucket drained sound . Pass null for no noise .
* @param doDrain true if the container should actually be drained , false if it should be simulated .
2016-10-10 07:44:50 +00:00
* @return a { @link FluidActionResult } holding the empty container if the fluid handler was filled .
* NOTE If the container is consumable , the empty container will be null on success .
2016-06-04 01:26:41 +00:00
* /
2016-11-13 22:09:54 +00:00
@Nonnull
2019-05-23 23:02:15 +00:00
public static FluidActionResult tryEmptyContainer ( @Nonnull ItemStack container , IFluidHandler fluidDestination , int maxAmount , @Nullable PlayerEntity player , boolean doDrain )
2016-06-04 01:26:41 +00:00
{
2017-05-02 00:25:45 +00:00
ItemStack containerCopy = ItemHandlerHelper . copyStackWithSize ( container , 1 ) ; // do not modify the input
2018-09-09 21:27:15 +00:00
return getFluidHandler ( containerCopy )
. map ( containerFluidHandler - > {
2019-10-05 17:49:26 +00:00
// We are acting on a COPY of the stack, so performing changes is acceptable even if we are simulating.
FluidStack transfer = tryFluidTransfer ( fluidDestination , containerFluidHandler , maxAmount , true ) ;
if ( transfer . isEmpty ( ) )
return FluidActionResult . FAILURE ;
if ( doDrain & & player ! = null )
2018-09-09 21:27:15 +00:00
{
2019-10-05 17:49:26 +00:00
SoundEvent soundevent = transfer . getFluid ( ) . getAttributes ( ) . getEmptySound ( transfer ) ;
2020-02-26 03:45:14 +00:00
player . world . playSound ( null , player . getPosX ( ) , player . getPosY ( ) + 0 . 5 , player . getPosZ ( ) , soundevent , SoundCategory . BLOCKS , 1 . 0F , 1 . 0F ) ;
2018-09-09 21:27:15 +00:00
}
2019-10-05 17:49:26 +00:00
ItemStack resultContainer = containerFluidHandler . getContainer ( ) ;
return new FluidActionResult ( resultContainer ) ;
2018-09-09 21:27:15 +00:00
} )
. orElse ( FluidActionResult . FAILURE ) ;
2016-06-04 01:26:41 +00:00
}
2018-01-18 21:23:30 +00:00
/ * *
* Takes an Fluid Container Item and tries to fill it from the given tank .
* If the player is in creative mode , the container will not be modified on success , and no additional items created .
* If the input itemstack has a stacksize > 1 it will stow the filled container in the given inventory .
* If the inventory does not accept it , it will be given to the player or dropped at the players feet .
* If player is null in this case , the action will be aborted .
*
* @param container The Fluid Container ItemStack to fill .
* Will not be modified directly , if modifications are necessary a modified copy is returned in the result .
* @param fluidSource The fluid source to fill from
* @param inventory An inventory where any additionally created item ( filled container if multiple empty are present ) are put
* @param maxAmount Maximum amount of fluid to take from the tank .
* @param player The player that gets the items the inventory can ' t take .
* Can be null , only used if the inventory cannot take the filled stack .
* @param doFill true if the container should actually be filled , false if it should be simulated .
* @return a { @link FluidActionResult } holding the result and the resulting container . The resulting container is empty on failure .
* /
@Nonnull
2019-05-23 23:02:15 +00:00
public static FluidActionResult tryFillContainerAndStow ( @Nonnull ItemStack container , IFluidHandler fluidSource , IItemHandler inventory , int maxAmount , @Nullable PlayerEntity player , boolean doFill )
2016-06-04 01:26:41 +00:00
{
2016-12-21 23:52:30 +00:00
if ( container . isEmpty ( ) )
2016-06-04 01:26:41 +00:00
{
2016-10-10 07:44:50 +00:00
return FluidActionResult . FAILURE ;
2016-06-04 01:26:41 +00:00
}
2019-06-22 02:42:16 +00:00
if ( player ! = null & & player . abilities . isCreativeMode )
2016-06-04 01:26:41 +00:00
{
2018-01-18 21:23:30 +00:00
FluidActionResult filledReal = tryFillContainer ( container , fluidSource , maxAmount , player , doFill ) ;
2016-10-10 07:44:50 +00:00
if ( filledReal . isSuccess ( ) )
2016-06-04 01:26:41 +00:00
{
2016-10-10 07:44:50 +00:00
return new FluidActionResult ( container ) ; // creative mode: item does not change
2016-06-04 01:26:41 +00:00
}
}
2016-12-21 23:52:30 +00:00
else if ( container . getCount ( ) = = 1 ) // don't need to stow anything, just fill the container stack
2016-06-04 01:26:41 +00:00
{
2018-01-18 21:23:30 +00:00
FluidActionResult filledReal = tryFillContainer ( container , fluidSource , maxAmount , player , doFill ) ;
2016-10-10 07:44:50 +00:00
if ( filledReal . isSuccess ( ) )
2016-06-04 01:26:41 +00:00
{
2016-10-10 07:44:50 +00:00
return filledReal ;
2016-01-02 13:38:18 +00:00
}
}
else
{
2016-10-10 07:44:50 +00:00
FluidActionResult filledSimulated = tryFillContainer ( container , fluidSource , maxAmount , player , false ) ;
if ( filledSimulated . isSuccess ( ) )
2016-01-02 13:38:18 +00:00
{
2016-06-04 01:26:41 +00:00
// check if we can give the itemStack to the inventory
2016-10-10 07:44:50 +00:00
ItemStack remainder = ItemHandlerHelper . insertItemStacked ( inventory , filledSimulated . getResult ( ) , true ) ;
2016-12-21 23:52:30 +00:00
if ( remainder . isEmpty ( ) | | player ! = null )
2016-06-04 01:26:41 +00:00
{
2018-01-18 21:23:30 +00:00
FluidActionResult filledReal = tryFillContainer ( container , fluidSource , maxAmount , player , doFill ) ;
remainder = ItemHandlerHelper . insertItemStacked ( inventory , filledReal . getResult ( ) , ! doFill ) ;
2016-06-04 01:26:41 +00:00
// give it to the player or drop it at their feet
2018-01-18 21:23:30 +00:00
if ( ! remainder . isEmpty ( ) & & player ! = null & & doFill )
2016-06-04 01:26:41 +00:00
{
ItemHandlerHelper . giveItemToPlayer ( player , remainder ) ;
}
2016-10-10 07:44:50 +00:00
ItemStack containerCopy = container . copy ( ) ;
2016-12-21 23:52:30 +00:00
containerCopy . shrink ( 1 ) ;
2016-10-10 07:44:50 +00:00
return new FluidActionResult ( containerCopy ) ;
2016-06-04 01:26:41 +00:00
}
2016-01-02 13:38:18 +00:00
}
2016-06-04 01:26:41 +00:00
}
2016-10-10 07:44:50 +00:00
return FluidActionResult . FAILURE ;
2016-06-04 01:26:41 +00:00
}
/ * *
* Takes an Fluid Container Item , tries to empty it into the fluid handler , and stows it in the given inventory .
* If the player is in creative mode , the container will not be modified on success , and no additional items created .
* If the input itemstack has a stacksize > 1 it will stow the emptied container in the given inventory .
* If the inventory does not accept the emptied container , it will be given to the player or dropped at the players feet .
* If player is null in this case , the action will be aborted .
*
2016-10-10 07:44:50 +00:00
* @param container The filled Fluid Container Itemstack to empty .
* Will not be modified directly , if modifications are necessary a modified copy is returned in the result .
2018-01-18 21:23:30 +00:00
* @param fluidDestination The fluid destination to fill from the fluid container .
* @param inventory An inventory where any additionally created item ( filled container if multiple empty are present ) are put
* @param maxAmount Maximum amount of fluid to take from the tank .
* @param player The player that gets the items the inventory can ' t take . Can be null , only used if the inventory cannot take the filled stack .
* @param doDrain true if the container should actually be drained , false if it should be simulated .
* @return a { @link FluidActionResult } holding the result and the resulting container . The resulting container is empty on failure .
* /
@Nonnull
2019-05-23 23:02:15 +00:00
public static FluidActionResult tryEmptyContainerAndStow ( @Nonnull ItemStack container , IFluidHandler fluidDestination , IItemHandler inventory , int maxAmount , @Nullable PlayerEntity player , boolean doDrain )
2016-06-04 01:26:41 +00:00
{
2016-12-21 23:52:30 +00:00
if ( container . isEmpty ( ) )
2016-06-04 01:26:41 +00:00
{
2016-10-10 07:44:50 +00:00
return FluidActionResult . FAILURE ;
2016-06-04 01:26:41 +00:00
}
2016-01-02 13:38:18 +00:00
2019-06-22 02:42:16 +00:00
if ( player ! = null & & player . abilities . isCreativeMode )
2016-06-04 01:26:41 +00:00
{
2018-01-18 21:23:30 +00:00
FluidActionResult emptiedReal = tryEmptyContainer ( container , fluidDestination , maxAmount , player , doDrain ) ;
2016-10-10 07:44:50 +00:00
if ( emptiedReal . isSuccess ( ) )
2016-06-04 01:26:41 +00:00
{
2016-10-10 07:44:50 +00:00
return new FluidActionResult ( container ) ; // creative mode: item does not change
2016-06-04 01:26:41 +00:00
}
}
2016-12-21 23:52:30 +00:00
else if ( container . getCount ( ) = = 1 ) // don't need to stow anything, just fill and edit the container stack
2016-06-04 01:26:41 +00:00
{
2018-01-18 21:23:30 +00:00
FluidActionResult emptiedReal = tryEmptyContainer ( container , fluidDestination , maxAmount , player , doDrain ) ;
2016-10-10 07:44:50 +00:00
if ( emptiedReal . isSuccess ( ) )
2016-11-13 22:09:54 +00:00
{
2016-10-10 07:44:50 +00:00
return emptiedReal ;
2016-06-04 01:26:41 +00:00
}
}
else
{
2016-10-10 07:44:50 +00:00
FluidActionResult emptiedSimulated = tryEmptyContainer ( container , fluidDestination , maxAmount , player , false ) ;
if ( emptiedSimulated . isSuccess ( ) )
2016-11-13 22:09:54 +00:00
{
// check if we can give the itemStack to the inventory
2016-10-10 07:44:50 +00:00
ItemStack remainder = ItemHandlerHelper . insertItemStacked ( inventory , emptiedSimulated . getResult ( ) , true ) ;
2016-12-21 23:52:30 +00:00
if ( remainder . isEmpty ( ) | | player ! = null )
2016-06-04 01:26:41 +00:00
{
2018-01-18 21:23:30 +00:00
FluidActionResult emptiedReal = tryEmptyContainer ( container , fluidDestination , maxAmount , player , doDrain ) ;
remainder = ItemHandlerHelper . insertItemStacked ( inventory , emptiedReal . getResult ( ) , ! doDrain ) ;
2016-06-04 01:26:41 +00:00
2016-11-13 22:09:54 +00:00
// give it to the player or drop it at their feet
2018-01-18 21:23:30 +00:00
if ( ! remainder . isEmpty ( ) & & player ! = null & & doDrain )
2016-11-13 22:09:54 +00:00
{
ItemHandlerHelper . giveItemToPlayer ( player , remainder ) ;
2016-01-02 13:38:18 +00:00
}
2016-11-13 22:09:54 +00:00
2016-10-10 07:44:50 +00:00
ItemStack containerCopy = container . copy ( ) ;
2016-12-21 23:52:30 +00:00
containerCopy . shrink ( 1 ) ;
2016-10-10 07:44:50 +00:00
return new FluidActionResult ( containerCopy ) ;
2016-01-02 13:38:18 +00:00
}
}
}
2016-10-10 07:44:50 +00:00
return FluidActionResult . FAILURE ;
2016-01-02 13:38:18 +00:00
}
2016-06-04 01:26:41 +00:00
/ * *
2017-05-02 00:25:45 +00:00
* Fill a destination fluid handler from a source fluid handler with a max amount .
* To specify a fluid to transfer instead of max amount , use { @link # tryFluidTransfer ( IFluidHandler , IFluidHandler , FluidStack , boolean ) }
* To transfer as much as possible , use { @link Integer # MAX_VALUE } for maxAmount .
2016-06-04 01:26:41 +00:00
*
* @param fluidDestination The fluid handler to be filled .
* @param fluidSource The fluid handler to be drained .
* @param maxAmount The largest amount of fluid that should be transferred .
* @param doTransfer True if the transfer should actually be done , false if it should be simulated .
* @return the fluidStack that was transferred from the source to the destination . null on failure .
* /
2019-08-29 01:01:11 +00:00
@Nonnull
2016-06-04 01:26:41 +00:00
public static FluidStack tryFluidTransfer ( IFluidHandler fluidDestination , IFluidHandler fluidSource , int maxAmount , boolean doTransfer )
2016-05-16 20:20:31 +00:00
{
2019-07-28 19:35:30 +00:00
FluidStack drainable = fluidSource . drain ( maxAmount , IFluidHandler . FluidAction . SIMULATE ) ;
2019-08-29 01:01:11 +00:00
if ( ! drainable . isEmpty ( ) )
2016-06-04 01:26:41 +00:00
{
2017-05-02 00:25:45 +00:00
return tryFluidTransfer_Internal ( fluidDestination , fluidSource , drainable , doTransfer ) ;
}
2019-08-29 01:01:11 +00:00
return FluidStack . EMPTY ;
2017-05-02 00:25:45 +00:00
}
/ * *
* Fill a destination fluid handler from a source fluid handler using a specific fluid .
* To specify a max amount to transfer instead of specific fluid , use { @link # tryFluidTransfer ( IFluidHandler , IFluidHandler , int , boolean ) }
* To transfer as much as possible , use { @link Integer # MAX_VALUE } for resource . amount .
*
* @param fluidDestination The fluid handler to be filled .
* @param fluidSource The fluid handler to be drained .
* @param resource The fluid that should be transferred . Amount represents the maximum amount to transfer .
* @param doTransfer True if the transfer should actually be done , false if it should be simulated .
* @return the fluidStack that was transferred from the source to the destination . null on failure .
* /
2019-08-29 01:01:11 +00:00
@Nonnull
2017-05-02 00:25:45 +00:00
public static FluidStack tryFluidTransfer ( IFluidHandler fluidDestination , IFluidHandler fluidSource , FluidStack resource , boolean doTransfer )
{
2019-07-28 19:35:30 +00:00
FluidStack drainable = fluidSource . drain ( resource , IFluidHandler . FluidAction . SIMULATE ) ;
2019-08-29 01:01:11 +00:00
if ( ! drainable . isEmpty ( ) & & resource . isFluidEqual ( drainable ) )
2017-05-02 00:25:45 +00:00
{
return tryFluidTransfer_Internal ( fluidDestination , fluidSource , drainable , doTransfer ) ;
}
2019-08-29 01:01:11 +00:00
return FluidStack . EMPTY ;
2017-05-02 00:25:45 +00:00
}
/ * *
* Internal method for filling a destination fluid handler from a source fluid handler using a specific fluid .
* Assumes that " drainable " can be drained from " fluidSource " .
*
* Modders : Instead of this method , use { @link # tryFluidTransfer ( IFluidHandler , IFluidHandler , FluidStack , boolean ) }
* or { @link # tryFluidTransfer ( IFluidHandler , IFluidHandler , int , boolean ) } .
* /
2019-08-29 01:01:11 +00:00
@Nonnull
2017-05-02 00:25:45 +00:00
private static FluidStack tryFluidTransfer_Internal ( IFluidHandler fluidDestination , IFluidHandler fluidSource , FluidStack drainable , boolean doTransfer )
{
2019-07-28 19:35:30 +00:00
int fillableAmount = fluidDestination . fill ( drainable , IFluidHandler . FluidAction . SIMULATE ) ;
2017-05-02 00:25:45 +00:00
if ( fillableAmount > 0 )
{
if ( doTransfer )
2016-06-04 01:26:41 +00:00
{
2019-07-28 19:35:30 +00:00
FluidStack drained = fluidSource . drain ( fillableAmount , IFluidHandler . FluidAction . EXECUTE ) ;
2019-08-29 01:01:11 +00:00
if ( ! drained . isEmpty ( ) )
2016-10-10 07:44:50 +00:00
{
2019-08-29 01:01:11 +00:00
drained . setAmount ( fluidDestination . fill ( drained , IFluidHandler . FluidAction . EXECUTE ) ) ;
2017-05-02 00:25:45 +00:00
return drained ;
2016-06-04 01:26:41 +00:00
}
}
2017-05-02 00:25:45 +00:00
else
{
2019-08-29 01:01:11 +00:00
drainable . setAmount ( fillableAmount ) ;
2017-05-02 00:25:45 +00:00
return drainable ;
}
2016-06-04 01:26:41 +00:00
}
2019-08-29 01:01:11 +00:00
return FluidStack . EMPTY ;
2016-05-16 20:20:31 +00:00
}
2016-01-02 13:38:18 +00:00
/ * *
2016-10-10 07:44:50 +00:00
* Helper method to get an { @link IFluidHandlerItem } for an itemStack .
2016-01-02 13:38:18 +00:00
*
2016-10-10 07:44:50 +00:00
* The itemStack passed in here WILL be modified , the { @link IFluidHandlerItem } acts on it directly .
* Some { @link IFluidHandlerItem } will change the item entirely , always use { @link IFluidHandlerItem # getContainer ( ) }
* after using the fluid handler to get the resulting item back .
2016-06-04 01:26:41 +00:00
*
* Note that the itemStack MUST have a stackSize of 1 if you want to fill or drain it .
2016-10-10 07:44:50 +00:00
* You can ' t fill or drain multiple items at once , if you do then liquid is multiplied or destroyed .
2016-06-04 01:26:41 +00:00
*
* Vanilla buckets will be converted to universal buckets if they are enabled .
2016-01-02 13:38:18 +00:00
* /
2019-01-27 06:38:53 +00:00
public static LazyOptional < IFluidHandlerItem > getFluidHandler ( @Nonnull ItemStack itemStack )
2016-01-02 13:38:18 +00:00
{
2018-09-09 21:27:15 +00:00
return itemStack . getCapability ( CapabilityFluidHandler . FLUID_HANDLER_ITEM_CAPABILITY ) ;
2016-06-04 01:26:41 +00:00
}
/ * *
* Helper method to get the fluid contained in an itemStack
* /
2020-08-25 01:11:28 +00:00
public static Optional < FluidStack > getFluidContained ( @Nonnull ItemStack container )
2016-06-04 01:26:41 +00:00
{
2016-12-21 23:52:30 +00:00
if ( ! container . isEmpty ( ) )
2016-01-02 13:38:18 +00:00
{
2017-05-02 00:25:45 +00:00
container = ItemHandlerHelper . copyStackWithSize ( container , 1 ) ;
2018-09-09 21:27:15 +00:00
return getFluidHandler ( container )
2019-07-28 19:35:30 +00:00
. map ( handler - > handler . drain ( Integer . MAX_VALUE , IFluidHandler . FluidAction . SIMULATE ) ) ;
2016-01-02 13:38:18 +00:00
}
2020-08-25 01:11:28 +00:00
return Optional . empty ( ) ;
2016-01-02 13:38:18 +00:00
}
2016-06-04 01:26:41 +00:00
/ * *
* Helper method to get an IFluidHandler for at a block position .
* /
2019-05-23 23:02:15 +00:00
public static LazyOptional < IFluidHandler > getFluidHandler ( World world , BlockPos blockPos , @Nullable Direction side )
2016-05-16 20:20:31 +00:00
{
2019-05-23 23:02:15 +00:00
BlockState state = world . getBlockState ( blockPos ) ;
2016-06-04 01:26:41 +00:00
Block block = state . getBlock ( ) ;
if ( block . hasTileEntity ( state ) )
{
TileEntity tileEntity = world . getTileEntity ( blockPos ) ;
2018-09-09 21:27:15 +00:00
if ( tileEntity ! = null )
2016-06-04 01:26:41 +00:00
{
return tileEntity . getCapability ( CapabilityFluidHandler . FLUID_HANDLER_CAPABILITY , side ) ;
}
}
2019-01-27 06:38:53 +00:00
return LazyOptional . empty ( ) ;
2016-05-16 20:20:31 +00:00
}
2016-06-04 01:26:41 +00:00
/ * *
* Attempts to pick up a fluid in the world and put it in an empty container item .
*
2016-10-10 07:44:50 +00:00
* @param emptyContainer The empty container to fill .
* Will not be modified directly , if modifications are necessary a modified copy is returned in the result .
2016-06-04 01:26:41 +00:00
* @param playerIn The player filling the container . Optional .
* @param worldIn The world the fluid is in .
* @param pos The position of the fluid in the world .
* @param side The side of the fluid that is being drained .
2016-10-10 07:44:50 +00:00
* @return a { @link FluidActionResult } holding the result and the resulting container .
2016-06-04 01:26:41 +00:00
* /
2016-11-13 22:09:54 +00:00
@Nonnull
2019-05-23 23:02:15 +00:00
public static FluidActionResult tryPickUpFluid ( @Nonnull ItemStack emptyContainer , @Nullable PlayerEntity playerIn , World worldIn , BlockPos pos , Direction side )
2016-06-04 01:26:41 +00:00
{
2016-12-21 23:52:30 +00:00
if ( emptyContainer . isEmpty ( ) | | worldIn = = null | | pos = = null )
2016-10-10 07:44:50 +00:00
{
return FluidActionResult . FAILURE ;
2016-06-04 01:26:41 +00:00
}
2019-05-23 23:02:15 +00:00
BlockState state = worldIn . getBlockState ( pos ) ;
2016-08-12 08:36:18 +00:00
Block block = state . getBlock ( ) ;
2019-08-29 01:01:11 +00:00
if ( block instanceof IFluidBlock )
2016-06-04 01:26:41 +00:00
{
2019-08-29 01:01:11 +00:00
IFluidHandler targetFluidHandler = getFluidHandler ( worldIn , pos , side ) . orElse ( null ) ;
2016-08-12 08:36:18 +00:00
if ( targetFluidHandler ! = null )
{
2017-05-02 00:25:45 +00:00
return tryFillContainer ( emptyContainer , targetFluidHandler , Integer . MAX_VALUE , playerIn , true ) ;
2016-08-12 08:36:18 +00:00
}
2019-08-29 01:01:11 +00:00
}
2016-10-10 07:44:50 +00:00
return FluidActionResult . FAILURE ;
2016-01-02 13:38:18 +00:00
}
2016-12-06 04:17:47 +00:00
/ * *
2019-07-28 19:35:30 +00:00
* ItemStack version of { @link # tryPlaceFluid ( PlayerEntity , World , BlockPos , IFluidHandler , FluidStack ) } .
2017-05-02 00:25:45 +00:00
* Use the returned { @link FluidActionResult } to update the container ItemStack .
2016-12-06 04:17:47 +00:00
*
* @param player Player who places the fluid . May be null for blocks like dispensers .
* @param world World to place the fluid in
2019-12-11 02:27:07 +00:00
* @param hand
2016-12-06 04:17:47 +00:00
* @param pos The position in the world to place the fluid block
* @param container The fluid container holding the fluidStack to place
* @param resource The fluidStack to place
* @return the container ' s ItemStack with the remaining amount of fluid if the placement was successful , null otherwise
* /
@Nonnull
2019-05-23 23:02:15 +00:00
public static FluidActionResult tryPlaceFluid ( @Nullable PlayerEntity player , World world , Hand hand , BlockPos pos , @Nonnull ItemStack container , FluidStack resource )
2017-05-02 00:25:45 +00:00
{
ItemStack containerCopy = ItemHandlerHelper . copyStackWithSize ( container , 1 ) ; // do not modify the input
2018-09-09 21:27:15 +00:00
return getFluidHandler ( containerCopy )
2019-05-23 23:02:15 +00:00
. filter ( handler - > tryPlaceFluid ( player , world , hand , pos , handler , resource ) )
2018-09-09 21:27:15 +00:00
. map ( IFluidHandlerItem : : getContainer )
. map ( FluidActionResult : : new )
. orElse ( FluidActionResult . FAILURE ) ;
2017-05-02 00:25:45 +00:00
}
/ * *
* Tries to place a fluid resource into the world as a block and drains the fluidSource .
* Makes a fluid emptying or vaporization sound when successful .
* Honors the amount of fluid contained by the used container .
* Checks if water - like fluids should vaporize like in the nether .
*
2019-07-28 19:35:30 +00:00
* Modeled after { @link ItemBucket # tryPlaceContainedLiquid ( PlayerEntity , World , BlockPos ) }
2017-05-02 00:25:45 +00:00
*
* @param player Player who places the fluid . May be null for blocks like dispensers .
* @param world World to place the fluid in
2019-12-11 02:27:07 +00:00
* @param hand
2017-05-02 00:25:45 +00:00
* @param pos The position in the world to place the fluid block
* @param fluidSource The fluid source holding the fluidStack to place
* @param resource The fluidStack to place .
* @return true if the placement was successful , false otherwise
* /
2019-05-23 23:02:15 +00:00
public static boolean tryPlaceFluid ( @Nullable PlayerEntity player , World world , Hand hand , BlockPos pos , IFluidHandler fluidSource , FluidStack resource )
2016-12-06 04:17:47 +00:00
{
2019-08-29 01:01:11 +00:00
if ( world = = null | | pos = = null )
2016-12-06 04:17:47 +00:00
{
2017-05-02 00:25:45 +00:00
return false ;
2016-12-06 04:17:47 +00:00
}
Fluid fluid = resource . getFluid ( ) ;
2019-07-28 19:35:30 +00:00
if ( fluid = = null | | ! fluid . getAttributes ( ) . canBePlacedInWorld ( world , pos , resource ) )
2016-12-06 04:17:47 +00:00
{
2017-05-02 00:25:45 +00:00
return false ;
}
2019-08-29 19:09:42 +00:00
if ( fluidSource . drain ( resource , IFluidHandler . FluidAction . SIMULATE ) . isEmpty ( ) )
2017-05-02 00:25:45 +00:00
{
return false ;
2016-12-06 04:17:47 +00:00
}
2020-06-19 14:58:45 +00:00
BlockItemUseContext context = new BlockItemUseContext ( new ItemUseContext ( player , hand , new BlockRayTraceResult ( Vector3d . ZERO , Direction . UP , pos , false ) ) ) ; //TODO: This neds proper context...
2018-09-21 06:50:50 +00:00
2016-12-06 04:17:47 +00:00
// check that we can place the fluid at the destination
2019-05-23 23:02:15 +00:00
BlockState destBlockState = world . getBlockState ( pos ) ;
2016-12-06 04:17:47 +00:00
Material destMaterial = destBlockState . getMaterial ( ) ;
boolean isDestNonSolid = ! destMaterial . isSolid ( ) ;
2018-09-22 10:40:22 +00:00
boolean isDestReplaceable = destBlockState . isReplaceable ( context ) ;
2016-12-06 04:17:47 +00:00
if ( ! world . isAirBlock ( pos ) & & ! isDestNonSolid & & ! isDestReplaceable )
{
2017-05-02 00:25:45 +00:00
return false ; // Non-air, solid, unreplacable block. We can't put fluid here.
2016-12-06 04:17:47 +00:00
}
2020-06-19 14:58:45 +00:00
if ( world . func_230315_m_ ( ) . func_236040_e_ ( ) & & fluid . getAttributes ( ) . doesVaporize ( world , pos , resource ) )
2016-12-06 04:17:47 +00:00
{
2019-07-28 19:35:30 +00:00
FluidStack result = fluidSource . drain ( resource , IFluidHandler . FluidAction . EXECUTE ) ;
2019-08-29 19:09:42 +00:00
if ( ! result . isEmpty ( ) )
2017-05-02 00:25:45 +00:00
{
2019-07-28 19:35:30 +00:00
result . getFluid ( ) . getAttributes ( ) . vaporize ( player , world , pos , result ) ;
2017-05-02 00:25:45 +00:00
return true ;
}
2016-12-06 04:17:47 +00:00
}
else
{
2017-05-02 00:25:45 +00:00
// This fluid handler places the fluid block when filled
IFluidHandler handler = getFluidBlockHandler ( fluid , world , pos ) ;
FluidStack result = tryFluidTransfer ( handler , fluidSource , resource , true ) ;
2019-08-29 19:09:42 +00:00
if ( ! result . isEmpty ( ) )
2016-12-06 04:17:47 +00:00
{
2019-07-28 19:35:30 +00:00
SoundEvent soundevent = resource . getFluid ( ) . getAttributes ( ) . getEmptySound ( resource ) ;
2017-05-02 00:25:45 +00:00
world . playSound ( player , pos , soundevent , SoundCategory . BLOCKS , 1 . 0F , 1 . 0F ) ;
return true ;
2016-12-06 04:17:47 +00:00
}
2017-05-02 00:25:45 +00:00
}
return false ;
}
2016-12-06 04:17:47 +00:00
2017-05-02 00:25:45 +00:00
/ * *
* Internal method for getting a fluid block handler for placing a fluid .
*
2019-07-28 19:35:30 +00:00
* Modders : Instead of this method , use { @link # tryPlaceFluid ( PlayerEntity , World , BlockPos , ItemStack , FluidStack ) }
* or { @link # tryPlaceFluid ( PlayerEntity , World , BlockPos , IFluidHandler , FluidStack ) }
2017-05-02 00:25:45 +00:00
* /
private static IFluidHandler getFluidBlockHandler ( Fluid fluid , World world , BlockPos pos )
{
2019-08-29 01:01:11 +00:00
BlockState state = fluid . getAttributes ( ) . getBlock ( world , pos , fluid . getDefaultState ( ) ) ;
return new BlockWrapper ( state , world , pos ) ;
2017-05-02 00:25:45 +00:00
}
/ * *
* Destroys a block when a fluid is placed in the same position .
2019-07-28 19:35:30 +00:00
* Modeled after { @link ItemBucket # tryPlaceContainedLiquid ( PlayerEntity , World , BlockPos ) }
2017-05-02 00:25:45 +00:00
*
* This is a helper method for implementing { @link IFluidBlock # place ( World , BlockPos , FluidStack , boolean ) } .
*
* @param world the world that the fluid will be placed in
* @param pos the location that the fluid will be placed
* /
public static void destroyBlockOnFluidPlacement ( World world , BlockPos pos )
{
if ( ! world . isRemote )
{
2019-05-23 23:02:15 +00:00
BlockState destBlockState = world . getBlockState ( pos ) ;
2017-05-02 00:25:45 +00:00
Material destMaterial = destBlockState . getMaterial ( ) ;
boolean isDestNonSolid = ! destMaterial . isSolid ( ) ;
2018-09-21 06:50:50 +00:00
boolean isDestReplaceable = false ; //TODO: Needs BlockItemUseContext destBlockState.getBlock().isReplaceable(world, pos);
2017-05-02 00:25:45 +00:00
if ( ( isDestNonSolid | | isDestReplaceable ) & & ! destMaterial . isLiquid ( ) )
2016-12-06 04:17:47 +00:00
{
2017-05-02 00:25:45 +00:00
world . destroyBlock ( pos , true ) ;
2016-12-06 04:17:47 +00:00
}
}
}
2017-03-11 05:06:40 +00:00
/ * *
* @param fluidStack contents used to fill the bucket .
* FluidStack is used instead of Fluid to preserve fluid NBT , the amount is ignored .
* @return a filled vanilla bucket or filled universal bucket .
* Returns empty itemStack if none of the enabled buckets can hold the fluid .
* /
@Nonnull
public static ItemStack getFilledBucket ( @Nonnull FluidStack fluidStack )
2019-08-29 01:01:11 +00:00
{
2017-03-11 05:06:40 +00:00
Fluid fluid = fluidStack . getFluid ( ) ;
2019-08-29 01:01:11 +00:00
if ( ! fluidStack . hasTag ( ) | | fluidStack . getTag ( ) . isEmpty ( ) )
2017-03-11 05:06:40 +00:00
{
2019-08-29 01:01:11 +00:00
if ( fluid = = Fluids . WATER )
2017-03-11 05:06:40 +00:00
{
return new ItemStack ( Items . WATER_BUCKET ) ;
}
2019-08-29 01:01:11 +00:00
else if ( fluid = = Fluids . LAVA )
2017-03-11 05:06:40 +00:00
{
return new ItemStack ( Items . LAVA_BUCKET ) ;
}
2019-08-29 01:01:11 +00:00
else if ( fluid . getRegistryName ( ) . equals ( new ResourceLocation ( " milk " ) ) )
2017-03-11 05:06:40 +00:00
{
return new ItemStack ( Items . MILK_BUCKET ) ;
}
}
2019-08-29 01:01:11 +00:00
return fluid . getAttributes ( ) . getBucket ( fluidStack ) ;
2017-03-11 05:06:40 +00:00
}
2016-01-02 13:38:18 +00:00
}