diff --git a/patches/minecraft/net/minecraft/item/ItemBucket.java.patch b/patches/minecraft/net/minecraft/item/ItemBucket.java.patch index 7ae0a9410..22d87a8e8 100644 --- a/patches/minecraft/net/minecraft/item/ItemBucket.java.patch +++ b/patches/minecraft/net/minecraft/item/ItemBucket.java.patch @@ -9,3 +9,13 @@ if (raytraceresult == null) { +@@ -175,4 +177,9 @@ + } + } + } ++ ++ @Override ++ public net.minecraftforge.common.capabilities.ICapabilityProvider initCapabilities(ItemStack stack, net.minecraft.nbt.NBTTagCompound nbt) { ++ return new net.minecraftforge.fluids.capability.wrappers.FluidBucketWrapper(stack); ++ } + } diff --git a/patches/minecraft/net/minecraft/item/ItemBucketMilk.java.patch b/patches/minecraft/net/minecraft/item/ItemBucketMilk.java.patch index 1bb9a073a..78a8a6e0c 100644 --- a/patches/minecraft/net/minecraft/item/ItemBucketMilk.java.patch +++ b/patches/minecraft/net/minecraft/item/ItemBucketMilk.java.patch @@ -9,3 +9,13 @@ } if (p_77654_3_ instanceof EntityPlayer) +@@ -55,4 +55,9 @@ + p_77659_3_.func_184598_c(p_77659_4_); + return new ActionResult(EnumActionResult.SUCCESS, p_77659_1_); + } ++ ++ @Override ++ public net.minecraftforge.common.capabilities.ICapabilityProvider initCapabilities(ItemStack stack, net.minecraft.nbt.NBTTagCompound nbt) { ++ return new net.minecraftforge.fluids.capability.wrappers.FluidBucketWrapper(stack); ++ } + } diff --git a/src/main/java/net/minecraftforge/client/model/ModelDynBucket.java b/src/main/java/net/minecraftforge/client/model/ModelDynBucket.java index 90037fc10..40b3b8c67 100644 --- a/src/main/java/net/minecraftforge/client/model/ModelDynBucket.java +++ b/src/main/java/net/minecraftforge/client/model/ModelDynBucket.java @@ -31,8 +31,10 @@ import net.minecraftforge.fluids.Fluid; import net.minecraftforge.fluids.FluidContainerRegistry; import net.minecraftforge.fluids.FluidRegistry; import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.FluidUtil; import net.minecraftforge.fluids.IFluidContainerItem; +import net.minecraftforge.fluids.capability.IFluidHandler; import org.apache.commons.lang3.tuple.Pair; import com.google.common.base.Function; @@ -237,14 +239,7 @@ public final class ModelDynBucket implements IModel, IModelCustomData, IRetextur @Override public IBakedModel handleItemState(IBakedModel originalModel, ItemStack stack, World world, EntityLivingBase entity) { - FluidStack fluidStack = FluidContainerRegistry.getFluidForFilledItem(stack); - if (fluidStack == null) - { - if (stack.getItem() instanceof IFluidContainerItem) - { - fluidStack = ((IFluidContainerItem) stack.getItem()).getFluid(stack); - } - } + FluidStack fluidStack = FluidUtil.getFluidContained(stack); // not a fluid item apparently if (fluidStack == null) diff --git a/src/main/java/net/minecraftforge/client/model/ModelLoader.java b/src/main/java/net/minecraftforge/client/model/ModelLoader.java index 45f4809e3..47031564b 100644 --- a/src/main/java/net/minecraftforge/client/model/ModelLoader.java +++ b/src/main/java/net/minecraftforge/client/model/ModelLoader.java @@ -69,9 +69,10 @@ import net.minecraftforge.common.model.animation.IClip; import net.minecraftforge.common.property.IExtendedBlockState; import net.minecraftforge.common.property.Properties; import net.minecraftforge.fluids.Fluid; -import net.minecraftforge.fluids.FluidContainerRegistry; import net.minecraftforge.fluids.FluidRegistry; import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.FluidUtil; +import net.minecraftforge.fluids.capability.IFluidHandler; import net.minecraftforge.fml.common.FMLLog; import net.minecraftforge.fml.common.ProgressManager; import net.minecraftforge.fml.common.ProgressManager.ProgressBar; @@ -355,8 +356,9 @@ public final class ModelLoader extends ModelBakery { // can the milk be put into a bucket? Fluid milk = FluidRegistry.getFluid("milk"); - FluidStack milkStack = new FluidStack(milk, FluidContainerRegistry.BUCKET_VOLUME); - if(FluidContainerRegistry.getContainerCapacity(milkStack, new ItemStack(Items.BUCKET)) == FluidContainerRegistry.BUCKET_VOLUME) + FluidStack milkStack = new FluidStack(milk, Fluid.BUCKET_VOLUME); + IFluidHandler bucketHandler = FluidUtil.getFluidHandler(new ItemStack(Items.BUCKET)); + if (bucketHandler != null && bucketHandler.fill(milkStack, false) == Fluid.BUCKET_VOLUME) { setBucketModel(Items.MILK_BUCKET); } diff --git a/src/main/java/net/minecraftforge/common/ForgeModContainer.java b/src/main/java/net/minecraftforge/common/ForgeModContainer.java index 1c3aa8fed..e29559cff 100644 --- a/src/main/java/net/minecraftforge/common/ForgeModContainer.java +++ b/src/main/java/net/minecraftforge/common/ForgeModContainer.java @@ -36,6 +36,7 @@ import net.minecraftforge.common.config.Property; import net.minecraftforge.common.model.animation.CapabilityAnimation; import net.minecraftforge.common.network.ForgeNetworkHandler; import net.minecraftforge.fluids.FluidRegistry; +import net.minecraftforge.fluids.capability.CapabilityFluidHandler; import net.minecraftforge.items.CapabilityItemHandler; import net.minecraftforge.fluids.UniversalBucket; import net.minecraftforge.fml.common.registry.GameRegistry; @@ -372,6 +373,7 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC public void preInit(FMLPreInitializationEvent evt) { CapabilityItemHandler.register(); + CapabilityFluidHandler.register(); CapabilityAnimation.register(); MinecraftForge.EVENT_BUS.register(MinecraftForge.INTERNAL_HANDLER); ForgeChunkManager.captureConfig(evt.getModConfigurationDirectory()); diff --git a/src/main/java/net/minecraftforge/common/capabilities/CapabilityDispatcher.java b/src/main/java/net/minecraftforge/common/capabilities/CapabilityDispatcher.java index ae36f7f8f..0a08e3241 100644 --- a/src/main/java/net/minecraftforge/common/capabilities/CapabilityDispatcher.java +++ b/src/main/java/net/minecraftforge/common/capabilities/CapabilityDispatcher.java @@ -1,5 +1,6 @@ package net.minecraftforge.common.capabilities; +import javax.annotation.Nullable; import java.util.List; import java.util.Map; @@ -66,7 +67,7 @@ public final class CapabilityDispatcher implements INBTSerializable capability, EnumFacing facing) + public boolean hasCapability(Capability capability, @Nullable EnumFacing facing) { for (ICapabilityProvider cap : caps) { @@ -79,7 +80,7 @@ public final class CapabilityDispatcher implements INBTSerializable T getCapability(Capability capability, EnumFacing facing) + public T getCapability(Capability capability, @Nullable EnumFacing facing) { for (ICapabilityProvider cap : caps) { diff --git a/src/main/java/net/minecraftforge/common/capabilities/ICapabilityProvider.java b/src/main/java/net/minecraftforge/common/capabilities/ICapabilityProvider.java index 13f79d9a2..4adaa78e8 100644 --- a/src/main/java/net/minecraftforge/common/capabilities/ICapabilityProvider.java +++ b/src/main/java/net/minecraftforge/common/capabilities/ICapabilityProvider.java @@ -1,5 +1,7 @@ package net.minecraftforge.common.capabilities; +import javax.annotation.Nullable; + import net.minecraft.util.EnumFacing; public interface ICapabilityProvider @@ -19,7 +21,7 @@ public interface ICapabilityProvider * CAN BE NULL. Null is defined to represent 'internal' or 'self' * @return True if this object supports the capability. */ - boolean hasCapability(Capability capability, EnumFacing facing); + boolean hasCapability(Capability capability, @Nullable EnumFacing facing); /** * Retrieves the handler for the capability requested on the specific side. @@ -31,5 +33,5 @@ public interface ICapabilityProvider * CAN BE NULL. Null is defined to represent 'internal' or 'self' * @return True if this object supports the capability. */ - T getCapability(Capability capability, EnumFacing facing); + T getCapability(Capability capability, @Nullable EnumFacing facing); } diff --git a/src/main/java/net/minecraftforge/fluids/BlockFluidClassic.java b/src/main/java/net/minecraftforge/fluids/BlockFluidClassic.java index c2490aca6..4f85f06a0 100644 --- a/src/main/java/net/minecraftforge/fluids/BlockFluidClassic.java +++ b/src/main/java/net/minecraftforge/fluids/BlockFluidClassic.java @@ -24,7 +24,7 @@ public class BlockFluidClassic extends BlockFluidBase public BlockFluidClassic(Fluid fluid, Material material) { super(fluid, material); - stack = new FluidStack(fluid, FluidContainerRegistry.BUCKET_VOLUME); + stack = new FluidStack(fluid, Fluid.BUCKET_VOLUME); } public BlockFluidClassic setFluidStack(FluidStack stack) diff --git a/src/main/java/net/minecraftforge/fluids/BlockFluidFinite.java b/src/main/java/net/minecraftforge/fluids/BlockFluidFinite.java index 285899e9b..a4adbf4d5 100644 --- a/src/main/java/net/minecraftforge/fluids/BlockFluidFinite.java +++ b/src/main/java/net/minecraftforge/fluids/BlockFluidFinite.java @@ -226,7 +226,7 @@ public class BlockFluidFinite extends BlockFluidBase @Override public FluidStack drain(World world, BlockPos pos, boolean doDrain) { - final FluidStack fluidStack = new FluidStack(getFluid(), MathHelper.floor_float(getQuantaPercentage(world, pos) * FluidContainerRegistry.BUCKET_VOLUME)); + final FluidStack fluidStack = new FluidStack(getFluid(), MathHelper.floor_float(getQuantaPercentage(world, pos) * Fluid.BUCKET_VOLUME)); if (doDrain) { diff --git a/src/main/java/net/minecraftforge/fluids/Fluid.java b/src/main/java/net/minecraftforge/fluids/Fluid.java index 27eb5768a..28a110cde 100644 --- a/src/main/java/net/minecraftforge/fluids/Fluid.java +++ b/src/main/java/net/minecraftforge/fluids/Fluid.java @@ -30,6 +30,8 @@ import net.minecraft.item.EnumRarity; */ public class Fluid { + public static final int BUCKET_VOLUME = 1000; + /** The unique identification name for this fluid. */ protected final String fluidName; diff --git a/src/main/java/net/minecraftforge/fluids/FluidContainerRegistry.java b/src/main/java/net/minecraftforge/fluids/FluidContainerRegistry.java index 7a10e79e2..2278bdac7 100644 --- a/src/main/java/net/minecraftforge/fluids/FluidContainerRegistry.java +++ b/src/main/java/net/minecraftforge/fluids/FluidContainerRegistry.java @@ -17,10 +17,7 @@ import net.minecraftforge.common.MinecraftForge; * Register simple items that contain fluids here. Useful for buckets, bottles, and things that have * ID/metadata mappings. * - * For more complex items, use {@link IFluidContainerItem} instead. - * - * Deprecated: We will eventually be moving this ALL away from a registry and instead EVERYTHING will use IFluidContainerItem. - * We need to decide a way of swapping Items/Stacks. + * @deprecated This will be removed soon. Create an item like {@link net.minecraftforge.fluids.capability.ItemFluidContainer} */ @Deprecated public abstract class FluidContainerRegistry @@ -70,8 +67,11 @@ public abstract class FluidContainerRegistry private static Map filledContainerMap = Maps.newHashMap(); private static Set emptyContainers = Sets.newHashSet(); - public static final int BUCKET_VOLUME = 1000; + @Deprecated + public static final int BUCKET_VOLUME = Fluid.BUCKET_VOLUME; + @Deprecated public static final ItemStack EMPTY_BUCKET = new ItemStack(Items.BUCKET); + @Deprecated public static final ItemStack EMPTY_BOTTLE = new ItemStack(Items.GLASS_BOTTLE); private static final ItemStack NULL_EMPTYCONTAINER = new ItemStack(Items.BUCKET); @@ -348,6 +348,21 @@ public abstract class FluidContainerRegistry return container != null && getFluidForFilledItem(container) != null; } + public static boolean hasNullEmptyContainer(ItemStack container) + { + if (container == null) + { + return false; + } + + FluidContainerData data = containerFluidMap.get(new ContainerKey(container)); + if (data != null) + { + return data.emptyContainer == NULL_EMPTYCONTAINER; + } + return false; + } + public static FluidContainerData[] getRegisteredFluidContainerData() { return containerFluidMap.values().toArray(new FluidContainerData[containerFluidMap.size()]); diff --git a/src/main/java/net/minecraftforge/fluids/FluidStack.java b/src/main/java/net/minecraftforge/fluids/FluidStack.java index fbff1e5e0..700e0356b 100644 --- a/src/main/java/net/minecraftforge/fluids/FluidStack.java +++ b/src/main/java/net/minecraftforge/fluids/FluidStack.java @@ -174,12 +174,7 @@ public class FluidStack return false; } - if (other.getItem() instanceof IFluidContainerItem) - { - return isFluidEqual(((IFluidContainerItem) other.getItem()).getFluid(other)); - } - - return isFluidEqual(FluidContainerRegistry.getFluidForFilledItem(other)); + return isFluidEqual(FluidUtil.getFluidContained(other)); } @Override diff --git a/src/main/java/net/minecraftforge/fluids/FluidTank.java b/src/main/java/net/minecraftforge/fluids/FluidTank.java index 4969137da..ba2975efd 100644 --- a/src/main/java/net/minecraftforge/fluids/FluidTank.java +++ b/src/main/java/net/minecraftforge/fluids/FluidTank.java @@ -1,26 +1,34 @@ - package net.minecraftforge.fluids; +import javax.annotation.Nullable; + import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; +import net.minecraftforge.fluids.capability.FluidTankPropertiesWrapper; +import net.minecraftforge.fluids.capability.IFluidHandler; +import net.minecraftforge.fluids.capability.IFluidTankProperties; /** * Reference implementation of {@link IFluidTank}. Use/extend this or implement your own. */ -public class FluidTank implements IFluidTank +public class FluidTank implements IFluidTank, IFluidHandler { + @Nullable protected FluidStack fluid; protected int capacity; protected TileEntity tile; + protected boolean canFill = true; + protected boolean canDrain = true; + protected IFluidTankProperties[] tankProperties; public FluidTank(int capacity) { this(null, capacity); } - public FluidTank(FluidStack stack, int capacity) + public FluidTank(@Nullable FluidStack fluidStack, int capacity) { - this.fluid = stack; + this.fluid = fluidStack; this.capacity = capacity; } @@ -56,23 +64,19 @@ public class FluidTank implements IFluidTank return nbt; } - public void setFluid(FluidStack fluid) - { - this.fluid = fluid; - } - - public void setCapacity(int capacity) - { - this.capacity = capacity; - } - /* IFluidTank */ @Override + @Nullable public FluidStack getFluid() { return fluid; } + public void setFluid(@Nullable FluidStack fluid) + { + this.fluid = fluid; + } + @Override public int getFluidAmount() { @@ -89,16 +93,36 @@ public class FluidTank implements IFluidTank return capacity; } + public void setCapacity(int capacity) + { + this.capacity = capacity; + } + + public void setTileEntity(TileEntity tile) + { + this.tile = tile; + } + @Override public FluidTankInfo getInfo() { return new FluidTankInfo(this); } + @Override + public IFluidTankProperties[] getTankProperties() + { + if (this.tankProperties == null) + { + this.tankProperties = new IFluidTankProperties[] { new FluidTankPropertiesWrapper(this) }; + } + return this.tankProperties; + } + @Override public int fill(FluidStack resource, boolean doFill) { - if (resource == null) + if (resource == null || resource.amount <= 0 || !canFillFluidType(resource)) { return 0; } @@ -122,6 +146,8 @@ public class FluidTank implements IFluidTank { fluid = new FluidStack(resource, Math.min(capacity, resource.amount)); + onContentsChanged(); + if (tile != null) { FluidEvent.fireEvent(new FluidEvent.FluidFillingEvent(fluid, tile.getWorld(), tile.getPos(), this, fluid.amount)); @@ -145,6 +171,8 @@ public class FluidTank implements IFluidTank fluid.amount = capacity; } + onContentsChanged(); + if (tile != null) { FluidEvent.fireEvent(new FluidEvent.FluidFillingEvent(fluid, tile.getWorld(), tile.getPos(), this, filled)); @@ -152,10 +180,20 @@ public class FluidTank implements IFluidTank return filled; } + @Override + public FluidStack drain(FluidStack resource, boolean doDrain) + { + if (resource == null || !resource.isFluidEqual(getFluid())) + { + return null; + } + return drain(resource.amount, doDrain); + } + @Override public FluidStack drain(int maxDrain, boolean doDrain) { - if (fluid == null) + if (fluid == null || maxDrain <= 0 || !canDrainFluidType(fluid)) { return null; } @@ -175,6 +213,8 @@ public class FluidTank implements IFluidTank fluid = null; } + onContentsChanged(); + if (tile != null) { FluidEvent.fireEvent(new FluidEvent.FluidDrainingEvent(fluid, tile.getWorld(), tile.getPos(), this, drained)); @@ -182,4 +222,75 @@ public class FluidTank implements IFluidTank } return stack; } + + /** + * Whether this tank can be filled with {@link IFluidHandler} + * + * @see IFluidTankProperties#canFill() + */ + public boolean canFill() + { + return canFill; + } + + /** + * Whether this tank can be drained with {@link IFluidHandler} + * + * @see IFluidTankProperties#canDrain() + */ + public boolean canDrain() + { + return canDrain; + } + + /** + * Set whether this tank can be filled with {@link IFluidHandler} + * + * @see IFluidTankProperties#canFill() + */ + public void setCanFill(boolean canFill) + { + this.canFill = canFill; + } + + /** + * Set whether this tank can be drained with {@link IFluidHandler} + * + * @see IFluidTankProperties#canDrain() + */ + public void setCanDrain(boolean canDrain) + { + this.canDrain = canDrain; + } + + /** + * Returns true if the tank can be filled with this type of fluid. + * Used as a filter for fluid types. + * Does not consider the current contents or capacity of the tank, + * only whether it could ever fill with this type of fluid. + * + * @see IFluidTankProperties#canFillFluidType(FluidStack) + */ + public boolean canFillFluidType(FluidStack fluid) + { + return canFill(); + } + + /** + * Returns true if the tank can drain out this type of fluid. + * Used as a filter for fluid types. + * Does not consider the current contents or capacity of the tank, + * only whether it could ever drain out this type of fluid. + * + * @see IFluidTankProperties#canDrainFluidType(FluidStack) + */ + public boolean canDrainFluidType(FluidStack fluid) + { + return canDrain(); + } + + protected void onContentsChanged() + { + + } } diff --git a/src/main/java/net/minecraftforge/fluids/FluidTankInfo.java b/src/main/java/net/minecraftforge/fluids/FluidTankInfo.java index ad1fba81f..1e9ee696c 100644 --- a/src/main/java/net/minecraftforge/fluids/FluidTankInfo.java +++ b/src/main/java/net/minecraftforge/fluids/FluidTankInfo.java @@ -1,14 +1,17 @@ package net.minecraftforge.fluids; +import javax.annotation.Nullable; + /** * Wrapper class used to encapsulate information about an IFluidTank. */ public final class FluidTankInfo { + @Nullable public final FluidStack fluid; public final int capacity; - public FluidTankInfo(FluidStack fluid, int capacity) + public FluidTankInfo(@Nullable FluidStack fluid, int capacity) { this.fluid = fluid; this.capacity = capacity; diff --git a/src/main/java/net/minecraftforge/fluids/FluidUtil.java b/src/main/java/net/minecraftforge/fluids/FluidUtil.java index f5b000f58..7aded7024 100644 --- a/src/main/java/net/minecraftforge/fluids/FluidUtil.java +++ b/src/main/java/net/minecraftforge/fluids/FluidUtil.java @@ -1,118 +1,509 @@ package net.minecraftforge.fluids; +import javax.annotation.Nullable; + import net.minecraft.block.Block; +import net.minecraft.block.BlockLiquid; import net.minecraft.block.material.Material; +import net.minecraft.block.state.IBlockState; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.SoundEvents; +import net.minecraft.item.Item; import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.SoundCategory; import net.minecraft.util.SoundEvent; -import net.minecraftforge.common.ForgeModContainer; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraftforge.fluids.capability.CapabilityFluidHandler; +import net.minecraftforge.fluids.capability.IFluidHandler; +import net.minecraftforge.fluids.capability.wrappers.BlockLiquidWrapper; +import net.minecraftforge.fluids.capability.wrappers.FluidBlockWrapper; +import net.minecraftforge.fluids.capability.wrappers.FluidContainerItemWrapper; +import net.minecraftforge.fluids.capability.wrappers.FluidContainerRegistryWrapper; +import net.minecraftforge.fluids.capability.wrappers.FluidHandlerWrapper; import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.ItemHandlerHelper; +import net.minecraftforge.items.wrapper.InvWrapper; import net.minecraftforge.items.wrapper.PlayerMainInvWrapper; public class FluidUtil { - private FluidUtil() { } - /** Returns true if interaction was successful. */ - public static boolean interactWithTank(ItemStack stack, EntityPlayer player, IFluidHandler tank, EnumFacing side) + /** + * Used to handle the common case of a fluid item right-clicking on a fluid handler. + * First it tries to fill the container item from the fluid handler, + * if that action fails then it tries to drain the container item into the fluid handler. + * + * Returns true if interaction was successful. + * Returns false if interaction failed. + */ + public static boolean interactWithFluidHandler(ItemStack stack, IFluidHandler fluidHandler, EntityPlayer player) { - if (stack == null) + if (stack == null || fluidHandler == null || player == null) { - return true; + return false; } - ItemStack result; + IItemHandler playerInventory = new InvWrapper(player.inventory); + return tryFillContainerAndStow(stack, fluidHandler, playerInventory, Integer.MAX_VALUE, player) || + tryEmptyContainerAndStow(stack, fluidHandler, playerInventory, Integer.MAX_VALUE, player); + } - // regular bucket? - int slot = player.inventory.currentItem; - if ((result = FluidUtil.tryFillBucket(stack, tank, side, player)) != null || - (result = FluidUtil.tryEmptyBucket(stack, tank, side, player)) != null) + /** + * Fill a container from the given fluidSource. + * + * @param container The container to be filled. Will not be modified. + * @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. + * @return The filled container or null if the liquid couldn't be taken from the tank. + */ + public static ItemStack tryFillContainer(ItemStack container, IFluidHandler fluidSource, int maxAmount, @Nullable EntityPlayer player, boolean doFill) + { + container = container.copy(); // do not modify the input + container.stackSize = 1; + IFluidHandler containerFluidHandler = getFluidHandler(container); + if (containerFluidHandler != null) { - // "use up" the input item if the player is not in creative - if (!player.capabilities.isCreativeMode) + FluidStack simulatedTransfer = tryFluidTransfer(containerFluidHandler, fluidSource, maxAmount, false); + if (simulatedTransfer != null) { - player.inventory.decrStackSize(slot, 1); - ItemHandlerHelper.giveItemToPlayer(player, result, slot); - } - // send inventory updates to client - if (player.inventoryContainer != null) - { - player.inventoryContainer.detectAndSendChanges(); - } - return true; - } - // IFluidContainerItems - else - { - // copy of the original item for creative mode - ItemStack original = stack; // needed for how minecraft manages inventory with interaction - ItemStack copy = stack.copy(); - stack = stack.copy(); // needed so we don't affect itemstacks outside of this function - boolean changedBucket = false; - // convert to fluidcontainer-bucket if it's a regular empty bucket - if (ItemStack.areItemsEqual(stack, FluidContainerRegistry.EMPTY_BUCKET) && FluidRegistry.isUniversalBucketEnabled()) - { - // try using the forge fluid bucket if it's enabled - stack = new ItemStack(ForgeModContainer.getInstance().universalBucket, copy.stackSize); - changedBucket = true; - } - - // try filling an empty fluidcontainer or emptying a filled fluidcontainer - if (FluidUtil.tryFillFluidContainerItem(stack, tank, side, player) || - FluidUtil.tryEmptyFluidContainerItem(stack, tank, side, player)) - { - original.stackSize--; // this tells the player that the item he held was "used up" and the inventory syncing will then play the correct animation - if (player.capabilities.isCreativeMode) + if (doFill) { - // reset the stack that got modified - player.inventory.setInventorySlotContents(slot, copy); + tryFluidTransfer(containerFluidHandler, fluidSource, maxAmount, true); + if (player != null) + { + SoundEvent soundevent = simulatedTransfer.getFluid().getFillSound(simulatedTransfer); + player.playSound(soundevent, 1f, 1f); + } } else { - // we passed in multiple stacksize and it changed, that means the new items are in the inventory - // but we have to readjust the old ones back - if (changedBucket && stack.stackSize != copy.stackSize) - { - copy.stackSize = stack.stackSize; - // replace the previously changed buckets that were not used back - player.inventory.setInventorySlotContents(slot, copy); - } - // we have the new stack now, but since we changed it from its original we have to set the contents anew - else - { - // if the original stack was multiple, replace it - if (copy.stackSize > 1) - { - player.inventory.setInventorySlotContents(slot, stack); - } - // otherwise reinsert it into the inventory - else - { - player.inventory.setInventorySlotContents(slot, null); - ItemHandlerHelper.giveItemToPlayer(player, stack, slot); - } - } + containerFluidHandler.fill(simulatedTransfer, true); } - // send inventory updates to client - if (player.inventoryContainer != null) + return container; + } + } + return null; + } + + /** + * Takes a filled container and tries to empty it into the given tank. + * + * @param container The filled container. Will not be modified. + * @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. + * @return The empty container if successful, null if the fluid handler couldn't be filled. + * NOTE The empty container will have a stackSize of 0 when a filled container is consumable, + * i.e. it has a "null" empty container but has successfully been emptied. + */ + @Nullable + public static ItemStack tryEmptyContainer(ItemStack container, IFluidHandler fluidDestination, int maxAmount, @Nullable EntityPlayer player, boolean doDrain) + { + container = container.copy(); // do not modify the input + container.stackSize = 1; + IFluidHandler containerFluidHandler = getFluidHandler(container); + if (containerFluidHandler != null) + { + FluidStack simulatedTransfer = tryFluidTransfer(fluidDestination, containerFluidHandler, maxAmount, false); + if (simulatedTransfer != null) + { + if (doDrain) { - player.inventoryContainer.detectAndSendChanges(); + tryFluidTransfer(fluidDestination, containerFluidHandler, maxAmount, true); + if (player != null) + { + SoundEvent soundevent = simulatedTransfer.getFluid().getEmptySound(simulatedTransfer); + player.playSound(soundevent, 1f, 1f); + } } + else + { + containerFluidHandler.drain(simulatedTransfer, true); + } + return container; + } + } + return null; + } + + /** + * 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. This stack WILL be modified on success. + * @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. + * @return True if the container was filled successfully and stowed, false otherwise. + */ + public static boolean tryFillContainerAndStow(ItemStack container, IFluidHandler fluidSource, IItemHandler inventory, int maxAmount, @Nullable EntityPlayer player) + { + if (container == null || container.stackSize < 1) + { + return false; + } + + if (player != null && player.capabilities.isCreativeMode) + { + ItemStack filledReal = tryFillContainer(container, fluidSource, maxAmount, player, true); + if (filledReal != null) + { return true; } } + else if (container.stackSize == 1) // don't need to stow anything, just fill and edit the container stack + { + ItemStack filledReal = tryFillContainer(container, fluidSource, maxAmount, player, true); + if (filledReal != null) + { + container.setItem(filledReal.getItem()); + container.setTagCompound(filledReal.getTagCompound()); + container.setItemDamage(filledReal.getItemDamage()); + return true; + } + } + else + { + ItemStack filledSimulated = tryFillContainer(container, fluidSource, maxAmount, player, false); + if (filledSimulated != null) + { + // check if we can give the itemStack to the inventory + ItemStack remainder = ItemHandlerHelper.insertItemStacked(inventory, filledSimulated, true); + if (remainder == null || player != null) + { + ItemStack filledReal = tryFillContainer(container, fluidSource, maxAmount, player, true); + remainder = ItemHandlerHelper.insertItemStacked(inventory, filledReal, false); + + // give it to the player or drop it at their feet + if (remainder != null && player != null) + { + ItemHandlerHelper.giveItemToPlayer(player, remainder); + } + + container.stackSize--; + return true; + } + } + } return false; } + /** + * 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. + * + * @param container The Fluid Container Itemstack to fill. This stack WILL be modified on success. + * @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. + * @return True if the container was filled successfully and stowed, false otherwise. + */ + public static boolean tryEmptyContainerAndStow(ItemStack container, IFluidHandler fluidSource, IItemHandler inventory, int maxAmount, @Nullable EntityPlayer player) + { + if (container == null || container.stackSize < 1) + { + return false; + } + + if (player != null && player.capabilities.isCreativeMode) + { + ItemStack emptiedReal = tryEmptyContainer(container, fluidSource, maxAmount, player, true); + if (emptiedReal != null) + { + return true; + } + } + else if (container.stackSize == 1) // don't need to stow anything, just fill and edit the container stack + { + ItemStack emptiedReal = tryEmptyContainer(container, fluidSource, maxAmount, player, true); + if (emptiedReal != null) + { + if (emptiedReal.stackSize <= 0) + { + container.stackSize--; + } + else + { + container.setItem(emptiedReal.getItem()); + container.setTagCompound(emptiedReal.getTagCompound()); + container.setItemDamage(emptiedReal.getItemDamage()); + } + return true; + } + } + else + { + ItemStack emptiedSimulated = tryEmptyContainer(container, fluidSource, maxAmount, player, false); + if (emptiedSimulated != null) + { + if (emptiedSimulated.stackSize <= 0) + { + tryEmptyContainer(container, fluidSource, maxAmount, player, true); + container.stackSize--; + return true; + } + else + { + // check if we can give the itemStack to the inventory + ItemStack remainder = ItemHandlerHelper.insertItemStacked(inventory, emptiedSimulated, true); + if (remainder == null || player != null) + { + ItemStack emptiedReal = tryEmptyContainer(container, fluidSource, maxAmount, player, true); + remainder = ItemHandlerHelper.insertItemStacked(inventory, emptiedReal, false); + + // give it to the player or drop it at their feet + if (remainder != null && player != null) + { + ItemHandlerHelper.giveItemToPlayer(player, remainder); + } + + container.stackSize--; + return true; + } + } + } + } + + return false; + } + + /** + * Fill a destination fluid handler from a source fluid handler. + * + * @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. + */ + @Nullable + public static FluidStack tryFluidTransfer(IFluidHandler fluidDestination, IFluidHandler fluidSource, int maxAmount, boolean doTransfer) + { + FluidStack drainable = fluidSource.drain(maxAmount, false); + if (drainable != null && drainable.amount > 0) + { + int fillableAmount = fluidDestination.fill(drainable, false); + if (fillableAmount > 0) + { + FluidStack drained = fluidSource.drain(fillableAmount, doTransfer); + if (drained != null) + { + drained.amount = fluidDestination.fill(drained, doTransfer); + return drained; + } + } + } + return null; + } + + /** + * Helper method to get an IFluidHandler for an itemStack. + * + * The itemStack passed in here WILL be modified, the IFluidHandler acts on it directly. + * + * Note that the itemStack MUST have a stackSize of 1 if you want to fill or drain it. + * You can't fill or drain a whole stack at once, if you do then liquid is multiplied or destroyed. + * + * Vanilla buckets will be converted to universal buckets if they are enabled. + * + * Returns null if the itemStack passed in does not have a fluid handler. + */ + @Nullable + public static IFluidHandler getFluidHandler(ItemStack itemStack) + { + if (itemStack == null) + { + return null; + } + + // check for capability + if (itemStack.hasCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, null)) + { + return itemStack.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, null); + } + + // legacy container handling + Item item = itemStack.getItem(); + if (item instanceof IFluidContainerItem) + { + return new FluidContainerItemWrapper((IFluidContainerItem) item, itemStack); + } + else if (FluidContainerRegistry.isContainer(itemStack)) + { + return new FluidContainerRegistryWrapper(itemStack); + } + else + { + return null; + } + } + + /** + * Helper method to get the fluid contained in an itemStack + */ + @Nullable + public static FluidStack getFluidContained(ItemStack container) + { + if (container != null) + { + container = container.copy(); + container.stackSize = 1; + IFluidHandler fluidHandler = FluidUtil.getFluidHandler(container); + if (fluidHandler != null) + { + return fluidHandler.drain(Integer.MAX_VALUE, false); + } + } + return null; + } + + /** + * Helper method to get an IFluidHandler for at a block position. + * + * Returns null if there is no valid fluid handler. + */ + @Nullable + public static IFluidHandler getFluidHandler(World world, BlockPos blockPos, @Nullable EnumFacing side) + { + IBlockState state = world.getBlockState(blockPos); + Block block = state.getBlock(); + + if (block.hasTileEntity(state)) + { + TileEntity tileEntity = world.getTileEntity(blockPos); + if (tileEntity != null && tileEntity.hasCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, side)) + { + return tileEntity.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, side); + } + } + else if (block instanceof IFluidBlock) + { + return new FluidBlockWrapper((IFluidBlock) block, world, blockPos); + } + else if (block instanceof BlockLiquid) + { + return new BlockLiquidWrapper((BlockLiquid) block, world, blockPos); + } + + return null; + } + + /** + * Tries to place a fluid in the world in block form. + * Makes a fluid emptying sound when successful. + * Checks if water-like fluids should vaporize like in the nether. + * + * Modeled after {@link net.minecraft.item.ItemBucket#tryPlaceContainedLiquid(EntityPlayer, World, BlockPos)} + * + * @param player Player who places the fluid. May be null for blocks like dispensers. + * @param worldIn World to place the fluid in + * @param fluid The fluid to place. + * @param pos The position in the world to place the fluid block + * @return true if successful + */ + public static boolean tryPlaceFluid(@Nullable EntityPlayer player, World worldIn, FluidStack fluid, BlockPos pos) + { + if (worldIn == null || fluid == null || fluid.getFluid() == null || pos == null) + { + return false; + } + + // check that we can place the fluid at the destination + IBlockState destBlockState = worldIn.getBlockState(pos); + Material destMaterial = destBlockState.getMaterial(); + boolean isDestNonSolid = !destMaterial.isSolid(); + boolean isDestReplaceable = destBlockState.getBlock().isReplaceable(worldIn, pos); + if (!worldIn.isAirBlock(pos) && !isDestNonSolid && !isDestReplaceable) + { + return false; // Non-air, solid, unreplacable block. We can't put fluid here. + } + + IBlockState fluidBlockState = fluid.getFluid().getBlock().getDefaultState(); + + if (worldIn.provider.doesWaterVaporize() && fluidBlockState.getMaterial() == Material.WATER) + { + worldIn.playSound(player, pos, SoundEvents.BLOCK_FIRE_EXTINGUISH, SoundCategory.BLOCKS, 0.5F, 2.6F + (worldIn.rand.nextFloat() - worldIn.rand.nextFloat()) * 0.8F); + + for (int l = 0; l < 8; ++l) + { + worldIn.spawnParticle(EnumParticleTypes.SMOKE_LARGE, (double) pos.getX() + Math.random(), (double) pos.getY() + Math.random(), (double) pos.getZ() + Math.random(), 0.0D, 0.0D, 0.0D); + } + } + else + { + if (!worldIn.isRemote && (isDestNonSolid || isDestReplaceable) && !destMaterial.isLiquid()) + { + worldIn.destroyBlock(pos, true); + } + + SoundEvent soundevent = fluid.getFluid().getEmptySound(worldIn, pos); + worldIn.playSound(player, pos, soundevent, SoundCategory.BLOCKS, 1.0F, 1.0F); + + worldIn.setBlockState(pos, fluidBlockState, 11); + } + return true; + } + + /** + * Attempts to pick up a fluid in the world and put it in an empty container item. + * + * @param emptyContainer The empty container to fill. Will not be modified. + * @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. + * @return a filled container if it was successful. returns null on failure. + */ + @Nullable + public static ItemStack tryPickUpFluid(ItemStack emptyContainer, @Nullable EntityPlayer playerIn, World worldIn, BlockPos pos, EnumFacing side) + { + if (emptyContainer == null || worldIn == null || pos == null) { + return null; + } + + IFluidHandler targetFluidHandler = FluidUtil.getFluidHandler(worldIn, pos, side); + if (targetFluidHandler != null) + { + return FluidUtil.tryFillContainer(emptyContainer, targetFluidHandler, Integer.MAX_VALUE, playerIn, true); + } + return null; + } + + /** + * Returns true if interaction was successful. + * @deprecated use {@link #interactWithFluidHandler(ItemStack, IFluidHandler, EntityPlayer)} + */ @Deprecated - public static ItemStack tryFillBucket(ItemStack bucket, IFluidHandler tank, EnumFacing side) + public static boolean interactWithTank(ItemStack stack, EntityPlayer player, net.minecraftforge.fluids.IFluidHandler tank, EnumFacing side) + { + IFluidHandler fluidHandler = new FluidHandlerWrapper(tank, side); + return interactWithFluidHandler(stack, fluidHandler, player); + } + + /** + * @deprecated use {@link #tryFillContainer(ItemStack, IFluidHandler, int, EntityPlayer, boolean)} + */ + @Deprecated + public static ItemStack tryFillBucket(ItemStack bucket, net.minecraftforge.fluids.IFluidHandler tank, EnumFacing side) { return tryFillBucket(bucket, tank, side, null); } @@ -120,47 +511,24 @@ public class FluidUtil /** * Fill an empty bucket from the given tank. Uses the FluidContainerRegistry. * - * @param bucket The empty bucket + * @param bucket The empty bucket. Will not be modified. * @param tank The tank to fill the bucket from * @param side Side to access the tank from * @return The filled bucket or null if the liquid couldn't be taken from the tank. + * @deprecated use {@link #tryFillContainer(ItemStack, IFluidHandler, int, EntityPlayer, boolean)} */ - public static ItemStack tryFillBucket(ItemStack bucket, IFluidHandler tank, EnumFacing side, EntityPlayer player) + @Deprecated + public static ItemStack tryFillBucket(ItemStack bucket, net.minecraftforge.fluids.IFluidHandler tank, EnumFacing side, EntityPlayer player) { - FluidTankInfo[] info = tank.getTankInfo(side); - // check for fluid in the tank - if (info == null || info.length == 0) - { - return null; - } - // check if we actually have an empty bucket - if (!FluidContainerRegistry.isEmptyContainer(bucket)) - { - return null; - } - // fluid in the tank - FluidStack inTank = info[0].fluid; - // drain one bucket if possible - FluidStack liquid = tank.drain(side, FluidContainerRegistry.getContainerCapacity(inTank, bucket), false); - if (liquid != null && liquid.amount > 0) - { - // play sound - if(player != null) - { - SoundEvent soundevent = liquid.getFluid().getFillSound(liquid); - player.playSound(soundevent, 1f, 1f); - } - - // success, return filled bucket - tank.drain(side, FluidContainerRegistry.getContainerCapacity(liquid, bucket), true); - return FluidContainerRegistry.fillFluidContainer(liquid, bucket); - } - - return null; + IFluidHandler newFluidHandler = new FluidHandlerWrapper(tank, side); + return tryFillContainer(bucket, newFluidHandler, Fluid.BUCKET_VOLUME, player, true); } + /** + * @deprecated use {@link #tryEmptyContainer(ItemStack, IFluidHandler, int, EntityPlayer, boolean)} + */ @Deprecated - public static ItemStack tryEmptyBucket(ItemStack bucket, IFluidHandler tank, EnumFacing side) + public static ItemStack tryEmptyBucket(ItemStack bucket, net.minecraftforge.fluids.IFluidHandler tank, EnumFacing side) { return tryEmptyBucket(bucket, tank, side, null); } @@ -171,39 +539,15 @@ public class FluidUtil * @param bucket The filled bucket * @param tank The tank to fill with the bucket * @param side Side to access the tank from - * @param player + * @param player Player for making the bucket drained sound. * @return The empty bucket if successful, null if the tank couldn't be filled. + * @deprecated use {@link #tryFillContainer(ItemStack, IFluidHandler, int, EntityPlayer, boolean)} */ - public static ItemStack tryEmptyBucket(ItemStack bucket, IFluidHandler tank, EnumFacing side, EntityPlayer player) + @Deprecated + public static ItemStack tryEmptyBucket(ItemStack bucket, net.minecraftforge.fluids.IFluidHandler tank, EnumFacing side, EntityPlayer player) { - // not a filled bucket - if (!FluidContainerRegistry.isFilledContainer(bucket)) - { - return null; - } - - // try filling the fluid from the bucket into the tank - FluidStack liquid = FluidContainerRegistry.getFluidForFilledItem(bucket); - if (tank.canFill(side, liquid.getFluid())) - { - // how much can we put into the tank? - int amount = tank.fill(side, liquid, false); - // not everything? - if (amount == liquid.amount) - { - // play sound - if(player != null) - { - SoundEvent soundevent = liquid.getFluid().getEmptySound(liquid); - player.playSound(soundevent, 1f, 1f); - } - // success, fully filled it into the tank, return empty bucket - tank.fill(side, liquid, true); - return FluidContainerRegistry.drainFluidContainer(bucket); - } - } - - return null; + IFluidHandler destination = new FluidHandlerWrapper(tank, side); + return tryEmptyContainer(bucket, destination, Fluid.BUCKET_VOLUME, player, true); } /** @@ -214,13 +558,19 @@ public class FluidUtil * @param side Side to access the tank from * @param player The player that tries to fill the bucket. Needed if the input itemstack has a stacksize > 1 to determine where the filled container goes. * @return True if the IFluidContainerItem was filled successfully, false otherwise. The passed container will have been modified to accommodate for anything done in this method. New Itemstacks might have been added to the players inventory. + * @deprecated use {@link #tryFillContainerAndStow(ItemStack, IFluidHandler, IItemHandler, int, EntityPlayer)} */ - public static boolean tryFillFluidContainerItem(ItemStack container, IFluidHandler tank, EnumFacing side, EntityPlayer player) + @Deprecated + public static boolean tryFillFluidContainerItem(ItemStack container, net.minecraftforge.fluids.IFluidHandler tank, EnumFacing side, EntityPlayer player) { return tryFillFluidContainerItem(container, tank, side, new PlayerMainInvWrapper(player.inventory), -1, player); } - public static boolean tryEmptyFluidContainerItem(ItemStack container, IFluidHandler tank, EnumFacing side, EntityPlayer player) + /** + * @deprecated use {@link #tryEmptyContainerAndStow(ItemStack, IFluidHandler, IItemHandler, int, EntityPlayer)} + */ + @Deprecated + public static boolean tryEmptyFluidContainerItem(ItemStack container, net.minecraftforge.fluids.IFluidHandler tank, EnumFacing side, EntityPlayer player) { return tryEmptyFluidContainerItem(container, tank, side, new PlayerMainInvWrapper(player.inventory), -1, player); } @@ -240,8 +590,10 @@ public class FluidUtil * @param max Maximum amount to take from the tank. Uses IFluidContainerItem capacity if <= 0 * @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. * @return True if the IFluidContainerItem was filled successfully, false otherwise. The passed container will have been modified to accommodate for anything done in this method. New Itemstacks might have been added to the players inventory. + * @deprecated use {@link #tryFillContainerAndStow(ItemStack, IFluidHandler, IItemHandler, int, EntityPlayer)} */ - public static boolean tryFillFluidContainerItem(ItemStack container, IFluidHandler tank, EnumFacing side, IItemHandler inventory, int max, EntityPlayer player) + @Deprecated + public static boolean tryFillFluidContainerItem(ItemStack container, net.minecraftforge.fluids.IFluidHandler tank, EnumFacing side, IItemHandler inventory, int max, @Nullable EntityPlayer player) { if (!(container.getItem() instanceof IFluidContainerItem)) { @@ -349,8 +701,10 @@ public class FluidUtil * @param max Maximum amount to take from the tank. Uses IFluidContainerItem capacity if <= 0 * @param player The player that gets the items the inventory can't take. Can be null, only used if the inventory cannot take the emptied stack. * @return True if the container successfully emptied at least 1 mb into the tank, false otherwise. The passed container itemstack will be modified to accommodate for the liquid transaction. + * @deprecated use {@link #tryEmptyContainerAndStow(ItemStack, IFluidHandler, IItemHandler, int, EntityPlayer)} */ - public static boolean tryEmptyFluidContainerItem(ItemStack container, IFluidHandler tank, EnumFacing side, IItemHandler inventory, int max, EntityPlayer player) + @Deprecated + public static boolean tryEmptyFluidContainerItem(ItemStack container, net.minecraftforge.fluids.IFluidHandler tank, EnumFacing side, IItemHandler inventory, int max, EntityPlayer player) { if (!(container.getItem() instanceof IFluidContainerItem)) { diff --git a/src/main/java/net/minecraftforge/fluids/IFluidContainerItem.java b/src/main/java/net/minecraftforge/fluids/IFluidContainerItem.java index 6ba66cca0..4f8354fdb 100644 --- a/src/main/java/net/minecraftforge/fluids/IFluidContainerItem.java +++ b/src/main/java/net/minecraftforge/fluids/IFluidContainerItem.java @@ -1,6 +1,7 @@ package net.minecraftforge.fluids; import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.capability.wrappers.FluidContainerItemWrapper; /** * Implement this interface on Item classes that support external manipulation of their internal @@ -10,7 +11,10 @@ import net.minecraft.item.ItemStack; * * NOTE: Use of NBT data on the containing ItemStack is encouraged. * + * @deprecated See {@link net.minecraftforge.fluids.capability.ItemFluidContainer} for a CapabilityProvider implementing the Capability {@link net.minecraftforge.fluids.capability.IFluidHandler} + * @see FluidContainerItemWrapper */ +@Deprecated public interface IFluidContainerItem { /** diff --git a/src/main/java/net/minecraftforge/fluids/IFluidHandler.java b/src/main/java/net/minecraftforge/fluids/IFluidHandler.java index a788328a8..43da42597 100644 --- a/src/main/java/net/minecraftforge/fluids/IFluidHandler.java +++ b/src/main/java/net/minecraftforge/fluids/IFluidHandler.java @@ -1,6 +1,7 @@ package net.minecraftforge.fluids; import net.minecraft.util.EnumFacing; +import net.minecraftforge.fluids.capability.wrappers.FluidHandlerWrapper; /** * Implement this interface on TileEntities which should handle fluids, generally storing them in @@ -8,7 +9,10 @@ import net.minecraft.util.EnumFacing; * * A reference implementation is provided {@link TileFluidHandler}. * + * @deprecated Use the Capability version {@link net.minecraftforge.fluids.capability.IFluidHandler}. + * @see FluidHandlerWrapper */ +@Deprecated public interface IFluidHandler { /** diff --git a/src/main/java/net/minecraftforge/fluids/IFluidTank.java b/src/main/java/net/minecraftforge/fluids/IFluidTank.java index fbf5d6bb1..1edd6cab1 100644 --- a/src/main/java/net/minecraftforge/fluids/IFluidTank.java +++ b/src/main/java/net/minecraftforge/fluids/IFluidTank.java @@ -1,5 +1,7 @@ package net.minecraftforge.fluids; +import javax.annotation.Nullable; + /** * A tank is the unit of interaction with Fluid inventories. * @@ -10,6 +12,7 @@ public interface IFluidTank /** * @return FluidStack representing the fluid in the tank, null if the tank is empty. */ + @Nullable FluidStack getFluid(); /** @@ -50,5 +53,6 @@ public interface IFluidTank * If false, the drain will only be simulated. * @return Amount of fluid that was removed from the tank. */ + @Nullable FluidStack drain(int maxDrain, boolean doDrain); } diff --git a/src/main/java/net/minecraftforge/fluids/ItemFluidContainer.java b/src/main/java/net/minecraftforge/fluids/ItemFluidContainer.java index 6127133e2..1c5832509 100644 --- a/src/main/java/net/minecraftforge/fluids/ItemFluidContainer.java +++ b/src/main/java/net/minecraftforge/fluids/ItemFluidContainer.java @@ -3,10 +3,14 @@ package net.minecraftforge.fluids; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.common.capabilities.ICapabilityProvider; +import net.minecraftforge.fluids.capability.wrappers.FluidContainerItemWrapper; /** * Reference implementation of {@link IFluidContainerItem}. Use/extend this or implement your own. + * @deprecated See {@link net.minecraftforge.fluids.capability.ItemFluidContainer} */ +@Deprecated public class ItemFluidContainer extends Item implements IFluidContainerItem { protected int capacity; @@ -153,4 +157,10 @@ public class ItemFluidContainer extends Item implements IFluidContainerItem } return stack; } + + @Override + public ICapabilityProvider initCapabilities(ItemStack stack, NBTTagCompound nbt) + { + return new FluidContainerItemWrapper(this, stack); + } } diff --git a/src/main/java/net/minecraftforge/fluids/TileFluidHandler.java b/src/main/java/net/minecraftforge/fluids/TileFluidHandler.java index d93d6693d..e240ce566 100644 --- a/src/main/java/net/minecraftforge/fluids/TileFluidHandler.java +++ b/src/main/java/net/minecraftforge/fluids/TileFluidHandler.java @@ -1,16 +1,23 @@ package net.minecraftforge.fluids; +import javax.annotation.Nullable; + import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.fluids.capability.CapabilityFluidHandler; +import net.minecraftforge.fluids.capability.wrappers.FluidHandlerWrapper; /** * Reference Tile Entity implementation of {@link IFluidHandler}. Use/extend this or write your own. + * @deprecated see {@link net.minecraftforge.fluids.capability.TileFluidHandler} */ +@Deprecated public class TileFluidHandler extends TileEntity implements IFluidHandler { - protected FluidTank tank = new FluidTank(FluidContainerRegistry.BUCKET_VOLUME); + protected FluidTank tank = new FluidTank(Fluid.BUCKET_VOLUME); @Override public void readFromNBT(NBTTagCompound tag) @@ -67,4 +74,20 @@ public class TileFluidHandler extends TileEntity implements IFluidHandler { return new FluidTankInfo[] { tank.getInfo() }; } + + @Override + public boolean hasCapability(Capability capability, @Nullable EnumFacing facing) + { + return capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY || super.hasCapability(capability, facing); + } + + @Override + public T getCapability(Capability capability, @Nullable EnumFacing facing) + { + if (capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) + { + return CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY.cast(new FluidHandlerWrapper(this, facing)); + } + return super.getCapability(capability, facing); + } } diff --git a/src/main/java/net/minecraftforge/fluids/UniversalBucket.java b/src/main/java/net/minecraftforge/fluids/UniversalBucket.java index 337550a2f..4e646edb8 100644 --- a/src/main/java/net/minecraftforge/fluids/UniversalBucket.java +++ b/src/main/java/net/minecraftforge/fluids/UniversalBucket.java @@ -2,11 +2,9 @@ package net.minecraftforge.fluids; import net.minecraft.block.Block; import net.minecraft.block.material.Material; -import net.minecraft.block.state.IBlockState; import net.minecraft.creativetab.CreativeTabs; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.Blocks; -import net.minecraft.init.SoundEvents; +import net.minecraft.init.Items; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; @@ -16,7 +14,9 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.text.translation.I18n; import net.minecraft.world.World; +import net.minecraftforge.common.capabilities.ICapabilityProvider; import net.minecraftforge.event.entity.player.FillBucketEvent; +import net.minecraftforge.fluids.capability.wrappers.FluidBucketWrapper; import net.minecraftforge.fml.common.eventhandler.Event; import net.minecraftforge.fml.common.eventhandler.EventPriority; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; @@ -38,7 +38,7 @@ public class UniversalBucket extends Item implements IFluidContainerItem public UniversalBucket() { - this(FluidContainerRegistry.BUCKET_VOLUME, FluidContainerRegistry.EMPTY_BUCKET, false); + this(Fluid.BUCKET_VOLUME, new ItemStack(Items.BUCKET), false); } /** @@ -125,7 +125,7 @@ public class UniversalBucket extends Item implements IFluidContainerItem if (player.canPlayerEdit(targetPos, mop.sideHit, itemstack)) { // try placing liquid - if (this.tryPlaceFluid(player, player.getEntityWorld(), fluidStack.getFluid().getBlock(), targetPos) + if (FluidUtil.tryPlaceFluid(player, player.getEntityWorld(), fluidStack, targetPos) && !player.capabilities.isCreativeMode) { // success! @@ -157,64 +157,20 @@ public class UniversalBucket extends Item implements IFluidContainerItem @Deprecated public boolean tryPlaceFluid(Block block, World worldIn, BlockPos pos) { - return tryPlaceFluid(null, worldIn, block, pos); - } - - private boolean tryPlaceFluid(EntityPlayer player, World worldIn, Block block, BlockPos pos) - { - if (block == null) + if (block instanceof IFluidBlock) { - return false; + IFluidBlock fluidBlock = (IFluidBlock) block; + return FluidUtil.tryPlaceFluid(null, worldIn, new FluidStack(fluidBlock.getFluid(), Fluid.BUCKET_VOLUME), pos); } - if(worldIn == null && player != null) + else if (block.getDefaultState().getMaterial() == Material.WATER) { - worldIn = player.getEntityWorld(); + FluidUtil.tryPlaceFluid(null, worldIn, new FluidStack(FluidRegistry.WATER, Fluid.BUCKET_VOLUME), pos); } - - Material material = worldIn.getBlockState(pos).getMaterial(); - boolean isSolid = material.isSolid(); - - // can only place in air or non-solid blocks - if (!worldIn.isAirBlock(pos) && isSolid) + else if (block.getDefaultState().getMaterial() == Material.LAVA) { - return false; + FluidUtil.tryPlaceFluid(null, worldIn, new FluidStack(FluidRegistry.LAVA, Fluid.BUCKET_VOLUME), pos); } - - // water goes poof? - if (worldIn.provider.doesWaterVaporize() && (block == Blocks.FLOWING_WATER || block == Blocks.WATER)) - { - int i = pos.getX(); - int j = pos.getY(); - int k = pos.getZ(); - worldIn.playSound(null, pos, SoundEvents.BLOCK_FIRE_EXTINGUISH, SoundCategory.BLOCKS, 0.5F, - 2.6F + (worldIn.rand.nextFloat() - worldIn.rand.nextFloat()) * 0.8F); - - for (int l = 0; l < 8; ++l) - { - worldIn.spawnParticle(EnumParticleTypes.SMOKE_LARGE, - (double) i + Math.random(), - (double) j + Math.random(), (double) k + Math.random(), 0.0D, 0.0D, 0.0D); - } - } - else - { - if (!worldIn.isRemote && !isSolid && !material.isLiquid()) - { - worldIn.destroyBlock(pos, true); - } - - if(player != null && block instanceof IFluidBlock) - { - Fluid fluid = ((IFluidBlock) block).getFluid(); - if(fluid != null) - { - worldIn.setBlockState(pos, block.getDefaultState(), 3); - SoundEvent soundevent = fluid.getEmptySound(worldIn, pos); - worldIn.playSound(player, pos, soundevent, SoundCategory.BLOCKS, 1.0F, 1.0F); - } - } - } - return true; + return false; } @SubscribeEvent(priority = EventPriority.LOW) // low priority so other mods can handle their stuff first @@ -227,57 +183,38 @@ public class UniversalBucket extends Item implements IFluidContainerItem } // not for us to handle - if (event.getEmptyBucket() == null || - !event.getEmptyBucket().isItemEqual(getEmpty()) || - (isNbtSensitive() && ItemStack.areItemStackTagsEqual(event.getEmptyBucket(), getEmpty()))) + ItemStack emptyBucket = event.getEmptyBucket(); + if (emptyBucket == null || + !emptyBucket.isItemEqual(getEmpty()) || + (isNbtSensitive() && ItemStack.areItemStackTagsEqual(emptyBucket, getEmpty()))) { return; } // needs to target a block - if (event.getTarget() == null || event.getTarget().typeOfHit != RayTraceResult.Type.BLOCK) + RayTraceResult target = event.getTarget(); + if (target == null || target.typeOfHit != RayTraceResult.Type.BLOCK) { return; } World world = event.getWorld(); - BlockPos pos = event.getTarget().getBlockPos(); - IBlockState state = world.getBlockState(pos); - // Note that water and lava are NOT an instance of IFluidBlock! They are therefore not handled by this code! - if (state.getBlock() instanceof IFluidBlock) + BlockPos pos = target.getBlockPos(); + + ItemStack singleBucket = emptyBucket.copy(); + singleBucket.stackSize = 1; + + ItemStack filledBucket = FluidUtil.tryPickUpFluid(singleBucket, event.getEntityPlayer(), world, pos, target.sideHit); + if (filledBucket != null) { - IFluidBlock fluidBlock = (IFluidBlock) state.getBlock(); - if (fluidBlock.canDrain(world, pos)) - { - FluidStack drained = fluidBlock.drain(world, pos, false); - // check if it fits exactly - if (drained != null && drained.amount == getCapacity()) - { - // check if the container accepts it - ItemStack filledBucket = new ItemStack(this); - int filled = this.fill(filledBucket, drained, false); - if (filled == drained.amount) - { - // actually transfer the fluid - drained = fluidBlock.drain(world, pos, true); - this.fill(filledBucket, drained, true); - - // set it as the result - event.setResult(Event.Result.ALLOW); - event.setFilledBucket(filledBucket); - - // sound! - SoundEvent soundevent = drained.getFluid().getFillSound(drained); - event.getEntityPlayer().playSound(soundevent, 1.0F, 1.0F); - } - else - { - // cancel event, otherwise the vanilla minecraft ItemBucket would - // convert it into a water/lava bucket depending on the blocks material - event.setCanceled(true); - } - } - } + event.setResult(Event.Result.ALLOW); + event.setFilledBucket(filledBucket); + } + else + { + // cancel event, otherwise the vanilla minecraft ItemBucket would + // convert it into a water/lava bucket depending on the blocks material + event.setCanceled(true); } } @@ -312,34 +249,66 @@ public class UniversalBucket extends Item implements IFluidContainerItem } // can only fill exact capacity - if (resource == null || resource.amount != getCapacity()) + if (resource == null || resource.amount < getCapacity()) { return 0; } + + // already contains fluid? + if (getFluid(container) != null) + { + return 0; + } + // registered in the registry? - if (!FluidRegistry.getBucketFluids().contains(resource.getFluid())) + if (FluidRegistry.getBucketFluids().contains(resource.getFluid())) { - return 0; - } - // fill the container - if (doFill) - { - NBTTagCompound tag = container.getTagCompound(); - if (tag == null) + // fill the container + if (doFill) { - tag = new NBTTagCompound(); + NBTTagCompound tag = container.getTagCompound(); + if (tag == null) + { + tag = new NBTTagCompound(); + } + resource.writeToNBT(tag); + container.setTagCompound(tag); } - resource.writeToNBT(tag); - container.setTagCompound(tag); + return getCapacity(); } - return getCapacity(); + else if (resource.getFluid() == FluidRegistry.WATER) + { + if (doFill) + { + container.setItem(Items.WATER_BUCKET); + container.setTagCompound(null); + } + return getCapacity(); + } + else if (resource.getFluid() == FluidRegistry.LAVA) + { + if (doFill) + { + container.setItem(Items.LAVA_BUCKET); + container.setTagCompound(null); + } + return getCapacity(); + } + + return 0; } @Override public FluidStack drain(ItemStack container, int maxDrain, boolean doDrain) { + // has to be exactly 1, must be handled from the caller + if (container.stackSize != 1) + { + return null; + } + // can only drain everything at once - if (maxDrain < getCapacity()) + if (maxDrain < getCapacity(container)) { return null; } @@ -375,4 +344,10 @@ public class UniversalBucket extends Item implements IFluidContainerItem { return nbtSensitive; } + + @Override + public ICapabilityProvider initCapabilities(ItemStack stack, NBTTagCompound nbt) + { + return new FluidBucketWrapper(stack); + } } diff --git a/src/main/java/net/minecraftforge/fluids/capability/CapabilityFluidHandler.java b/src/main/java/net/minecraftforge/fluids/capability/CapabilityFluidHandler.java new file mode 100644 index 000000000..58c2732a5 --- /dev/null +++ b/src/main/java/net/minecraftforge/fluids/capability/CapabilityFluidHandler.java @@ -0,0 +1,65 @@ +package net.minecraftforge.fluids.capability; + +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.EnumFacing; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.CapabilityInject; +import net.minecraftforge.common.capabilities.CapabilityManager; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.FluidTank; +import net.minecraftforge.fluids.IFluidTank; + +import java.util.concurrent.Callable; + +public class CapabilityFluidHandler +{ + @CapabilityInject(IFluidHandler.class) + public static Capability FLUID_HANDLER_CAPABILITY = null; + + public static void register() + { + CapabilityManager.INSTANCE.register(IFluidHandler.class, new Capability.IStorage() + { + @Override + public NBTBase writeNBT(Capability capability, IFluidHandler instance, EnumFacing side) + { + if (!(instance instanceof IFluidTank)) + throw new RuntimeException("IFluidHandler instance does not implement IFluidTank"); + NBTTagCompound nbt = new NBTTagCompound(); + IFluidTank tank = (IFluidTank) instance; + FluidStack fluid = tank.getFluid(); + if (fluid != null) + { + fluid.writeToNBT(nbt); + } + else + { + nbt.setString("Empty", ""); + } + nbt.setInteger("Capacity", tank.getCapacity()); + return nbt; + } + + @Override + public void readNBT(Capability capability, IFluidHandler instance, EnumFacing side, NBTBase nbt) + { + if (!(instance instanceof IFluidTank)) + throw new RuntimeException("IFluidHandler instance is not instance of FluidTank"); + NBTTagCompound tags = (NBTTagCompound) nbt; + FluidTank tank = (FluidTank) instance; + tank.setCapacity(tags.getInteger("Capacity")); + tank.readFromNBT(tags); + } + }, new Callable() + { + @Override + public IFluidHandler call() throws Exception + { + return new FluidTank(Fluid.BUCKET_VOLUME); + } + }); + } + +} diff --git a/src/main/java/net/minecraftforge/fluids/capability/FluidTankProperties.java b/src/main/java/net/minecraftforge/fluids/capability/FluidTankProperties.java new file mode 100644 index 000000000..f2bf5da4f --- /dev/null +++ b/src/main/java/net/minecraftforge/fluids/capability/FluidTankProperties.java @@ -0,0 +1,79 @@ +package net.minecraftforge.fluids.capability; + +import javax.annotation.Nullable; + +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.FluidTankInfo; + +/** + * Basic implementation of {@link IFluidTankProperties}. + */ +public class FluidTankProperties implements IFluidTankProperties +{ + public static FluidTankProperties[] convert(FluidTankInfo[] fluidTankInfos) + { + FluidTankProperties[] properties = new FluidTankProperties[fluidTankInfos.length]; + for (int i = 0; i < fluidTankInfos.length; i++) + { + FluidTankInfo info = fluidTankInfos[i]; + properties[i] = new FluidTankProperties(info.fluid, info.capacity); + } + return properties; + } + + @Nullable + private final FluidStack contents; + private final int capacity; + private final boolean canFill; + private final boolean canDrain; + + public FluidTankProperties(@Nullable FluidStack contents, int capacity) + { + this(contents, capacity, true, true); + } + + public FluidTankProperties(@Nullable FluidStack contents, int capacity, boolean canFill, boolean canDrain) + { + this.contents = contents; + this.capacity = capacity; + this.canFill = canFill; + this.canDrain = canDrain; + } + + @Nullable + @Override + public FluidStack getContents() + { + return contents == null ? null : contents.copy(); + } + + @Override + public int getCapacity() + { + return capacity; + } + + @Override + public boolean canFill() + { + return canFill; + } + + @Override + public boolean canDrain() + { + return canDrain; + } + + @Override + public boolean canFillFluidType(FluidStack fluidStack) + { + return canFill; + } + + @Override + public boolean canDrainFluidType(FluidStack fluidStack) + { + return canDrain; + } +} diff --git a/src/main/java/net/minecraftforge/fluids/capability/FluidTankPropertiesWrapper.java b/src/main/java/net/minecraftforge/fluids/capability/FluidTankPropertiesWrapper.java new file mode 100644 index 000000000..16e0ea975 --- /dev/null +++ b/src/main/java/net/minecraftforge/fluids/capability/FluidTankPropertiesWrapper.java @@ -0,0 +1,57 @@ +package net.minecraftforge.fluids.capability; + +import javax.annotation.Nullable; + +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.FluidTank; + +/** + * Basic {@link IFluidTankProperties} wrapper for {@link FluidTank}. + */ +public class FluidTankPropertiesWrapper implements IFluidTankProperties +{ + protected final FluidTank tank; + + public FluidTankPropertiesWrapper(FluidTank tank) + { + this.tank = tank; + } + + @Nullable + @Override + public FluidStack getContents() + { + FluidStack contents = tank.getFluid(); + return contents == null ? null : contents.copy(); + } + + @Override + public int getCapacity() + { + return tank.getCapacity(); + } + + @Override + public boolean canFill() + { + return tank.canFill(); + } + + @Override + public boolean canDrain() + { + return tank.canDrain(); + } + + @Override + public boolean canFillFluidType(FluidStack fluidStack) + { + return tank.canFillFluidType(fluidStack); + } + + @Override + public boolean canDrainFluidType(FluidStack fluidStack) + { + return tank.canDrainFluidType(fluidStack); + } +} diff --git a/src/main/java/net/minecraftforge/fluids/capability/IFluidHandler.java b/src/main/java/net/minecraftforge/fluids/capability/IFluidHandler.java new file mode 100644 index 000000000..cfdc95b25 --- /dev/null +++ b/src/main/java/net/minecraftforge/fluids/capability/IFluidHandler.java @@ -0,0 +1,55 @@ +package net.minecraftforge.fluids.capability; + +import javax.annotation.Nullable; + +import net.minecraftforge.fluids.*; + +/** + * Implement this interface as a capability which should handle fluids, generally storing them in + * one or more internal {@link IFluidTank} objects. + *

+ * A reference implementation is provided {@link TileFluidHandler}. + */ +public interface IFluidHandler +{ + /** + * Returns an array of objects which represent the internal tanks. + * These objects cannot be used to manipulate the internal tanks. + * + * @return Properties for the relevant internal tanks. + */ + IFluidTankProperties[] getTankProperties(); + + /** + * Fills fluid into internal tanks, distribution is left entirely to the IFluidHandler. + * + * @param resource FluidStack representing the Fluid and maximum amount of fluid to be filled. + * @param doFill If false, fill will only be simulated. + * @return Amount of resource that was (or would have been, if simulated) filled. + */ + int fill(FluidStack resource, boolean doFill); + + /** + * Drains fluid out of internal tanks, distribution is left entirely to the IFluidHandler. + * + * @param resource FluidStack representing the Fluid and maximum amount of fluid to be drained. + * @param doDrain If false, drain will only be simulated. + * @return FluidStack representing the Fluid and amount that was (or would have been, if + * simulated) drained. + */ + @Nullable + FluidStack drain(FluidStack resource, boolean doDrain); + + /** + * Drains fluid out of internal tanks, distribution is left entirely to the IFluidHandler. + *

+ * This method is not Fluid-sensitive. + * + * @param maxDrain Maximum amount of fluid to drain. + * @param doDrain If false, drain will only be simulated. + * @return FluidStack representing the Fluid and amount that was (or would have been, if + * simulated) drained. + */ + @Nullable + FluidStack drain(int maxDrain, boolean doDrain); +} diff --git a/src/main/java/net/minecraftforge/fluids/capability/IFluidTankProperties.java b/src/main/java/net/minecraftforge/fluids/capability/IFluidTankProperties.java new file mode 100644 index 000000000..721c9f213 --- /dev/null +++ b/src/main/java/net/minecraftforge/fluids/capability/IFluidTankProperties.java @@ -0,0 +1,65 @@ +package net.minecraftforge.fluids.capability; + +import javax.annotation.Nullable; + +import net.minecraftforge.fluids.FluidStack; + +/** + * Simplified Read-only Information about the internals of an {@link IFluidHandler}. + * This is useful for displaying information, and as hints for interacting with it. + * These properties are constant and do not depend on the fluid contents (except the contents themselves, of course). + * + * The information here may not tell the full story of how the tank actually works, + * for real fluid transactions you must use {@link IFluidHandler} to simulate, check, and then interact. + * None of the information in these properties is required to successfully interact using a {@link IFluidHandler}. + */ +public interface IFluidTankProperties +{ + /** + * @return A copy of the fluid contents of this tank. May be null. + * To modify the contents, use {@link IFluidHandler}. + */ + @Nullable + FluidStack getContents(); + + /** + * @return The maximum amount of fluid this tank can hold, in millibuckets. + */ + int getCapacity(); + + /** + * Returns true if the tank can be filled at any time (even if it is currently full). + * It does not consider the contents or capacity of the tank. + * + * This value is constant. If the tank behavior is more complicated, returns true. + */ + boolean canFill(); + + /** + * Returns true if the tank can be drained at any time (even if it is currently empty). + * It does not consider the contents or capacity of the tank. + * + * This value is constant. If the tank behavior is more complicated, returns true. + */ + boolean canDrain(); + + /** + * Returns true if the tank can be filled with a specific type of fluid. + * Used as a filter for fluid types. + * + * Does not consider the current contents or capacity of the tank, + * only whether it could ever fill with this type of fluid. + * {@link FluidStack} is used here because fluid properties can depend on NBT, the amount is ignored. + */ + boolean canFillFluidType(FluidStack fluidStack); + + /** + * Returns true if the tank can drain out this a specific of fluid. + * Used as a filter for fluid types. + * + * Does not consider the current contents or capacity of the tank, + * only whether it could ever drain out this type of fluid. + * {@link FluidStack} is used here because fluid properties can depend on NBT, the amount is ignored. + */ + boolean canDrainFluidType(FluidStack fluidStack); +} diff --git a/src/main/java/net/minecraftforge/fluids/capability/ItemFluidContainer.java b/src/main/java/net/minecraftforge/fluids/capability/ItemFluidContainer.java new file mode 100644 index 000000000..fb4e9a0f5 --- /dev/null +++ b/src/main/java/net/minecraftforge/fluids/capability/ItemFluidContainer.java @@ -0,0 +1,33 @@ +package net.minecraftforge.fluids.capability; + +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.common.capabilities.ICapabilityProvider; +import net.minecraftforge.fluids.FluidContainerRegistry; +import net.minecraftforge.fluids.IFluidContainerItem; +import net.minecraftforge.fluids.capability.templates.FluidHandlerItemStack; + +/** + * A simple fluid container, to replace the functionality of {@link FluidContainerRegistry) and {@link IFluidContainerItem}. + * This fluid container may be set so that is can only completely filled or empty. (binary) + * It may also be set so that it gets consumed when it is drained. (consumable) + */ +public class ItemFluidContainer extends Item +{ + protected final int capacity; + + /** + * @param capacity The maximum capacity of this fluid container. + */ + public ItemFluidContainer(int capacity) + { + this.capacity = capacity; + } + + @Override + public ICapabilityProvider initCapabilities(ItemStack stack, NBTTagCompound nbt) + { + return new FluidHandlerItemStack(stack, capacity); + } +} diff --git a/src/main/java/net/minecraftforge/fluids/capability/TileFluidHandler.java b/src/main/java/net/minecraftforge/fluids/capability/TileFluidHandler.java new file mode 100644 index 000000000..35742c4ac --- /dev/null +++ b/src/main/java/net/minecraftforge/fluids/capability/TileFluidHandler.java @@ -0,0 +1,43 @@ +package net.minecraftforge.fluids.capability; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.EnumFacing; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidTank; + +public class TileFluidHandler extends TileEntity +{ + protected FluidTank tank = new FluidTank(Fluid.BUCKET_VOLUME); + + @Override + public void readFromNBT(NBTTagCompound tag) + { + super.readFromNBT(tag); + tank.readFromNBT(tag); + } + + @Override + public NBTTagCompound writeToNBT(NBTTagCompound tag) + { + tag = super.writeToNBT(tag); + tank.writeToNBT(tag); + return tag; + } + + @Override + public boolean hasCapability(Capability capability, EnumFacing facing) + { + return capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY || super.hasCapability(capability, facing); + } + + @SuppressWarnings("unchecked") + @Override + public T getCapability(Capability capability, EnumFacing facing) + { + if (capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) + return (T) tank; + return super.getCapability(capability, facing); + } +} diff --git a/src/main/java/net/minecraftforge/fluids/capability/templates/EmptyFluidHandler.java b/src/main/java/net/minecraftforge/fluids/capability/templates/EmptyFluidHandler.java new file mode 100644 index 000000000..f205b112c --- /dev/null +++ b/src/main/java/net/minecraftforge/fluids/capability/templates/EmptyFluidHandler.java @@ -0,0 +1,69 @@ +package net.minecraftforge.fluids.capability.templates; + +import javax.annotation.Nullable; + +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.FluidTankInfo; +import net.minecraftforge.fluids.capability.FluidTankProperties; +import net.minecraftforge.fluids.IFluidTank; +import net.minecraftforge.fluids.capability.IFluidTankProperties; +import net.minecraftforge.fluids.capability.IFluidHandler; + +public class EmptyFluidHandler implements IFluidHandler, IFluidTank +{ + public static final EmptyFluidHandler INSTANCE = new EmptyFluidHandler(); + public static final FluidTankInfo EMPTY_TANK_INFO = new FluidTankInfo(null, 0); + public static final IFluidTankProperties EMPTY_TANK_PROPERTIES = new FluidTankProperties(null, 0, false, false); + public static final IFluidTankProperties[] EMPTY_TANK_PROPERTIES_ARRAY = new IFluidTankProperties[] { EMPTY_TANK_PROPERTIES }; + + protected EmptyFluidHandler() {} + + @Override + public IFluidTankProperties[] getTankProperties() + { + return EMPTY_TANK_PROPERTIES_ARRAY; + } + + @Override + @Nullable + public FluidStack getFluid() + { + return null; + } + + @Override + public int getFluidAmount() + { + return 0; + } + + @Override + public int getCapacity() + { + return 0; + } + + @Override + public FluidTankInfo getInfo() + { + return EMPTY_TANK_INFO; + } + + @Override + public int fill(FluidStack resource, boolean doFill) + { + return 0; + } + + @Override + public FluidStack drain(FluidStack resource, boolean doDrain) + { + return null; + } + + @Override + public FluidStack drain(int maxDrain, boolean doDrain) + { + return null; + } +} diff --git a/src/main/java/net/minecraftforge/fluids/capability/templates/FluidHandlerConcatenate.java b/src/main/java/net/minecraftforge/fluids/capability/templates/FluidHandlerConcatenate.java new file mode 100644 index 000000000..08594d5db --- /dev/null +++ b/src/main/java/net/minecraftforge/fluids/capability/templates/FluidHandlerConcatenate.java @@ -0,0 +1,121 @@ +package net.minecraftforge.fluids.capability.templates; + +import com.google.common.collect.Lists; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.capability.IFluidTankProperties; +import net.minecraftforge.fluids.capability.IFluidHandler; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * FluidHandlerConcatenate is a template class for concatenating multiple handlers into one. + * If each tank is restricted to exactly one type of fluid, then use {@link FluidHandlerFluidMap} as it is more efficient. + */ +public class FluidHandlerConcatenate implements IFluidHandler +{ + protected final IFluidHandler[] subHandlers; + + public FluidHandlerConcatenate(IFluidHandler... subHandlers) + { + this.subHandlers = subHandlers; + } + + public FluidHandlerConcatenate(Collection subHandlers) + { + this.subHandlers = subHandlers.toArray(new IFluidHandler[subHandlers.size()]); + } + + @Override + public IFluidTankProperties[] getTankProperties() + { + List tanks = Lists.newArrayList(); + for (IFluidHandler handler : subHandlers) + { + Collections.addAll(tanks, handler.getTankProperties()); + } + return tanks.toArray(new IFluidTankProperties[tanks.size()]); + } + + @Override + public int fill(FluidStack resource, boolean doFill) + { + if (resource == null || resource.amount <= 0) + return 0; + + resource = resource.copy(); + + int totalFillAmount = 0; + for (IFluidHandler handler : subHandlers) + { + int fillAmount = handler.fill(resource, doFill); + totalFillAmount += fillAmount; + resource.amount -= fillAmount; + if (resource.amount <= 0) + break; + } + return totalFillAmount; + } + + @Override + public FluidStack drain(FluidStack resource, boolean doDrain) + { + if (resource == null || resource.amount <= 0) + return null; + + resource = resource.copy(); + + FluidStack totalDrained = null; + for (IFluidHandler handler : subHandlers) + { + FluidStack drain = handler.drain(resource, doDrain); + if (drain != null) + { + if (totalDrained == null) + totalDrained = drain; + else + totalDrained.amount += drain.amount; + + resource.amount -= drain.amount; + if (resource.amount <= 0) + break; + } + } + return totalDrained; + } + + @Override + public FluidStack drain(int maxDrain, boolean doDrain) + { + if (maxDrain == 0) + return null; + FluidStack totalDrained = null; + for (IFluidHandler handler : subHandlers) + { + if (totalDrained == null) + { + totalDrained = handler.drain(maxDrain, doDrain); + if (totalDrained != null) + { + maxDrain -= totalDrained.amount; + } + } + else + { + FluidStack copy = totalDrained.copy(); + copy.amount = maxDrain; + FluidStack drain = handler.drain(copy, doDrain); + if (drain != null) + { + totalDrained.amount += drain.amount; + maxDrain -= drain.amount; + } + } + + if (maxDrain <= 0) + break; + } + return totalDrained; + } +} diff --git a/src/main/java/net/minecraftforge/fluids/capability/templates/FluidHandlerFluidMap.java b/src/main/java/net/minecraftforge/fluids/capability/templates/FluidHandlerFluidMap.java new file mode 100644 index 000000000..e069a521f --- /dev/null +++ b/src/main/java/net/minecraftforge/fluids/capability/templates/FluidHandlerFluidMap.java @@ -0,0 +1,80 @@ +package net.minecraftforge.fluids.capability.templates; + +import com.google.common.collect.Lists; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.capability.IFluidTankProperties; +import net.minecraftforge.fluids.capability.IFluidHandler; + +import java.util.*; + +/** + * FluidHandlerFluidMap is a template class for concatenating multiple handlers into one, + * where each handler is associated with a different fluid. + */ +public class FluidHandlerFluidMap implements IFluidHandler +{ + protected final Map handlers; + + public FluidHandlerFluidMap() + { + // LinkedHashMap to ensure iteration order is consistent. + this(new LinkedHashMap()); + } + + public FluidHandlerFluidMap(Map handlers) + { + this.handlers = handlers; + } + + public FluidHandlerFluidMap addHandler(Fluid fluid, IFluidHandler handler) + { + handlers.put(fluid, handler); + return this; + } + + @Override + public IFluidTankProperties[] getTankProperties() + { + List tanks = Lists.newArrayList(); + for (IFluidHandler iFluidHandler : handlers.values()) + { + Collections.addAll(tanks, iFluidHandler.getTankProperties()); + } + return tanks.toArray(new IFluidTankProperties[tanks.size()]); + } + + @Override + public int fill(FluidStack resource, boolean doFill) + { + if (resource == null) + return 0; + IFluidHandler handler = handlers.get(resource.getFluid()); + if (handler == null) + return 0; + return handler.fill(resource, doFill); + } + + @Override + public FluidStack drain(FluidStack resource, boolean doDrain) + { + if (resource == null) + return null; + IFluidHandler handler = handlers.get(resource.getFluid()); + if (handler == null) + return null; + return handler.drain(resource, doDrain); + } + + @Override + public FluidStack drain(int maxDrain, boolean doDrain) + { + for (IFluidHandler handler : handlers.values()) + { + FluidStack drain = handler.drain(maxDrain, doDrain); + if (drain != null) + return drain; + } + return null; + } +} diff --git a/src/main/java/net/minecraftforge/fluids/capability/templates/FluidHandlerItemStack.java b/src/main/java/net/minecraftforge/fluids/capability/templates/FluidHandlerItemStack.java new file mode 100644 index 000000000..305aeac5f --- /dev/null +++ b/src/main/java/net/minecraftforge/fluids/capability/templates/FluidHandlerItemStack.java @@ -0,0 +1,229 @@ +package net.minecraftforge.fluids.capability.templates; + +import javax.annotation.Nullable; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.EnumFacing; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.ICapabilityProvider; +import net.minecraftforge.fluids.*; +import net.minecraftforge.fluids.capability.CapabilityFluidHandler; +import net.minecraftforge.fluids.capability.FluidTankProperties; +import net.minecraftforge.fluids.capability.IFluidHandler; +import net.minecraftforge.fluids.capability.IFluidTankProperties; + +/** + * FluidHandlerItemStack is a template capability provider for ItemStacks. + * Data is stored directly in the vanilla NBT, in the same way as the old deprecated {@link ItemFluidContainer}. + * + * This class allows an itemStack to contain any partial level of fluid up to its capacity, unlike {@link FluidHandlerItemStackSimple} + * + * Additional examples are provided to enable consumable fluid containers (see {@link Consumable}), + * fluid containers with different empty and full items (see {@link SwapEmpty}, + */ +public class FluidHandlerItemStack implements IFluidHandler, ICapabilityProvider +{ + public static final String FLUID_NBT_KEY = "Fluid"; + + protected final ItemStack container; + protected final int capacity; + + /** + * @param container The container itemStack, data is stored on it directly as NBT. + * @param capacity The maximum capacity of this fluid tank. + */ + public FluidHandlerItemStack(ItemStack container, int capacity) + { + this.container = container; + this.capacity = capacity; + } + + @Nullable + public FluidStack getFluid() + { + NBTTagCompound tagCompound = container.getTagCompound(); + if (tagCompound == null || !tagCompound.hasKey(FLUID_NBT_KEY)) + { + return null; + } + return FluidStack.loadFluidStackFromNBT(tagCompound.getCompoundTag(FLUID_NBT_KEY)); + } + + protected void setFluid(FluidStack fluid) + { + if (!container.hasTagCompound()) + { + container.setTagCompound(new NBTTagCompound()); + } + + NBTTagCompound fluidTag = new NBTTagCompound(); + fluid.writeToNBT(fluidTag); + container.getTagCompound().setTag(FLUID_NBT_KEY, fluidTag); + } + + @Override + public IFluidTankProperties[] getTankProperties() + { + return new FluidTankProperties[] { new FluidTankProperties(getFluid(), capacity) }; + } + + @Override + public int fill(FluidStack resource, boolean doFill) + { + if (container.stackSize != 1 || resource == null || resource.amount <= 0 || !canFillFluidType(resource)) + { + return 0; + } + + FluidStack contained = getFluid(); + if (contained == null) + { + int fillAmount = Math.min(capacity, resource.amount); + + if (doFill) + { + FluidStack filled = resource.copy(); + filled.amount = fillAmount; + setFluid(filled); + } + + return fillAmount; + } + else + { + if (contained.isFluidEqual(resource)) + { + int fillAmount = Math.min(capacity - contained.amount, resource.amount); + + if (doFill && fillAmount > 0) { + contained.amount += fillAmount; + setFluid(contained); + } + + return fillAmount; + } + + return 0; + } + } + + @Override + public FluidStack drain(FluidStack resource, boolean doDrain) + { + if (container.stackSize != 1 || resource == null || resource.amount <= 0 || !resource.isFluidEqual(getFluid())) + { + return null; + } + return drain(resource.amount, doDrain); + } + + @Override + public FluidStack drain(int maxDrain, boolean doDrain) + { + if (container.stackSize != 1 || maxDrain <= 0) + { + return null; + } + + FluidStack contained = getFluid(); + if (contained == null || contained.amount <= 0 || !canDrainFluidType(contained)) + { + return null; + } + + final int drainAmount = Math.min(contained.amount, maxDrain); + + FluidStack drained = contained.copy(); + drained.amount = drainAmount; + + if (doDrain) + { + contained.amount -= drainAmount; + if (contained.amount == 0) + { + setContainerToEmpty(); + } + else + { + setFluid(contained); + } + } + + return drained; + } + + public boolean canFillFluidType(FluidStack fluid) + { + return true; + } + + public boolean canDrainFluidType(FluidStack fluid) + { + return true; + } + + /** + * Override this method for special handling. + * Can be used to swap out the container's item for a different one with "container.setItem". + * Can be used to destroy the container with "container.stackSize--" + */ + protected void setContainerToEmpty() + { + container.getTagCompound().removeTag(FLUID_NBT_KEY); + } + + @Override + public boolean hasCapability(Capability capability, EnumFacing facing) + { + return capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY; + } + + @SuppressWarnings("unchecked") + @Override + public T getCapability(Capability capability, EnumFacing facing) + { + return capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY ? (T) this : null; + } + + /** + * Destroys the container item when it's emptied. + */ + public static class Consumable extends FluidHandlerItemStack + { + public Consumable(ItemStack container, int capacity) + { + super(container, capacity); + } + + @Override + protected void setContainerToEmpty() + { + super.setContainerToEmpty(); + container.stackSize--; + } + } + + /** + * Swaps the container item for a different one when it's emptied. + */ + public static class SwapEmpty extends FluidHandlerItemStack + { + protected final ItemStack emptyContainer; + + public SwapEmpty(ItemStack container, ItemStack emptyContainer, int capacity) + { + super(container, capacity); + this.emptyContainer = emptyContainer; + } + + @Override + protected void setContainerToEmpty() + { + super.setContainerToEmpty(); + container.setItem(emptyContainer.getItem()); + container.setTagCompound(emptyContainer.getTagCompound()); + container.setItemDamage(emptyContainer.getItemDamage()); + } + } +} diff --git a/src/main/java/net/minecraftforge/fluids/capability/templates/FluidHandlerItemStackSimple.java b/src/main/java/net/minecraftforge/fluids/capability/templates/FluidHandlerItemStackSimple.java new file mode 100644 index 000000000..46090ce32 --- /dev/null +++ b/src/main/java/net/minecraftforge/fluids/capability/templates/FluidHandlerItemStackSimple.java @@ -0,0 +1,206 @@ +package net.minecraftforge.fluids.capability.templates; + +import javax.annotation.Nullable; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.EnumFacing; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.ICapabilityProvider; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.capability.FluidTankProperties; +import net.minecraftforge.fluids.capability.IFluidTankProperties; +import net.minecraftforge.fluids.ItemFluidContainer; +import net.minecraftforge.fluids.capability.CapabilityFluidHandler; +import net.minecraftforge.fluids.capability.IFluidHandler; + +/** + * FluidHandlerItemStackSimple is a template capability provider for ItemStacks. + * Data is stored directly in the vanilla NBT, in the same way as the old deprecated {@link ItemFluidContainer}. + * + * This implementation only allows item containers to be fully filled or emptied, similar to vanilla buckets. + */ +public class FluidHandlerItemStackSimple implements IFluidHandler, ICapabilityProvider +{ + public static final String FLUID_NBT_KEY = "Fluid"; + + protected final ItemStack container; + protected final int capacity; + + /** + * @param container The container itemStack, data is stored on it directly as NBT. + * @param capacity The maximum capacity of this fluid tank. + */ + public FluidHandlerItemStackSimple(ItemStack container, int capacity) + { + this.container = container; + this.capacity = capacity; + } + + @Nullable + public FluidStack getFluid() + { + NBTTagCompound tagCompound = container.getTagCompound(); + if (tagCompound == null || !tagCompound.hasKey(FLUID_NBT_KEY)) + { + return null; + } + return FluidStack.loadFluidStackFromNBT(tagCompound.getCompoundTag(FLUID_NBT_KEY)); + } + + protected void setFluid(FluidStack fluid) + { + if (!container.hasTagCompound()) + { + container.setTagCompound(new NBTTagCompound()); + } + + NBTTagCompound fluidTag = new NBTTagCompound(); + fluid.writeToNBT(fluidTag); + container.getTagCompound().setTag(FLUID_NBT_KEY, fluidTag); + } + + @Override + public IFluidTankProperties[] getTankProperties() + { + return new IFluidTankProperties[] { new FluidTankProperties(getFluid(), capacity) }; + } + + @Override + public int fill(FluidStack resource, boolean doFill) + { + if (container.stackSize != 1 || resource == null || resource.amount <= 0 || !canFillFluidType(resource)) + { + return 0; + } + + FluidStack contained = getFluid(); + if (contained == null) + { + int fillAmount = Math.min(capacity, resource.amount); + if (fillAmount == capacity) { + if (doFill) { + FluidStack filled = resource.copy(); + filled.amount = fillAmount; + setFluid(filled); + } + + return fillAmount; + } + } + + return 0; + } + + @Override + public FluidStack drain(FluidStack resource, boolean doDrain) + { + if (container.stackSize != 1 || resource == null || resource.amount <= 0 || !resource.isFluidEqual(getFluid())) + { + return null; + } + return drain(resource.amount, doDrain); + } + + @Override + public FluidStack drain(int maxDrain, boolean doDrain) + { + if (container.stackSize != 1 || maxDrain <= 0) + { + return null; + } + + FluidStack contained = getFluid(); + if (contained == null || contained.amount <= 0 || !canDrainFluidType(contained)) + { + return null; + } + + final int drainAmount = Math.min(contained.amount, maxDrain); + if (drainAmount == capacity) { + FluidStack drained = contained.copy(); + + if (doDrain) { + setContainerToEmpty(); + } + + return drained; + } + + return null; + } + + public boolean canFillFluidType(FluidStack fluid) + { + return true; + } + + public boolean canDrainFluidType(FluidStack fluid) + { + return true; + } + + /** + * Override this method for special handling. + * Can be used to swap out the container's item for a different one with "container.setItem". + * Can be used to destroy the container with "container.stackSize--" + */ + protected void setContainerToEmpty() + { + container.getTagCompound().removeTag(FLUID_NBT_KEY); + } + + @Override + public boolean hasCapability(Capability capability, EnumFacing facing) + { + return capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY; + } + + @SuppressWarnings("unchecked") + @Override + public T getCapability(Capability capability, EnumFacing facing) + { + return capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY ? (T) this : null; + } + + /** + * Destroys the container item when it's emptied. + */ + public static class Consumable extends FluidHandlerItemStackSimple + { + public Consumable(ItemStack container, int capacity) + { + super(container, capacity); + } + + @Override + protected void setContainerToEmpty() + { + super.setContainerToEmpty(); + container.stackSize--; + } + } + + /** + * Swaps the container item for a different one when it's emptied. + */ + public static class SwapEmpty extends FluidHandlerItemStackSimple + { + protected final ItemStack emptyContainer; + + public SwapEmpty(ItemStack container, ItemStack emptyContainer, int capacity) + { + super(container, capacity); + this.emptyContainer = emptyContainer; + } + + @Override + protected void setContainerToEmpty() + { + super.setContainerToEmpty(); + container.setItem(emptyContainer.getItem()); + container.setTagCompound(emptyContainer.getTagCompound()); + container.setItemDamage(emptyContainer.getItemDamage()); + } + } +} diff --git a/src/main/java/net/minecraftforge/fluids/capability/wrappers/BlockLiquidWrapper.java b/src/main/java/net/minecraftforge/fluids/capability/wrappers/BlockLiquidWrapper.java new file mode 100644 index 000000000..cf2675fd2 --- /dev/null +++ b/src/main/java/net/minecraftforge/fluids/capability/wrappers/BlockLiquidWrapper.java @@ -0,0 +1,125 @@ +package net.minecraftforge.fluids.capability.wrappers; + +import javax.annotation.Nullable; + +import net.minecraft.block.BlockLiquid; +import net.minecraft.block.material.Material; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Blocks; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidRegistry; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.capability.FluidTankProperties; +import net.minecraftforge.fluids.capability.IFluidTankProperties; +import net.minecraftforge.fluids.capability.IFluidHandler; + +/** + * Wrapper to handle vanilla Water or Lava as an IFluidHandler. + * Methods are modeled after {@link net.minecraft.item.ItemBucket#onItemRightClick(ItemStack, World, EntityPlayer, EnumHand)} + */ +public class BlockLiquidWrapper implements IFluidHandler +{ + protected final BlockLiquid blockLiquid; + protected final World world; + protected final BlockPos blockPos; + + public BlockLiquidWrapper(BlockLiquid blockLiquid, World world, BlockPos blockPos) + { + this.blockLiquid = blockLiquid; + this.world = world; + this.blockPos = blockPos; + } + + @Override + public IFluidTankProperties[] getTankProperties() + { + FluidStack containedStack = null; + IBlockState blockState = world.getBlockState(blockPos); + if (blockState.getBlock() == blockLiquid) + { + containedStack = getStack(blockState); + } + return new FluidTankProperties[]{new FluidTankProperties(containedStack, Fluid.BUCKET_VOLUME, false, true)}; + } + + @Override + public int fill(FluidStack resource, boolean doFill) + { + return 0; + } + + @Nullable + @Override + public FluidStack drain(FluidStack resource, boolean doDrain) + { + if (resource == null || resource.amount < Fluid.BUCKET_VOLUME) + { + return null; + } + + IBlockState blockState = world.getBlockState(blockPos); + if (blockState.getBlock() == blockLiquid && blockState.getValue(BlockLiquid.LEVEL) == 0) + { + FluidStack containedStack = getStack(blockState); + if (containedStack != null && resource.containsFluid(containedStack)) + { + if (doDrain) + { + world.setBlockState(blockPos, Blocks.AIR.getDefaultState(), 11); + } + return containedStack; + } + + } + return null; + } + + @Nullable + @Override + public FluidStack drain(int maxDrain, boolean doDrain) + { + if (maxDrain < Fluid.BUCKET_VOLUME) + { + return null; + } + + IBlockState blockState = world.getBlockState(blockPos); + if (blockState.getBlock() == blockLiquid) + { + FluidStack containedStack = getStack(blockState); + if (containedStack != null && containedStack.amount <= maxDrain) + { + if (doDrain) + { + world.setBlockState(blockPos, Blocks.AIR.getDefaultState(), 11); + } + return containedStack; + } + + } + return null; + } + + @Nullable + private FluidStack getStack(IBlockState blockState) + { + Material material = blockState.getMaterial(); + if (material == Material.WATER && blockState.getValue(BlockLiquid.LEVEL) == 0) + { + return new FluidStack(FluidRegistry.WATER, Fluid.BUCKET_VOLUME); + } + else if (material == Material.LAVA && blockState.getValue(BlockLiquid.LEVEL) == 0) + { + return new FluidStack(FluidRegistry.LAVA, Fluid.BUCKET_VOLUME); + } + else + { + return null; + } + } +} 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 000000000..cc4eec2b2 --- /dev/null +++ b/src/main/java/net/minecraftforge/fluids/capability/wrappers/FluidBlockWrapper.java @@ -0,0 +1,98 @@ +package net.minecraftforge.fluids.capability.wrappers; + +import javax.annotation.Nullable; + +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.capability.FluidTankProperties; +import net.minecraftforge.fluids.IFluidBlock; +import net.minecraftforge.fluids.capability.IFluidTankProperties; +import net.minecraftforge.fluids.capability.IFluidHandler; + +/** + * Wrapper to handle {@link IFluidBlock} as an 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 IFluidTankProperties[] getTankProperties() + { + float percentFilled = fluidBlock.getFilledPercentage(world, blockPos); + if (percentFilled < 0) + { + percentFilled *= -1; + } + int amountFilled = Math.round(Fluid.BUCKET_VOLUME * percentFilled); + FluidStack fluid = amountFilled > 0 ? new FluidStack(fluidBlock.getFluid(), amountFilled) : null; + return new FluidTankProperties[]{ new FluidTankProperties(fluid, Fluid.BUCKET_VOLUME, false, true)}; + } + + @Override + public int fill(FluidStack resource, boolean doFill) + { + return 0; + } + + @Nullable + @Override + public FluidStack drain(FluidStack resource, boolean doDrain) + { + if (resource == null || !fluidBlock.canDrain(world, blockPos)) + { + return null; + } + + FluidStack simulatedDrain = fluidBlock.drain(world, blockPos, false); + if (resource.containsFluid(simulatedDrain)) + { + if (doDrain) + { + return fluidBlock.drain(world, blockPos, true); + } + else + { + return simulatedDrain; + } + } + + return null; + } + + @Nullable + @Override + public FluidStack drain(int maxDrain, boolean doDrain) + { + if (maxDrain <= 0 || !fluidBlock.canDrain(world, blockPos)) + { + return null; + } + + FluidStack simulatedDrain = fluidBlock.drain(world, blockPos, false); + if (simulatedDrain != null && simulatedDrain.amount <= maxDrain) + { + if (doDrain) + { + return fluidBlock.drain(world, blockPos, true); + } + else + { + return simulatedDrain; + } + } + + return null; + } +} diff --git a/src/main/java/net/minecraftforge/fluids/capability/wrappers/FluidBucketWrapper.java b/src/main/java/net/minecraftforge/fluids/capability/wrappers/FluidBucketWrapper.java new file mode 100644 index 000000000..16525ccc7 --- /dev/null +++ b/src/main/java/net/minecraftforge/fluids/capability/wrappers/FluidBucketWrapper.java @@ -0,0 +1,184 @@ +package net.minecraftforge.fluids.capability.wrappers; + +import javax.annotation.Nullable; + +import net.minecraft.init.Items; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumFacing; +import net.minecraftforge.common.ForgeModContainer; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.ICapabilityProvider; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidRegistry; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.capability.FluidTankProperties; +import net.minecraftforge.fluids.capability.IFluidTankProperties; +import net.minecraftforge.fluids.UniversalBucket; +import net.minecraftforge.fluids.capability.CapabilityFluidHandler; +import net.minecraftforge.fluids.capability.IFluidHandler; + +/** + * Wrapper for vanilla and forge buckets. + * Swaps between empty bucket and filled bucket of the correct type. + */ +public class FluidBucketWrapper implements IFluidHandler, ICapabilityProvider +{ + protected final ItemStack container; + + public FluidBucketWrapper(ItemStack container) + { + this.container = container; + } + + public boolean canFillFluidType(FluidStack fluid) + { + if (fluid.getFluid() == FluidRegistry.WATER || fluid.getFluid() == FluidRegistry.LAVA || fluid.getFluid().getName().equals("milk")) + { + return true; + } + return FluidRegistry.isUniversalBucketEnabled() && FluidRegistry.getBucketFluids().contains(fluid.getFluid()); + } + + @Nullable + public FluidStack getFluid() + { + Item item = container.getItem(); + if (item == Items.WATER_BUCKET) + { + return new FluidStack(FluidRegistry.WATER, Fluid.BUCKET_VOLUME); + } + else if (item == Items.LAVA_BUCKET) + { + return new FluidStack(FluidRegistry.LAVA, Fluid.BUCKET_VOLUME); + } + else if (item == Items.MILK_BUCKET) + { + return FluidRegistry.getFluidStack("milk", Fluid.BUCKET_VOLUME); + } + else if (item == ForgeModContainer.getInstance().universalBucket) + { + return ForgeModContainer.getInstance().universalBucket.getFluid(container); + } + else + { + return null; + } + } + + protected void setFluid(Fluid fluid) { + if (fluid == null) + { + container.setItem(Items.BUCKET); + container.setTagCompound(null); + container.setItemDamage(0); + } + else if (fluid == FluidRegistry.WATER) + { + container.setItem(Items.WATER_BUCKET); + container.setTagCompound(null); + container.setItemDamage(0); + } + else if (fluid == FluidRegistry.LAVA) + { + container.setItem(Items.LAVA_BUCKET); + container.setTagCompound(null); + container.setItemDamage(0); + } + else if (fluid.getName().equals("milk")) + { + container.setItem(Items.MILK_BUCKET); + container.setTagCompound(null); + container.setItemDamage(0); + } + else if (FluidRegistry.isUniversalBucketEnabled() && FluidRegistry.getBucketFluids().contains(fluid)) + { + ItemStack filledBucket = UniversalBucket.getFilledBucket(ForgeModContainer.getInstance().universalBucket, fluid); + container.setItem(filledBucket.getItem()); + container.setTagCompound(filledBucket.getTagCompound()); + container.setItemDamage(filledBucket.getItemDamage()); + } + } + + @Override + public IFluidTankProperties[] getTankProperties() + { + return new FluidTankProperties[] { new FluidTankProperties(getFluid(), Fluid.BUCKET_VOLUME) }; + } + + @Override + public int fill(FluidStack resource, boolean doFill) + { + if (container.stackSize != 1 || resource == null || resource.amount < Fluid.BUCKET_VOLUME || getFluid() != null || !canFillFluidType(resource)) + { + return 0; + } + + if (doFill) + { + setFluid(resource.getFluid()); + } + + return Fluid.BUCKET_VOLUME; + } + + @Nullable + @Override + public FluidStack drain(FluidStack resource, boolean doDrain) + { + if (container.stackSize != 1 || resource == null || resource.amount < Fluid.BUCKET_VOLUME) + { + return null; + } + + FluidStack fluidStack = getFluid(); + if (fluidStack != null && fluidStack.isFluidEqual(resource)) + { + if (doDrain) + { + setFluid(null); + } + return fluidStack; + } + + return null; + } + + @Nullable + @Override + public FluidStack drain(int maxDrain, boolean doDrain) + { + if (container.stackSize != 1 || maxDrain < Fluid.BUCKET_VOLUME) + { + return null; + } + + FluidStack fluidStack = getFluid(); + if (fluidStack != null) + { + if (doDrain) + { + setFluid(null); + } + return fluidStack; + } + + return null; + } + + @Override + public boolean hasCapability(Capability capability, EnumFacing facing) + { + return capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY; + } + + @Override + public T getCapability(Capability capability, EnumFacing facing) + { + if (capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) + { + return CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY.cast(this); + } + return null; + } +} diff --git a/src/main/java/net/minecraftforge/fluids/capability/wrappers/FluidContainerItemWrapper.java b/src/main/java/net/minecraftforge/fluids/capability/wrappers/FluidContainerItemWrapper.java new file mode 100644 index 000000000..050b527d5 --- /dev/null +++ b/src/main/java/net/minecraftforge/fluids/capability/wrappers/FluidContainerItemWrapper.java @@ -0,0 +1,90 @@ +package net.minecraftforge.fluids.capability.wrappers; + +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumFacing; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.ICapabilityProvider; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.capability.FluidTankProperties; +import net.minecraftforge.fluids.IFluidContainerItem; +import net.minecraftforge.fluids.capability.CapabilityFluidHandler; +import net.minecraftforge.fluids.capability.IFluidHandler; + +/** + * FluidContainerItemWrapper converts an old {@link IFluidContainerItem} to IFluidHandler. + * Note that successful operations WILL modify the container itemStack. + * @deprecated will be removed along with {@link IFluidContainerItem} + */ +@Deprecated +public class FluidContainerItemWrapper implements IFluidHandler, ICapabilityProvider +{ + protected final IFluidContainerItem handler; + protected final ItemStack container; + + public FluidContainerItemWrapper(IFluidContainerItem handler, ItemStack container) + { + this.handler = handler; + this.container = container; + } + + @Override + public FluidTankProperties[] getTankProperties() + { + return new FluidTankProperties[] { new FluidTankProperties(handler.getFluid(container), handler.getCapacity(container)) }; + } + + @Override + public int fill(FluidStack resource, boolean doFill) + { + if (container.stackSize != 1) + { + return 0; + } + return handler.fill(container, resource, doFill); + } + + @Override + public FluidStack drain(FluidStack resource, boolean doDrain) + { + if (container.stackSize != 1 || resource == null) + { + return null; + } + + FluidStack canDrain = drain(resource.amount, false); + if (canDrain != null) + { + if (canDrain.isFluidEqual(resource)) + { + return drain(resource.amount, doDrain); + } + } + return null; + } + + @Override + public FluidStack drain(int maxDrain, boolean doDrain) + { + if (container.stackSize != 1) + { + return null; + } + return handler.drain(container, maxDrain, doDrain); + } + + @Override + public boolean hasCapability(Capability capability, EnumFacing facing) + { + return capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY; + } + + @Override + public T getCapability(Capability capability, EnumFacing facing) + { + if (capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) + { + return CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY.cast(this); + } + return null; + } +} diff --git a/src/main/java/net/minecraftforge/fluids/capability/wrappers/FluidContainerRegistryWrapper.java b/src/main/java/net/minecraftforge/fluids/capability/wrappers/FluidContainerRegistryWrapper.java new file mode 100644 index 000000000..09894e316 --- /dev/null +++ b/src/main/java/net/minecraftforge/fluids/capability/wrappers/FluidContainerRegistryWrapper.java @@ -0,0 +1,141 @@ +package net.minecraftforge.fluids.capability.wrappers; + +import javax.annotation.Nullable; + +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumFacing; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.ICapabilityProvider; +import net.minecraftforge.fluids.FluidContainerRegistry; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.capability.FluidTankProperties; +import net.minecraftforge.fluids.capability.IFluidTankProperties; +import net.minecraftforge.fluids.capability.CapabilityFluidHandler; +import net.minecraftforge.fluids.capability.IFluidHandler; + +/** + * Wraps a liquid container that uses the {@link FluidContainerRegistry} + * Note that successful operations WILL modify the container itemStack. + * @deprecated will be removed along with {@link FluidContainerRegistry} + */ +@Deprecated +public class FluidContainerRegistryWrapper implements IFluidHandler, ICapabilityProvider +{ + protected final ItemStack container; + + public FluidContainerRegistryWrapper(ItemStack container) + { + this.container = container; + } + + private void updateContainer(ItemStack newContainerData) + { + container.setItem(newContainerData.getItem()); + container.setTagCompound(newContainerData.getTagCompound()); + container.setItemDamage(newContainerData.getItemDamage()); + container.stackSize = newContainerData.stackSize; + } + + @Override + public IFluidTankProperties[] getTankProperties() + { + FluidStack fluid = FluidContainerRegistry.getFluidForFilledItem(container); + int capacity = FluidContainerRegistry.getContainerCapacity(fluid, container); + return new FluidTankProperties[] { new FluidTankProperties(fluid, capacity) }; + } + + @Override + public int fill(FluidStack resource, boolean doFill) + { + if (container.stackSize != 1 || resource == null) + { + return 0; + } + + FluidStack originalContained = FluidContainerRegistry.getFluidForFilledItem(container); + + ItemStack result = FluidContainerRegistry.fillFluidContainer(resource, container); + if (result == null) + { + return 0; + } + + if (doFill) + { + updateContainer(result); + } + + FluidStack newContained = FluidContainerRegistry.getFluidForFilledItem(result); + + int originalAmount = originalContained == null ? 0 : originalContained.amount; + int newAmount = newContained == null ? 0 : newContained.amount; + return newAmount - originalAmount; + } + + @Nullable + @Override + public FluidStack drain(FluidStack resource, boolean doDrain) + { + if (container.stackSize != 1 || resource == null) + { + return null; + } + + FluidStack contained = FluidContainerRegistry.getFluidForFilledItem(container); + if (contained != null && contained.isFluidEqual(resource)) + { + return drain(resource.amount, doDrain); + } + + return null; + } + + @Nullable + @Override + public FluidStack drain(int maxDrain, boolean doDrain) + { + if (container.stackSize != 1) + { + return null; + } + + FluidStack contained = FluidContainerRegistry.getFluidForFilledItem(container); + if (contained != null) + { + if (contained.amount <= maxDrain) + { + ItemStack emptyContainer = FluidContainerRegistry.drainFluidContainer(container); + if (emptyContainer != null) + { + if (doDrain) + { + if (FluidContainerRegistry.hasNullEmptyContainer(container)) + { + emptyContainer.stackSize = 0; + } + updateContainer(emptyContainer); + } + return contained; + } + } + } + + return null; + } + + @Override + public boolean hasCapability(Capability capability, @Nullable EnumFacing facing) + { + return capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY; + } + + @Override + public T getCapability(Capability capability, @Nullable EnumFacing facing) + { + if (capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) + { + return CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY.cast(this); + } + return null; + } +} diff --git a/src/main/java/net/minecraftforge/fluids/capability/wrappers/FluidHandlerWrapper.java b/src/main/java/net/minecraftforge/fluids/capability/wrappers/FluidHandlerWrapper.java new file mode 100644 index 000000000..78dcd3799 --- /dev/null +++ b/src/main/java/net/minecraftforge/fluids/capability/wrappers/FluidHandlerWrapper.java @@ -0,0 +1,52 @@ +package net.minecraftforge.fluids.capability.wrappers; + +import net.minecraft.util.EnumFacing; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.capability.FluidTankProperties; +import net.minecraftforge.fluids.capability.IFluidTankProperties; +import net.minecraftforge.fluids.capability.IFluidHandler; + +/** + * FluidHandlerWrapper automatically converts the old {@link net.minecraftforge.fluids.IFluidHandler} to the new version. + * @deprecated will be removed along with {@link net.minecraftforge.fluids.IFluidHandler} + */ +@Deprecated +public class FluidHandlerWrapper implements IFluidHandler +{ + protected final net.minecraftforge.fluids.IFluidHandler handler; + protected final EnumFacing side; + + public FluidHandlerWrapper(net.minecraftforge.fluids.IFluidHandler handler, EnumFacing side) + { + this.handler = handler; + this.side = side; + } + + @Override + public IFluidTankProperties[] getTankProperties() + { + return FluidTankProperties.convert(handler.getTankInfo(side)); + } + + @Override + public int fill(FluidStack resource, boolean doFill) + { + if (resource == null || !handler.canFill(side, resource.getFluid())) + return 0; + return handler.fill(side, resource, doFill); + } + + @Override + public FluidStack drain(FluidStack resource, boolean doDrain) + { + if (resource == null || !handler.canDrain(side, resource.getFluid())) + return null; + return handler.drain(side, resource, doDrain); + } + + @Override + public FluidStack drain(int maxDrain, boolean doDrain) + { + return handler.drain(side, maxDrain, doDrain); + } +}