From 8eea7662884a13a7d5f162c7aa5a28dae267e0ed Mon Sep 17 00:00:00 2001 From: mezz Date: Mon, 5 Dec 2016 20:17:47 -0800 Subject: [PATCH] [1.11.x] Refine fluid placement utilities (#3509) --- .../fluids/BlockFluidClassic.java | 14 + .../fluids/BlockFluidFinite.java | 37 ++- .../fluids/DispenseFluidContainer.java | 10 +- .../net/minecraftforge/fluids/FluidUtil.java | 136 +++++---- .../minecraftforge/fluids/IFluidBlock.java | 16 ++ .../fluids/UniversalBucket.java | 7 +- .../templates/VoidFluidHandler.java | 91 ++++++ .../wrappers/BlockLiquidWrapper.java | 19 +- .../capability/wrappers/BlockWrapper.java | 61 ++++ .../wrappers/FluidBlockWrapper.java | 7 +- .../debug/FluidPlacementTest.java | 270 ++++++++++++++++++ .../blockstates/finite_fluid_block.json | 16 ++ 12 files changed, 615 insertions(+), 69 deletions(-) create mode 100644 src/main/java/net/minecraftforge/fluids/capability/templates/VoidFluidHandler.java create mode 100644 src/main/java/net/minecraftforge/fluids/capability/wrappers/BlockWrapper.java create mode 100644 src/test/java/net/minecraftforge/debug/FluidPlacementTest.java create mode 100644 src/test/resources/assets/forgedebugfluidplacement/blockstates/finite_fluid_block.json diff --git a/src/main/java/net/minecraftforge/fluids/BlockFluidClassic.java b/src/main/java/net/minecraftforge/fluids/BlockFluidClassic.java index e0b6bd47c5..de12432d96 100644 --- a/src/main/java/net/minecraftforge/fluids/BlockFluidClassic.java +++ b/src/main/java/net/minecraftforge/fluids/BlockFluidClassic.java @@ -358,6 +358,20 @@ public class BlockFluidClassic extends BlockFluidBase } /* IFluidBlock */ + @Override + public int place(World world, BlockPos pos, @Nonnull FluidStack fluidStack, boolean doPlace) + { + if (fluidStack.amount < Fluid.BUCKET_VOLUME) + { + return 0; + } + if (doPlace) + { + world.setBlockState(pos, this.getDefaultState(), 11); + } + return Fluid.BUCKET_VOLUME; + } + @Override public FluidStack drain(World world, BlockPos pos, boolean doDrain) { diff --git a/src/main/java/net/minecraftforge/fluids/BlockFluidFinite.java b/src/main/java/net/minecraftforge/fluids/BlockFluidFinite.java index 1c475c322f..27329e9911 100644 --- a/src/main/java/net/minecraftforge/fluids/BlockFluidFinite.java +++ b/src/main/java/net/minecraftforge/fluids/BlockFluidFinite.java @@ -64,7 +64,7 @@ public class BlockFluidFinite extends BlockFluidBase @Override public boolean canCollideCheck(@Nonnull IBlockState state, boolean fullHit) { - return fullHit && state.getValue(LEVEL) == quantaPerBlock - 1; + return fullHit; } @Override @@ -244,6 +244,41 @@ public class BlockFluidFinite extends BlockFluidBase } /* IFluidBlock */ + @Override + public int place(World world, BlockPos pos, @Nonnull FluidStack fluidStack, boolean doPlace) + { + IBlockState existing = world.getBlockState(pos); + float quantaAmount = Fluid.BUCKET_VOLUME / quantaPerBlockFloat; + // If the stack contains more available fluid than the full source block, + // set a source block + int closest = Fluid.BUCKET_VOLUME; + int quanta = quantaPerBlock; + if (fluidStack.amount < closest) + { + // Figure out maximum level to match stack amount + closest = MathHelper.floor_float(quantaAmount * MathHelper.floor_float(fluidStack.amount / quantaAmount)); + quanta = MathHelper.floor_float(closest / quantaAmount); + } + if (existing.getBlock() == this) + { + int existingQuanta = existing.getValue(LEVEL) + 1; + int missingQuanta = quantaPerBlock - existingQuanta; + closest = Math.min(closest, MathHelper.floor_float(missingQuanta * quantaAmount)); + quanta = Math.min(quanta + existingQuanta, quantaPerBlock); + } + + // If too little (or too much, technically impossible) fluid is to be placed, abort + if (quanta < 1 || quanta > 16) + return 0; + + if (doPlace) + { + world.setBlockState(pos, getDefaultState().withProperty(LEVEL, quanta - 1), 11); + } + + return closest; + } + @Override public FluidStack drain(World world, BlockPos pos, boolean doDrain) { diff --git a/src/main/java/net/minecraftforge/fluids/DispenseFluidContainer.java b/src/main/java/net/minecraftforge/fluids/DispenseFluidContainer.java index b287601219..2fb5f8da9a 100644 --- a/src/main/java/net/minecraftforge/fluids/DispenseFluidContainer.java +++ b/src/main/java/net/minecraftforge/fluids/DispenseFluidContainer.java @@ -109,13 +109,13 @@ public class DispenseFluidContainer extends BehaviorDefaultDispenseItem FluidStack fluidStack = fluidHandler.drain(Fluid.BUCKET_VOLUME, false); EnumFacing dispenserFacing = source.getBlockState().getValue(BlockDispenser.FACING); BlockPos blockpos = source.getBlockPos().offset(dispenserFacing); + FluidActionResult result = fluidStack != null ? FluidUtil.tryPlaceFluid(null, source.getWorld(), blockpos, stack, fluidStack) : FluidActionResult.FAILURE; - if (fluidStack != null && fluidStack.amount == Fluid.BUCKET_VOLUME && FluidUtil.tryPlaceFluid(null, source.getWorld(), fluidStack, blockpos)) + if (result.isSuccess()) { - fluidHandler.drain(Fluid.BUCKET_VOLUME, true); - ItemStack drainedStack = fluidHandler.getContainer(); + ItemStack drainedStack = result.getResult(); - if (stack.func_190916_E() == 1) + if (drainedStack.func_190916_E() == 1) { return drainedStack; } @@ -124,7 +124,7 @@ public class DispenseFluidContainer extends BehaviorDefaultDispenseItem this.dispenseBehavior.dispense(source, drainedStack); } - ItemStack stackCopy = stack.copy(); + ItemStack stackCopy = drainedStack.copy(); stackCopy.func_190918_g(1); return stackCopy; } diff --git a/src/main/java/net/minecraftforge/fluids/FluidUtil.java b/src/main/java/net/minecraftforge/fluids/FluidUtil.java index f2ae6748a9..6a9a7c307d 100644 --- a/src/main/java/net/minecraftforge/fluids/FluidUtil.java +++ b/src/main/java/net/minecraftforge/fluids/FluidUtil.java @@ -37,7 +37,9 @@ import net.minecraft.world.World; import net.minecraftforge.fluids.capability.CapabilityFluidHandler; import net.minecraftforge.fluids.capability.IFluidHandler; import net.minecraftforge.fluids.capability.IFluidHandlerItem; +import net.minecraftforge.fluids.capability.templates.VoidFluidHandler; import net.minecraftforge.fluids.capability.wrappers.BlockLiquidWrapper; +import net.minecraftforge.fluids.capability.wrappers.BlockWrapper; import net.minecraftforge.fluids.capability.wrappers.FluidBlockWrapper; import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.ItemHandlerHelper; @@ -419,62 +421,6 @@ public class FluidUtil 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 fluidStack The fluidStack 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 fluidStack, BlockPos pos) - { - if (worldIn == null || fluidStack == null || pos == null) - { - return false; - } - - Fluid fluid = fluidStack.getFluid(); - if (fluid == null || !fluid.canBePlacedInWorld()) - { - 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. - } - - if (worldIn.provider.doesWaterVaporize() && fluid.doesVaporize(fluidStack)) - { - fluid.vaporize(player, worldIn, pos, fluidStack); - } - else - { - if (!worldIn.isRemote && (isDestNonSolid || isDestReplaceable) && !destMaterial.isLiquid()) - { - worldIn.destroyBlock(pos, true); - } - - SoundEvent soundevent = fluid.getEmptySound(fluidStack); - worldIn.playSound(player, pos, soundevent, SoundCategory.BLOCKS, 1.0F, 1.0F); - - IBlockState fluidBlockState = fluid.getBlock().getDefaultState(); - worldIn.setBlockState(pos, fluidBlockState, 11); - } - return true; - } - /** * Attempts to pick up a fluid in the world and put it in an empty container item. * @@ -511,4 +457,82 @@ public class FluidUtil } return FluidActionResult.FAILURE; } + + /** + * Tries to place a fluid in the world in block form and drains the container. + * Makes a fluid emptying sound when successful. + * Honors the amount of fluid contained by the used container. + * 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 world World to place the fluid in + * @param pos The position in the world to place the fluid block + * @param container The fluid container holding the fluidStack to place + * @param resource The fluidStack to place + * @return the container's ItemStack with the remaining amount of fluid if the placement was successful, null otherwise + */ + @Nonnull + public static FluidActionResult tryPlaceFluid(@Nullable EntityPlayer player, World world, BlockPos pos, @Nonnull ItemStack container, FluidStack resource) + { + if (world == null || resource == null || pos == null) + { + return FluidActionResult.FAILURE; + } + + Fluid fluid = resource.getFluid(); + if (fluid == null || !fluid.canBePlacedInWorld()) + { + return FluidActionResult.FAILURE; + } + + // check that we can place the fluid at the destination + IBlockState destBlockState = world.getBlockState(pos); + Material destMaterial = destBlockState.getMaterial(); + boolean isDestNonSolid = !destMaterial.isSolid(); + boolean isDestReplaceable = destBlockState.getBlock().isReplaceable(world, pos); + if (!world.isAirBlock(pos) && !isDestNonSolid && !isDestReplaceable) + { + return FluidActionResult.FAILURE; // Non-air, solid, unreplacable block. We can't put fluid here. + } + + if (world.provider.doesWaterVaporize() && fluid.doesVaporize(resource)) + { + fluid.vaporize(player, world, pos, resource); + return tryEmptyContainer(container, new VoidFluidHandler(), Integer.MAX_VALUE, player, true); + } + else + { + if (!world.isRemote && (isDestNonSolid || isDestReplaceable) && !destMaterial.isLiquid()) + { + world.destroyBlock(pos, true); + } + + // Defer the placement to the fluid block + // Instead of actually "filling", the fluid handler method replaces the block + Block block = fluid.getBlock(); + IFluidHandler handler; + if (block instanceof IFluidBlock) + { + handler = new FluidBlockWrapper((IFluidBlock) block, world, pos); + } + else if (block instanceof BlockLiquid) + { + handler = new BlockLiquidWrapper((BlockLiquid) block, world, pos); + } + else + { + handler = new BlockWrapper(block, world, pos); + } + FluidActionResult result = tryEmptyContainer(container, handler, Integer.MAX_VALUE, player, true); + if (result.isSuccess()) + { + SoundEvent soundevent = fluid.getEmptySound(resource); + world.playSound(player, pos, soundevent, SoundCategory.BLOCKS, 1.0F, 1.0F); + } + + return result; + } + } } diff --git a/src/main/java/net/minecraftforge/fluids/IFluidBlock.java b/src/main/java/net/minecraftforge/fluids/IFluidBlock.java index 1f39b24070..ae200f429c 100644 --- a/src/main/java/net/minecraftforge/fluids/IFluidBlock.java +++ b/src/main/java/net/minecraftforge/fluids/IFluidBlock.java @@ -22,6 +22,8 @@ package net.minecraftforge.fluids; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; +import javax.annotation.Nonnull; + /** * Implement this interface on Block classes which represent world-placeable Fluids. * @@ -35,6 +37,20 @@ public interface IFluidBlock */ Fluid getFluid(); + /** + * Attempts to place the block at a given position. The placed block's level will correspond + * to the provided fluid amount. + * This method should be called by fluid containers such as buckets, but it is recommended + * to use {@link FluidUtil}. + * + * @param world the world to place the block in + * @param pos the position to place the block at + * @param fluidStack the fluid stack to get the required data from + * @param doPlace if false, the placement will only be simulated + * @return the amount of fluid extracted from the provided stack to achieve some fluid level + */ + int place(World world, BlockPos pos, @Nonnull FluidStack fluidStack, boolean doPlace); + /** * Attempt to drain the block. This method should be called by devices such as pumps. * diff --git a/src/main/java/net/minecraftforge/fluids/UniversalBucket.java b/src/main/java/net/minecraftforge/fluids/UniversalBucket.java index 9bfba7c8df..fb2098d0db 100644 --- a/src/main/java/net/minecraftforge/fluids/UniversalBucket.java +++ b/src/main/java/net/minecraftforge/fluids/UniversalBucket.java @@ -177,14 +177,15 @@ public class UniversalBucket extends Item if (player.canPlayerEdit(targetPos, mop.sideHit, itemstack)) { // try placing liquid - if (FluidUtil.tryPlaceFluid(player, player.getEntityWorld(), fluidStack, targetPos) - && !player.capabilities.isCreativeMode) + FluidActionResult result = FluidUtil.tryPlaceFluid(player, world, targetPos, itemstack, fluidStack); + if (result.isSuccess() && !player.capabilities.isCreativeMode) { // success! player.addStat(StatList.getObjectUseStats(this)); itemstack.func_190918_g(1); - ItemStack emptyStack = !getEmpty().func_190926_b() ? getEmpty().copy() : new ItemStack(this); + ItemStack drained = result.getResult(); + ItemStack emptyStack = !drained.func_190926_b() ? drained.copy() : new ItemStack(this); // check whether we replace the item or add the empty one to the inventory if (itemstack.func_190926_b()) diff --git a/src/main/java/net/minecraftforge/fluids/capability/templates/VoidFluidHandler.java b/src/main/java/net/minecraftforge/fluids/capability/templates/VoidFluidHandler.java new file mode 100644 index 0000000000..39989673ca --- /dev/null +++ b/src/main/java/net/minecraftforge/fluids/capability/templates/VoidFluidHandler.java @@ -0,0 +1,91 @@ +/* + * Minecraft Forge + * Copyright (c) 2016. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.fluids.capability.templates; + +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.FluidTankInfo; +import net.minecraftforge.fluids.IFluidTank; +import net.minecraftforge.fluids.capability.IFluidHandler; +import net.minecraftforge.fluids.capability.IFluidTankProperties; + +import javax.annotation.Nullable; + +import static net.minecraftforge.fluids.capability.templates.EmptyFluidHandler.EMPTY_TANK_INFO; +import static net.minecraftforge.fluids.capability.templates.EmptyFluidHandler.EMPTY_TANK_PROPERTIES_ARRAY; + +/** + * VoidFluidHandler is a template fluid handler that can be filled indefinitely without ever getting full. + * It does not store fluid that gets filled into it, but "destroys" it upon receiving it. + */ +public class VoidFluidHandler implements IFluidHandler, IFluidTank +{ + public static final EmptyFluidHandler INSTANCE = new EmptyFluidHandler(); + + public VoidFluidHandler() {} + + @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 Integer.MAX_VALUE; + } + + @Override + public FluidTankInfo getInfo() + { + return EMPTY_TANK_INFO; + } + + @Override + public int fill(FluidStack resource, boolean doFill) + { + return resource.amount; + } + + @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/wrappers/BlockLiquidWrapper.java b/src/main/java/net/minecraftforge/fluids/capability/wrappers/BlockLiquidWrapper.java index 2fcd48e5e0..7104c2eded 100644 --- a/src/main/java/net/minecraftforge/fluids/capability/wrappers/BlockLiquidWrapper.java +++ b/src/main/java/net/minecraftforge/fluids/capability/wrappers/BlockLiquidWrapper.java @@ -26,7 +26,7 @@ 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.item.ItemBucket; import net.minecraft.util.EnumHand; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; @@ -39,7 +39,7 @@ 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)} + * Methods are modeled after {@link ItemBucket#onItemRightClick(World, EntityPlayer, EnumHand)} */ public class BlockLiquidWrapper implements IFluidHandler { @@ -69,7 +69,20 @@ public class BlockLiquidWrapper implements IFluidHandler @Override public int fill(FluidStack resource, boolean doFill) { - return 0; + // NOTE: "Filling" means placement in this context! + if (resource.amount < Fluid.BUCKET_VOLUME) + { + return 0; + } + + if (doFill) + { + Material material = blockLiquid.getDefaultState().getMaterial(); + BlockLiquid block = BlockLiquid.getStaticBlock(material); + world.setBlockState(blockPos, block.getDefaultState().withProperty(BlockLiquid.LEVEL, 0), 11); + } + + return Fluid.BUCKET_VOLUME; } @Nullable diff --git a/src/main/java/net/minecraftforge/fluids/capability/wrappers/BlockWrapper.java b/src/main/java/net/minecraftforge/fluids/capability/wrappers/BlockWrapper.java new file mode 100644 index 0000000000..f7ce2676fe --- /dev/null +++ b/src/main/java/net/minecraftforge/fluids/capability/wrappers/BlockWrapper.java @@ -0,0 +1,61 @@ +/* + * Minecraft Forge + * Copyright (c) 2016. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.fluids.capability.wrappers; + +import net.minecraft.block.Block; +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.templates.VoidFluidHandler; + +/** + * Wrapper around any block, only accounts for fluid placement, otherwise the block acts a void. + * If the block in question inherits from the default Vanilla or Forge implementations, + * consider using {@link BlockLiquidWrapper} or {@link FluidBlockWrapper} respectively. + */ +public class BlockWrapper extends VoidFluidHandler +{ + protected final Block block; + protected final World world; + protected final BlockPos blockPos; + + public BlockWrapper(Block block, World world, BlockPos blockPos) + { + this.block = block; + this.world = world; + this.blockPos = blockPos; + } + + @Override + public int fill(FluidStack resource, boolean doFill) + { + // NOTE: "Filling" means placement in this context! + if (resource.amount < Fluid.BUCKET_VOLUME) + { + return 0; + } + if (doFill) + { + world.setBlockState(blockPos, block.getDefaultState(), 11); + } + return Fluid.BUCKET_VOLUME; + } +} \ No newline at end of file diff --git a/src/main/java/net/minecraftforge/fluids/capability/wrappers/FluidBlockWrapper.java b/src/main/java/net/minecraftforge/fluids/capability/wrappers/FluidBlockWrapper.java index ef8e403205..67a8bdcbf3 100644 --- a/src/main/java/net/minecraftforge/fluids/capability/wrappers/FluidBlockWrapper.java +++ b/src/main/java/net/minecraftforge/fluids/capability/wrappers/FluidBlockWrapper.java @@ -62,7 +62,12 @@ public class FluidBlockWrapper implements IFluidHandler @Override public int fill(FluidStack resource, boolean doFill) { - return 0; + // NOTE: "Filling" means placement in this context! + if (resource == null) + { + return 0; + } + return fluidBlock.place(world, blockPos, resource, doFill); } @Nullable diff --git a/src/test/java/net/minecraftforge/debug/FluidPlacementTest.java b/src/test/java/net/minecraftforge/debug/FluidPlacementTest.java new file mode 100644 index 0000000000..3d0b3d25c3 --- /dev/null +++ b/src/test/java/net/minecraftforge/debug/FluidPlacementTest.java @@ -0,0 +1,270 @@ +package net.minecraftforge.debug; + +import net.minecraft.block.material.Material; +import net.minecraft.block.state.IBlockState; +import net.minecraft.client.renderer.ItemMeshDefinition; +import net.minecraft.client.renderer.block.model.ModelBakery; +import net.minecraft.client.renderer.block.model.ModelResourceLocation; +import net.minecraft.client.renderer.block.statemap.StateMapperBase; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Blocks; +import net.minecraft.item.*; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.NonNullList; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.client.model.ModelLoader; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.common.capabilities.ICapabilityProvider; +import net.minecraftforge.fluids.*; +import net.minecraftforge.fluids.capability.templates.FluidHandlerItemStack; +import net.minecraftforge.fluids.capability.wrappers.FluidBucketWrapper; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.Mod.EventHandler; +import net.minecraftforge.fml.common.SidedProxy; +import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; +import net.minecraftforge.fml.common.registry.GameRegistry; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.List; + +import static net.minecraftforge.fluids.capability.templates.FluidHandlerItemStack.FLUID_NBT_KEY; + +@Mod(modid = FluidPlacementTest.MODID, name = "ForgeDebugFluidPlacement", version = FluidPlacementTest.VERSION) +public class FluidPlacementTest +{ + public static final String MODID = "forgedebugfluidplacement"; + public static final String VERSION = "1.0"; + + public static final boolean ENABLE = true; + + @SidedProxy + public static CommonProxy proxy; + + @EventHandler + public void preInit(FMLPreInitializationEvent event) + { + if (ENABLE && ModelFluidDebug.ENABLE) + { + proxy.preInit(event); + } + } + + public static class CommonProxy + { + public void preInit(FMLPreInitializationEvent event) + { + FluidRegistry.registerFluid(FiniteFluid.instance); + FluidRegistry.addBucketForFluid(FiniteFluid.instance); + GameRegistry.register(EmptyFluidContainer.instance); + GameRegistry.register(FluidContainer.instance); + GameRegistry.register(FiniteFluidBlock.instance); + GameRegistry.register(new ItemBlock(FiniteFluidBlock.instance).setRegistryName(FiniteFluidBlock.instance.getRegistryName())); + MinecraftForge.EVENT_BUS.register(FluidContainer.instance); + } + } + + public static class ServerProxy extends CommonProxy + { + } + + public static class ClientProxy extends CommonProxy + { + private static ModelResourceLocation fluidLocation = new ModelResourceLocation(MODID.toLowerCase() + ":" + FiniteFluidBlock.name, "normal"); + + @Override + public void preInit(FMLPreInitializationEvent event) + { + super.preInit(event); + Item fluid = Item.getItemFromBlock(FiniteFluidBlock.instance); + ModelLoader.setCustomModelResourceLocation(EmptyFluidContainer.instance, 0, new ModelResourceLocation("forge:bucket", "inventory")); + ModelLoader.setBucketModelDefinition(FluidContainer.instance); + // no need to pass the locations here, since they'll be loaded by the block model logic. + ModelBakery.registerItemVariants(fluid); + ModelLoader.setCustomMeshDefinition(fluid, new ItemMeshDefinition() + { + public ModelResourceLocation getModelLocation(ItemStack stack) + { + return fluidLocation; + } + }); + ModelLoader.setCustomStateMapper(FiniteFluidBlock.instance, new StateMapperBase() + { + protected ModelResourceLocation getModelResourceLocation(IBlockState state) + { + return fluidLocation; + } + }); + } + } + + public static final class FiniteFluid extends Fluid + { + public static final String name = "finitefluid"; + public static final FiniteFluid instance = new FiniteFluid(); + + private FiniteFluid() + { + super(name, new ResourceLocation("blocks/water_still"), new ResourceLocation("blocks/water_flow")); + } + + @Override + public int getColor() + { + return 0xFFFFFF00; + } + + @Override + public String getLocalizedName(FluidStack stack) + { + return "Finite Fluid"; + } + } + + public static final class FiniteFluidBlock extends BlockFluidFinite + { + public static final FiniteFluidBlock instance = new FiniteFluidBlock(); + public static final String name = "finite_fluid_block"; + + private FiniteFluidBlock() + { + super(FiniteFluid.instance, Material.WATER); + setCreativeTab(CreativeTabs.BUILDING_BLOCKS); + setUnlocalizedName(MODID + ":" + name); + setRegistryName(MODID, name); + } + } + + public static final class EmptyFluidContainer extends ItemBucket + { + public static final EmptyFluidContainer instance = new EmptyFluidContainer(); + public static final String name = "empty_fluid_container"; + + private EmptyFluidContainer() + { + super(Blocks.AIR); + setRegistryName(MODID, name); + setUnlocalizedName(MODID + ":" + name); + setMaxStackSize(16); + } + + @Override + public ICapabilityProvider initCapabilities(ItemStack stack, @Nullable NBTTagCompound nbt) + { + return new EmptyContainerHandler(stack); + } + + private static final class EmptyContainerHandler extends FluidBucketWrapper + { + public EmptyContainerHandler(@Nonnull ItemStack container) + { + super(container); + } + + @Override + public int fill(FluidStack resource, boolean doFill) + { + if (container.func_190916_E() != 1 || resource == null || resource.amount > Fluid.BUCKET_VOLUME || container + .getItem() instanceof ItemBucketMilk || getFluid() != null || !canFillFluidType(resource)) + { + return 0; + } + + if (doFill) + { + container = new ItemStack(FluidContainer.instance); + NBTTagCompound tag = new NBTTagCompound(); + NBTTagCompound fluidTag = new NBTTagCompound(); + resource.writeToNBT(fluidTag); + tag.setTag(FLUID_NBT_KEY, fluidTag); + container.setTagCompound(tag); + } + + return resource.amount; + } + } + } + + public static final class FluidContainer extends UniversalBucket + { + public static final FluidContainer instance = new FluidContainer(); + public static final String name = "fluid_container"; + + private FluidContainer() + { + super(1000, new ItemStack(EmptyFluidContainer.instance), false); + setCreativeTab(CreativeTabs.MISC); + setRegistryName(MODID, name); + setUnlocalizedName(MODID + ":" + name); + } + + @Nonnull + @Override + public String getItemStackDisplayName(@Nonnull ItemStack stack) + { + FluidStack fluid = getFluid(stack); + if (fluid == null || fluid.getFluid() == null) + { + return "Empty Variable Container"; + } + return "Variable Container (" + getFluid(stack).getLocalizedName() + ")"; + } + + @Override + public FluidStack getFluid(ItemStack container) + { + container = container.copy(); + if (container.getTagCompound() != null) + container.setTagCompound(container.getTagCompound().getCompoundTag(FLUID_NBT_KEY)); + return super.getFluid(container); + } + + @Override + @SideOnly(Side.CLIENT) + public void addInformation(ItemStack stack, EntityPlayer playerIn, List tooltip, boolean advanced) + { + FluidStack fluid = getFluid(stack); + if (fluid != null) + { + tooltip.add(fluid.amount + "/1000"); + } + } + + @Override + public void getSubItems(@Nonnull Item itemIn, @Nonnull CreativeTabs tab, @Nonnull NonNullList subItems) + { + Fluid[] fluids = new Fluid[]{FluidRegistry.WATER, FluidRegistry.LAVA, FiniteFluid.instance, ModelFluidDebug.TestFluid.instance}; + // add 16 variable fillings + for (Fluid fluid : fluids) + { + for (int amount = 125; amount <= 1000; amount += 125) + { + for (int offset = 63; offset >= 0; offset -= 63) + { + FluidStack fs = new FluidStack(fluid, amount - offset); + ItemStack stack = new ItemStack(this); + NBTTagCompound tag = stack.getTagCompound(); + if (tag == null) + { + tag = new NBTTagCompound(); + } + NBTTagCompound fluidTag = new NBTTagCompound(); + fs.writeToNBT(fluidTag); + tag.setTag(FLUID_NBT_KEY, fluidTag); + stack.setTagCompound(tag); + subItems.add(stack); + } + } + } + } + + @Override + public ICapabilityProvider initCapabilities(ItemStack stack, NBTTagCompound nbt) + { + return new FluidHandlerItemStack.SwapEmpty(stack, new ItemStack(EmptyFluidContainer.instance), 1000); + } + } +} \ No newline at end of file diff --git a/src/test/resources/assets/forgedebugfluidplacement/blockstates/finite_fluid_block.json b/src/test/resources/assets/forgedebugfluidplacement/blockstates/finite_fluid_block.json new file mode 100644 index 0000000000..53cc9dd240 --- /dev/null +++ b/src/test/resources/assets/forgedebugfluidplacement/blockstates/finite_fluid_block.json @@ -0,0 +1,16 @@ +{ + "forge_marker": 1, + "defaults": { + "model": "forge:fluid", + "transform": "forge:default-item" + }, + "variants": { + "normal": [ + { + "custom": { + "fluid": "finitefluid" + } + } + ] + } +} \ No newline at end of file