Skip to main content
Version: 26.1.x

Fluid Base Classes

The classes in dev.architectury.core.fluid (plus the matching block and item) are what you extend or instantiate to build a custom fluid from common code. Creating a Custom Fluid is the step-by-step guide; this page is the reference for each class.

A fluid is described once by an ArchitecturyFluidAttributes, then four registered objects share it: a source fluid, a flowing fluid, a liquid block, and a bucket item.

SimpleArchitecturyFluidAttributes

The builder you use to describe a fluid. Create it from the flowing and source fluids, then chain setters; the result is an ArchitecturyFluidAttributes you pass to everything else.

Factory methods

MethodUse
of(Supplier<? extends Fluid> flowing, Supplier<? extends Fluid> source)Pass suppliers that return the fluids.
ofSupplier(Supplier<? extends Supplier<? extends Fluid>> flowing, Supplier<? extends Supplier<? extends Fluid>> source)Pass suppliers of your RegistrySupplier fields - convenient when the fluids are registered in the same class.
public static final ArchitecturyFluidAttributes OIL_ATTRIBUTES =
SimpleArchitecturyFluidAttributes.ofSupplier(() -> OIL_FLOWING, () -> OIL_STILL)
.blockSupplier(() -> OIL_BLOCK)
.bucketItem(() -> Optional.of(OIL_BUCKET.get()))
.sourceTexture(Identifier.fromNamespaceAndPath(MOD_ID, "block/oil_still"))
.flowingTexture(Identifier.fromNamespaceAndPath(MOD_ID, "block/oil_flow"))
.viscosity(2000)
.convertToSource(false);

Setters

Every setter returns the builder, so they chain. Defaults are what you get if you don't call them.

SetterTypeDefaultMeaning
convertToSource(boolean)booleanfalseWhether two adjacent source blocks form a new source block between them.
slopeFindDistance(int)int4How far the fluid looks for a downward slope when flowing.
dropOff(int)int1How much the fluid level drops per block as it spreads.
tickDelay(int)int5Ticks between flow updates; higher values slow the spread.
explosionResistance(float)float100.0Blast resistance of the fluid.
sourceTexture(Identifier)Identifier-Still texture.
flowingTexture(Identifier)Identifier-Flowing texture.
overlayTexture(Identifier)IdentifiernoneOverlay shown against glass/when submerged.
color(int)int (ARGB)0xffffffTint applied to the textures.
luminosity(int)int (0–15)0Light level the fluid emits.
density(int)int1000Fluid density.
temperature(int)int300Fluid temperature.
viscosity(int)int1000Resistance to flow; higher values flow more slowly.
lighterThanAir(boolean)booleanfalseIf true, the fluid rises rather than falls.
rarity(Rarity)RarityCOMMONRarity of the bucket item.
fillSound(SoundEvent)SoundEventBUCKET_FILLSound when filling a bucket.
emptySound(SoundEvent)SoundEventBUCKET_EMPTYSound when emptying a bucket.
block(...) / blockSupplier(...)see belowemptyThe liquid block for this fluid.
bucketItem(...) / bucketItemSupplier(...)see belowemptyThe bucket item for this fluid.

The block and bucket links each have a few overloads so you can pass whatever you have - a RegistrySupplier, a supplier of one, or a supplier of an Optional:

BlockBucket
block(RegistrySupplier<? extends LiquidBlock>)bucketItem(RegistrySupplier<Item>)
blockSupplier(Supplier<RegistrySupplier<? extends LiquidBlock>>)bucketItemSupplier(Supplier<RegistrySupplier<? extends Item>>)
block(Supplier<? extends Optional<? extends LiquidBlock>>)bucketItem(Supplier<? extends Optional<? extends Item>>)
Use suppliers for the links

The attributes reference the block/bucket and those reference the attributes back. Passing suppliers (blockSupplier(() -> OIL_BLOCK)) lets the references resolve lazily, so field initialization order doesn't matter.

ArchitecturyFluidAttributes

The interface produced by the builder - the type you store in a static final field and hand to the fluids, block, and bucket. You normally only build it, but it also exposes read accessors if you need to inspect a fluid's settings later (for example, to drive your own fluid rendering or tooltips): getName(), getColor(), getLuminosity(...), getSourceTexture(...), getFlowingTexture(...), getBucketItem(), getBlock(), and the rest mirror the setters above.

ArchitecturyFlowingFluid

The fluid itself. Register one of each nested type in Registries.FLUID, both taking the shared attributes:

ClassRole
ArchitecturyFlowingFluid.SourceThe still/source fluid.
ArchitecturyFlowingFluid.FlowingThe flowing fluid.
public static final RegistrySupplier<ArchitecturyFlowingFluid.Source> OIL_STILL =
FLUIDS.register("oil", () -> new ArchitecturyFlowingFluid.Source(OIL_ATTRIBUTES));

public static final RegistrySupplier<ArchitecturyFlowingFluid.Flowing> OIL_FLOWING =
FLUIDS.register("flowing_oil", () -> new ArchitecturyFlowingFluid.Flowing(OIL_ATTRIBUTES));

ArchitecturyLiquidBlock

The block placed in the world for the fluid. Register it in Registries.BLOCK.

ArchitecturyLiquidBlock(Supplier<? extends FlowingFluid> fluid, BlockBehaviour.Properties properties)
public static final RegistrySupplier<LiquidBlock> OIL_BLOCK = BLOCKS.register("oil", () -> {
ResourceKey<Block> key = ResourceKey.create(Registries.BLOCK,
Identifier.fromNamespaceAndPath(MOD_ID, "oil"));
return new ArchitecturyLiquidBlock(OIL_STILL, BlockBehaviour.Properties.of().liquid().setId(key));
});

ArchitecturyBucketItem

The bucket item for the fluid. Register it in Registries.ITEM, typically with craftRemainder(Items.BUCKET).

ArchitecturyBucketItem(Supplier<? extends Fluid> fluid, Item.Properties properties)
public static final RegistrySupplier<Item> OIL_BUCKET = ITEMS.register("oil_bucket", () -> {
ResourceKey<Item> key = ResourceKey.create(Registries.ITEM,
Identifier.fromNamespaceAndPath(MOD_ID, "oil_bucket"));
return new ArchitecturyBucketItem(OIL_STILL,
new Item.Properties().craftRemainder(Items.BUCKET).setId(key));
});

getContainedFluid() returns the Fluid the bucket holds. (You can also read it through the injected arch$getFluid() on any BucketItem.)

note

The setId(...) on the block and bucket Properties is required for every item/block registration - see DeferredRegister.

The block, bucket, and fluids behave identically on Fabric and NeoForge - you write and register them once, exactly as above.