Skip to main content
Version: 26.2

Creating a Custom Fluid

Architectury provides base classes so you can add a working fluid - flowing, source, liquid block, and bucket - from common code. This page is the practical guide; Fluid Base Classes covers each class in more detail.

The pieces

A complete fluid is made of four registered objects, all described by a single ArchitecturyFluidAttributes:

PieceClassRegistry
Source fluidArchitecturyFlowingFluid.SourceRegistries.FLUID
Flowing fluidArchitecturyFlowingFluid.FlowingRegistries.FLUID
Liquid blockArchitecturyLiquidBlockRegistries.BLOCK
Bucket itemArchitecturyBucketItemRegistries.ITEM

Describing the fluid

Build the attributes with SimpleArchitecturyFluidAttributes. It takes suppliers for the flowing and source fluids (which you register next), then a chain of settings:

public static final ArchitecturyFluidAttributes OIL_ATTRIBUTES =
SimpleArchitecturyFluidAttributes.of(() -> OIL_FLOWING.get(), () -> OIL_STILL.get())
.sourceTexture(Identifier.fromNamespaceAndPath(MOD_ID, "block/oil_still"))
.flowingTexture(Identifier.fromNamespaceAndPath(MOD_ID, "block/oil_flow"))
.color(0xFF1A1A1A)
.density(2000)
.viscosity(2000)
.blockSupplier(() -> OIL_BLOCK)
.bucketItemSupplier(() -> OIL_BUCKET);

The builder exposes every fluid property, each returning the builder so you can chain them:

  • Rendering: sourceTexture, flowingTexture, overlayTexture, color, luminosity.
  • Physics: density, temperature, viscosity, lighterThanAir, slopeFindDistance, dropOff, tickDelay, explosionResistance, convertToSource.
  • Misc: rarity, fillSound, emptySound.
  • Links: blockSupplier / block, and bucketItemSupplier / bucketItem.

Registering the pieces

Register all four through DeferredRegister. The source and flowing fluids both take the shared attributes:

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));

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));
});

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));
});
Why suppliers are used

The attributes reference the fluids, and the fluids reference the attributes. Because the builder takes suppliers (() -> OIL_FLOWING.get(), blockSupplier(() -> OIL_BLOCK)), the references resolve lazily - so the order in which these fields initialize doesn't matter.

Remember the setId(...) on the block and bucket Properties, as with any item or block registration.

Next

Fluid Base Classes documents SimpleArchitecturyFluidAttributes (every builder setting), ArchitecturyFlowingFluid, ArchitecturyLiquidBlock, and ArchitecturyBucketItem in full.