Skip to main content
Version: 26.2.x

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.