This is an old revision of the document!
Table of Contents
Fabric Transfer API: Understanding Storage<T>
This article is part of a series on the Fabric Transfer API. Link to the home page of the series.
Storage<FluidVariant>
is a thing that contains fluids, it is what tanks, buckets, etc… implement and it is what pipe mods and others use to move fluids between containers.
You already used Storage<FluidVariant>
Now that you implemented a simple tank, let's have a look at what exactly this SingleVariantStorage<FluidVariant>
is.
public abstract class SingleVariantStorage<T extends TransferVariant<?>> extends ... implements SingleSlotStorage<T> { ... } public interface SingleSlotStorage<T> extends Storage<T>, ... { ... }
The takeaway here is that SingleVariantStorage<FluidVariant>
is a Storage<FluidVariant>
, which is what we registered to FluidStorage.SIDED
!
Retrieving a Storage<FluidVariant> from the world
Let's see how we can retrieve one from the world:
World world = ...; // Important: must be a server world!!! You must never query this on the client. BlockPos pos = ...; // The postion in the world Direction direction = ...; // The side for which we want a storage. @Nullable Storage<FluidVariant> storage = FluidStorage.SIDED.find(world, pos, direction); if (storage != null) { // Use the storage }
What you can do with a Storage<FluidVariant>
A look at Storage<T>
Let's have a look at Storage.java
:
public interface Storage<T> { // Try to insert a resource in the storage, return how much was inserted. long insert(T resource, long maxAmount, TransactionContext transaction); // Try to extract a resource from the storage, return how much was extracted. long extract(T resource, long maxAmount, TransactionContext transaction); // Iterate over the contents of this storage. default Iterable<StorageView<T>> iterable(TransactionContext transaction) { ... } ... }
This interface allows us to insert into a storage, extract from it, and read its contents.
First example: how to insert exactly one bucket of water into a storage
Storage<FluidVariant> storage = ...; FluidVariant water = FluidVariant.of(Fluids.WATER); // Open a transaction: this allows cancelling the operation if it doesn't go as expected. try (Transaction transaction = Transaction.openOuter()) { // Try to insert, will return how much was actually inserted. long amountInserted = storage.insert(water, FluidConstants.BUCKET, transaction); if (amountInserted == FluidConstants.BUCKET) { // "Commit" the transaction: this validates all the operations that were part of this transaction. // You should call this if you are satisfied with the result of the operation, and want to keep it. transaction.commit(); } else { // Doing nothing "aborts" the transaction: this cancels the insertion. // You should call this if you are not satisfied with the result of the operation, and want to abort it. } }
Second example: move exactly one bucket of lava from a storage to another storage
import static net.fabricmc.fabric.api.transfer.v1.fluid.FluidConstants.BUCKET; // a bit shorter to type :) Storage<FluidVariant> source, destination; FluidVariant lava = FluidVariant.of(Fluids.LAVA); try (Transaction transaction = Transaction.openOuter()) { if (source.extract(lava, BUCKET, transaction) == BUCKET && destination.insert(lava, BUCKET, transaction == BUCKET) { transaction.commit(); } }
Hopefully you understand why this works, and why this will never duplicate or void fluid.