===== Fabric Transfer API: Creating a simple tank =====
//This article is part of a series on the Fabric Transfer API. [[tutorial:transfer-api|Link to the home page of the series]].//
=== 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);
CAUTION: make sure that you know the base understanding of [[tutorial:blockentity|making a block entity]] and [[tutorial:inventory|creating block entity inventories]] before continuing to read.
Let's see how we can make a block entity contain some fluid:
// Make sure you implement ExtendedScreenHandlerFactory because we need to write the pos of this entity...
public class MyTankBlockEntity extends BlockEntity implements ExtendedScreenHandlerFactory, ImplementedInventory {
private final DefaultedList inventory = DefaultedList.ofSize(3, ItemStack.EMPTY);
// This field is going to contain the amount, and the fluid variant (more on that in a bit).
public final SingleVariantStorage 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) / 81 // This will convert it to mB amount to be consistent;
}
@Override
protected void onFinalCommit() {
// Called after a successful insertion or extraction, markDirty to ensure the new amount and variant will be saved properly.
markDirty();
if (!world.isClient) {
var buf = PacketByteBufs.create();
// Write your data here.
PlayerLookup.tracking(MyTankBlockEntity.this).forEach(player -> {
ServerPlayNetworking.send(player, YOUR_IDENTIFIER, buf);
});
}
}
};
@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 the block entity in order to ensure that other mods can properly interact with our tank:
BlockEntityType 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);
=== More on Fabric API Lookup ===
To understand what the call to ''FluidStorage.SIDED'' does, please have a look at [[https://maven.fabricmc.net/docs/fabric-api-0.40.0+1.17/net/fabricmc/fabric/api/lookup/v1/block/BlockApiLookup.html|the Usage Example in the documentation of ''BlockApiLookup'']].
The Fabric API Lookup system allows blocks to expose interfaces such as ''Storage'', so that pipes and other devices can use them.