User Tools

Site Tools


tutorial:transfer-api_simpletank

This is an old revision of the document!


Fabric Transfer API: Creating a simple tank

This article is part of a series on the Fabric 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 making a block entity and 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<ItemStack> 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<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) / 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 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);

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.

tutorial/transfer-api_simpletank.1665161706.txt.gz · Last modified: 2022/10/07 16:55 by nexus-dino