This is an old revision of the document!
[WIP] Understanding the Fabric Transfer API
This tutorial gives a brief overview of how the Fabric Transfer API works, and how you can use it for your own mods.
For any question, please go to the #fluid-volume-api-debating
channel on the discord server.
Creating a simple tank
Let's see how we can make a block entity contain some fluid:
public class MyTankBlockEntity extends BlockEntity { // This field is going to contain the amount, and the fluid variant (more on that in a bit). public final SingleVariantStorage<FluidVariant> fluidStorage = new SingleVariantStorage<>() { @Override protected FluidVariant getBlankVariant() { return FluidVariant.blank(); } @Override protected long getCapacity(FluidVariant variant) { // Here, you can pick your capacity depending on the fluid variant. // For example, if we want to store 8 buckets of any fluid: return 8 * FluidConstants.BUCKET; } @Override protected void onFinalCommit() { // Called after a successful insertion or extraction, markDirty to ensure the new amount and variant will be saved properly. markDirty(); } }; @Override public NbtCompound writeNbt(NbtCompound tag) { tag.put("fluidVariant", fluidStorage.variant.toNbt()); tag.putLong("amount", fluidStorage.amount); return super.writeNbt(tag); } @Override public void readNbt(NbtCompound tag) { super.readNbt(tag); fluidStorage.variant = FluidVariant.fromNbt(tag.getCompound("fluidVariant")); fluidStorage.amount = tag.getLong("amount"); } }
Alright, now we can contain some fluid, and we are properly saving it if it changes. Now, we must register ensure that other mods can properly interact with our tank:
BlockEntityType<MyTankBlockEntity> MY_TANK = // see block entity tutorial // Put this in your mod initializer, after you have created the block entity type: FluidStorage.SIDED.registerForBlockEntity((myTank, direction) -> myTank.fluidStorage, MY_TANK);
But wait, what is a FluidVariant ?
A FluidVariant
is what the Transfer API uses to represent the “type” of a fluid. It contains the Fluid
in the minecraft sense, and also an optional NBT compound tag:
// Creating a fluid variant from a fluid, without an NBT tag. FluidVariant waterVariant = FluidVariant.of(Fluids.WATER); waterVariant.getFluid() // returns Fluids.WATER waterVariant.copyNbt() // returns a copy of the optional nbt tag, in this case null // Creating a fluid variant from a fluid, with an NBT tag. NbtCompound customTag = new NbtCompound(); customTag.putBoolean("magic", true); FluidVariant magicWater = FluidVariant.of(Fluids.WATER, customTag);
Variants are always compared with .equals
, NEVER WITH ==
!
waterVariant.equals(waterVariant); // returns true waterVariant.equals(magicWater); // returns false // You can easily test if a variant has some fluid: waterVariant.isOf(Fluids.WATER); // returns true magicWater.isOf(Fluids.WATER); // returns true
They can easily be serialized to and from NBT or network packets:
// NBT NbtCompound compound = variant.toNbt(); FluidVariant variant = FluidVariant.fromNbt(compound); // Network packets variant.toPacket(buf); FluidVariant variant = FluidVariant.fromPacket(buf);
More on Fabric API Lookup
To understand what the call to FluidStorage.SIDED
does, please have a look at the Usage Example in the documentation of ''BlockApiLookup''.
The Fabric API Lookup system allows blocks to expose interfaces such as Storage<FluidVariant>
, so that pipes and other devices can use them.