User:ChampionAsh5357/Sandbox/Fluids API

From Forge Community Wiki

In Forge 41.0.28, the Fluid API had been completely overhauled. These changes expanded the system to allow for custom physics logic and additional behavior not tied directly to the water and lava tags. This guide will go through all the changes and additions that Fluids now provide for you to use.

An example of the new fluid system can be found in the test mod.

Quick Guide

This is a quick guide representing the one to one conversions that the Fluid API changed

  • FluidAttributes -> FluidType (This is a Forge Registry forge:fluid_type)
  • FluidAttributes#BUCKET_VOLUME -> FluidType#BUCKET_VOLUME
  • FluidAttributes#getBucket -> FluidType#getBucket
  • FluidAttributes#getBlock -> FluidType#getBlockForFluidState
  • FluidAttributes#getStateForPlacement -> FluidType#getStateForPlacement
  • FluidAttributes#canBePlacedInWorld -> FluidType#canBePlacedInLevel
  • FluidAttributes#isLighterThanAir -> FluidType#isLighterThanAir
  • FluidAttributes#doesVaporize -> FluidType#isVaporizedOnPlacement
  • FluidAttributes#vaporize -> FluidType#onVaporize
  • FluidAttributes#getDisplayName(FluidStack) -> FluidType#getDescription(FluidStack)
  • FluidAttributes#getTranslationKey(FluidStack) -> FluidType#getDescriptionId(FluidStack)
  • FluidAttributes#getTranslationKey -> FluidType#getDescriptionId
  • FluidAttributes#getLuminosity -> FluidType#getLightLevel
  • FluidAttributes#getDensity -> FluidType#getDensity
  • FluidAttributes#getTemperature -> FluidType#getTemperature
  • FluidAttributes#getViscosity -> FluidType#getViscosity
  • FluidAttributes#isGaseous -> Tags$Fluids#GASEOUS
  • FluidAttributes#getRarity -> FluidType#getRarity
  • FluidAttributes#getColor -> IFluidTypeRenderProperties#getColorTint
  • FluidAttributes#getStillTexture -> IFluidTypeRenderProperties#getStillTexture
  • FluidAttributes#getFlowingTexture -> IFluidTypeRenderProperties#getFlowingTexture
  • FluidAttributes#getOverlayTexture -> IFluidTypeRenderProperties#getOverlayTexture
  • FluidAttributes#getFillSound -> FluidType#getSound(SoundActions.BUCKET_FILL)
  • FluidAttributes#getEmptySound -> FluidType#getSound(SoundActions.BUCKET_EMPTY)
  • FluidAttributes#getLuminosity(FluidStack) -> FluidType#getLightLevel(FluidStack)
  • FluidAttributes#getDensity(FluidStack) -> FluidType#getDensity(FluidStack)
  • FluidAttributes#getTemperature(FluidStack) -> FluidType#getTemperature(FluidStack)
  • FluidAttributes#getViscosity(FluidStack) -> FluidType#getViscosity(FluidStack)
  • FluidAttributes#isGaseous(FluidStack) -> REMOVED
  • FluidAttributes#getColor(FluidStack) -> IFluidTypeRenderProperties#getColorTint(FluidStack)
  • FluidAttributes#getStillTexture(FluidStack) -> IFluidTypeRenderProperties#getStillTexture(FluidStack)
  • FluidAttributes#getFlowingTexture(FluidStack) -> IFluidTypeRenderProperties#getFlowingTexture(FluidStack)
  • FluidAttributes#getFillSound(FluidStack) -> FluidType#getSound(FluidStack, SoundActions.BUCKET_FILL
  • FluidAttributes#getEmptySound(FluidStack) -> FluidType#getSound(FluidStack, SoundActions.BUCKET_EMPTY)
  • FluidAttributes#getLuminosity(BlockAndTintGetter, BlockPos) -> FluidType#getLightLevel(FluidState, BlockAndTintGetter, BlockPos)
  • FluidAttributes#getDensity(BlockAndTintGetter, BlockPos) -> FluidType#getDensity(FluidState, BlockAndTintGetter, BlockPos)
  • FluidAttributes#getTemperature(BlockAndTintGetter, BlockPos) -> FluidType#getTemperature(FluidState, BlockAndTintGetter, BlockPos)
  • FluidAttributes#getViscosity(BlockAndTintGetter, BlockPos) -> FluidType#getViscosity(FluidState, BlockAndTintGetter, BlockPos)
  • FluidAttributes#isGaseous(BlockAndTintGetter, BlockPos) -> REMOVED
  • FluidAttributes#getRarity(BlockAndTintGetter, BlockPos) -> REMOVED
  • FluidAttributes#getColor(BlockAndTintGetter, BlockPos) -> FluidType#getColorTint(FluidState, BlockAndTintGetter, BlockPos)
  • FluidAttributes#getStillTexture(BlockAndTintGetter, BlockPos) -> FluidType#getStillTexture(FluidState, BlockAndTintGetter, BlockPos)
  • FluidAttributes#getFlowingTexture(BlockAndTintGetter, BlockPos) -> FluidType#getFlowingTexture(FluidState, BlockAndTintGetter, BlockPos)
  • FluidAttributes#getFillSound(BlockAndTintGetter, BlockPos) -> FluidType#getSound(Player, BlockGetter, BlockPos, SoundActions.BUCKET_FILL)
  • FluidAttributes#getEmptySound(BlockAndTintGetter, BlockPos) -> FluidType#getSound(Player, BlockGetter, BlockPos, SoundActions.BUCKET_EMPTY)
  • FluidAttributes#getTextures -> IFluidTypeRenderProperties#getTextures
  • FluidAttributes$Builder -> FluidType$Properties
  • IForgeBlock#getAiPathNodeType -> IForgeBlock#getBlockPathType
  • IForgeEntity#canBeRiddenInWater -> IForgeEntity#canBeRiddenUnderFluidType
  • IForgeFluid#isEntityInside -> REMOVED
  • IForgeFluid#isAABBInsideMaterial -> REMOVED
  • IForgeFluid#getAttributes -> IForgeFluid#getFluidType
  • IForgeFluidState#isEntityInside -> REMOVED
  • ForgeFlowingFluid$Properties(Fluid, Fluid, FluidAttributes) -> ForgeFlowingFluid$Properties(FluidType, Fluid, Fluid)
  • ForgeFlowingFluid#canMultiply -> FluidType#canConvertToSource(FluidState, LevelReader, BlockPos)

Vanilla Breaking Change: No Physics in Tags

One of the largest changes within the Fluid API is that Fluid Tags no longer control the physics of fluids. To account for this, a different unifying object which can represent any group of fluids must be chosen. After much consideration, this became FluidType, which replaced FluidAttributes.

Removal of FluidAttributes

FluidAttributes has been completely removed with most of its logic being delegated to either FluidType or IFluidTypeRenderProperties for rendering. The object itself wasn't unique and tended to be reconstructed whenever referenced. As such, it was removed in favor of a more singleton approach.


FluidType replaces FluidAttributes. All fluids must override IForgeFluid#getFluidType similar to the previous #getAttributes. Additionally, the FluidType can be accessed from the fluid or fluid state for convenience.

private static final DeferredRegister<Fluid> FLUIDS = DeferredRegister.create(ForgeRegistries.FLUIDS, ID);

// TestFluid extends FlowingFluid
private static final RegistryObject<FlowingFluid> TEST_FLUID = FLUIDS.register("test_fluid", () -> new TestFluid() {
    public FluidType getFluidType() {
        return TEST_FLUID_TYPE.get();

FluidType is a forge registry, and as such needs to be registered. If using DeferredRegister, you must use the one that takes in the registry name and modid since the registry does not exist during mod construction.

private static final DeferredRegister<FluidType> FLUID_TYPES = DeferredRegister.create(ForgeRegistries.Keys.FLUID_TYPES, ID);

private static final RegistryObject<FluidType> TEST_FLUID_TYPE = FLUID_TYPES.register("test_fluid", () -> new FluidType(FluidType.Properties.create()));


FluidType#isVaporizedOnPlacement has now been expanded to allow for vaporization to occur outside of only ultra warm dimensions. To do so, simply override the method.


IFluidTypeRenderProperties contains all rendering related logic to fluids. This includes the color tint, still texture, flowing texture, and overlay texture. Note that the still texture and flowing texture must be specified by overriding #getStillTexture and #getFlowingTexture respectively.

Modified Accessors

All properties in FluidType and IFluidTypeRenderProperties have either an entity, level, or stack based accessor attached to it. These provide context in question if the behavior of the fluid should change in relation to the entity its acting upon, the location of the fluid in the level, or the containing fluid stack respectively.

There are some accessors with no parameters. These are typically because the use case may not be fully defined in every context that are available to the modder. They should not be used whenever possible as they may be removed in future releases once the context has been fully specified.

Chained Methods

A good portion of the accessors in FluidType are chained in some capacity. This means that modders who are using fluids from other mods can specify unique behavior without having to modify the fluid itself. Let's take two examples: #canHydrate(Level Accessor) and #supportsBoating.

#canHydrate can be modified by the FluidType, Fluid, or Block. This means that the FluidType can define general behavior to use while the Block may define specific behavior for if it can be hydrated.

#supportsBoating works similarly with FluidType and Boat. The FluidType can define in general if boats should work on it while a specific Boat can determine whether it actually can float on the fluid.

Entity Pathing

Entity pathing can now be specified for a given block or the underlying fluid using IForgeBlock#getBlockPathType, IForgeBlock#getAdjacentBlockPathType, FluidType#getBlockPathType, and FluidType#getAdjacentBlockPathType. For clarity, the regular block path type gets the raw type to be used if the adjacents are not specified. The adjacent path type specifically determines which one the entity will move to and will either default to OPEN, WALKABLE or BLOCKED internally. Typically, the entity will pathfind to the smallest positive malus of the type, where negative numbers are considered intraversable. Additional types can be created using BlockPathTypes#create.


IForgeEntity now contains many additional commands to handle fluid physics logic. These include the height, which fluid the entity's eyes are in, and if an entity is in a fluid type. All of these methods are properly documented in the extension. Note that these replace their vanilla counterparts and should be used instead.


IForgeLivingEntity contains two unique fluid logic handlers: how an entity "jumps" in a fluid, and how they "sink". These methods can currently only be overridden on an entity and not by the FluidType.


SoundAction is the abstracted sound manager for defining which sound to play in a given context. It works exactly like ToolAction where it simply defines a unique name that performs a given action. A new action can be created using SoundAction#get. Vanilla actions are defined within SoundActions (filling/empty bucket, vaporizing).

Fluid Interactions

Fluid interactions are handled by the FluidInteractionRegistry. Interactions define the behavior a block should handle in all directions besides down. The down direction must be defined by the fluid in FlowingFluid#spreadTo, and will not be considered in this registry. An interaction can be registered using #addInteraction in FMLCommonSetupEvent by specifying the source of the interaction (which is typically the fluid being replaced), and the interaction information which defines the conditions of when the interaction should occur and what the interaction should do.

private void commonSetup(FMLCommonSetupEvent event) {

   // Test Fluid + Lava (source/flowing) -> Gold Block (Test fluid source/flowing gets replaced)
   FluidInteractionRegistry.addInteraction(TEST_FLUID_TYPE.get(), new FluidInteractionRegistry.InteractionInformation(ForgeMod.LAVA_TYPE.get(), Blocks.GOLD_BLOCK.defaultBlockState()));


Gaseous Fluids

A Fluid is considered gaseous at room temperature if it is in the forge:gaseous tag. This is typically correlated with a negative density.