filename:
fabric/src/main/java/rearth/oritech/fabric/FabricFluidApiImpl.java
branch:
1.21
back to repo
package rearth.oritech.fabric;
import com.google.common.collect.Streams;
import dev.architectury.fluid.FluidStack;
import dev.architectury.hooks.fluid.fabric.FluidStackHooksFabric;
import net.fabricmc.fabric.api.transfer.v1.context.ContainerItemContext;
import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant;
import net.fabricmc.fabric.api.transfer.v1.storage.Storage;
import net.fabricmc.fabric.api.transfer.v1.storage.StorageView;
import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction;
import net.fabricmc.fabric.api.transfer.v1.transaction.TransactionContext;
import net.fabricmc.fabric.api.transfer.v1.transaction.base.SnapshotParticipant;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.fluid.Fluids;
import net.minecraft.item.Item;
import net.minecraft.util.Pair;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.World;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import rearth.oritech.Oritech;
import rearth.oritech.util.StackContext;
import rearth.oritech.api.fluid.BlockFluidApi;
import rearth.oritech.api.fluid.FluidApi;
import rearth.oritech.api.fluid.ItemFluidApi;
import rearth.oritech.api.fluid.containers.DelegatingFluidStorage;
import rearth.oritech.api.fluid.containers.SimpleItemFluidStorage;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;
public class FabricFluidApiImpl implements BlockFluidApi, ItemFluidApi {
@SuppressWarnings("IfCanBeSwitch")
@Override
public void registerBlockEntity(Supplier<BlockEntityType<?>> typeSupplier) {
net.fabricmc.fabric.api.transfer.v1.fluid.FluidStorage.SIDED.registerForBlockEntity(
(entity, direction) -> {
var storage = ((FluidApi.BlockProvider) entity).getFluidStorage(direction);
if (storage == null) return null;
if (storage instanceof FluidApi.InOutSlotStorage inOutContainer) {
return InOutFluidContainerStorageWrapper.of(inOutContainer);
} else if (storage instanceof FluidApi.SingleSlotStorage singleContainer) {
return SingleSlotContainerStorageWrapper.of(singleContainer);
} else if (storage instanceof DelegatingFluidStorage delegatedStorage) {
return DelegatedContainerStorageWrapper.of(delegatedStorage);
}
Oritech.LOGGER.error("Error during fluid provider registration, unable to register a fluid container");
Oritech.LOGGER.error("Erroring container type is: {}", typeSupplier.get());
return null;
}, typeSupplier.get());
}
@Override
public void registerForItem(Supplier<Item> itemSupplier) {
net.fabricmc.fabric.api.transfer.v1.fluid.FluidStorage.ITEM.registerForItems(
(stack, context) -> SingleSlotContainerStorageWrapper.of(((FluidApi.ItemProvider) stack.getItem()).getFluidStorage(stack)),
itemSupplier.get()
);
}
@Override
public FluidApi.FluidStorage find(World world, BlockPos pos, @Nullable BlockState state, @Nullable BlockEntity entity, @Nullable Direction direction) {
var candidate = net.fabricmc.fabric.api.transfer.v1.fluid.FluidStorage.SIDED.find(world, pos, state, entity, direction);
return switch (candidate) {
case null -> null;
case SingleSlotContainerStorageWrapper wrapper -> wrapper.container;
case InOutFluidContainerStorageWrapper wrapper -> wrapper.container.getStorageForDirection(direction);
case DelegatedContainerStorageWrapper wrapper -> wrapper.storage;
default -> new FabricStorageWrapper(candidate, null);
};
}
@Override
public FluidApi.FluidStorage find(World world, BlockPos pos, @Nullable Direction direction) {
return find(world, pos, null, null, direction);
}
@Override
public FluidApi.FluidStorage find(StackContext stack) {
var context = ContainerItemContext.ofSingleSlot(new ItemStackStorage(stack));
var candidate = net.fabricmc.fabric.api.transfer.v1.fluid.FluidStorage.ITEM.find(stack.getValue(), context);
if (candidate == null) return null;
if (candidate instanceof SingleSlotContainerStorageWrapper wrapper && wrapper.container instanceof SimpleItemFluidStorage itemContainer) return itemContainer.withCallback(ignored -> stack.sync());
return new FabricStorageWrapper(candidate, stack);
}
// this is used to interact with fluid storages from other mods
public static class FabricStorageWrapper extends FluidApi.FluidStorage {
public final Storage<FluidVariant> storage;
public final @Nullable StackContext context;
public FabricStorageWrapper(Storage<FluidVariant> storage, @Nullable StackContext context) {
this.storage = storage;
this.context = context;
}
@Override
public long insert(FluidStack in, boolean simulate) {
try (var transaction = Transaction.openOuter()) {
var inserted = storage.insert(FluidStackHooksFabric.toFabric(in), in.getAmount(), transaction);
if (!simulate)
transaction.commit();
return inserted;
}
}
@Override
public long extract(FluidStack out, boolean simulate) {
try (var transaction = Transaction.openOuter()) {
var extracted = storage.extract(FluidStackHooksFabric.toFabric(out), out.getAmount(), transaction);
if (!simulate)
transaction.commit();
return extracted;
}
}
@Override
public List<FluidStack> getContent() {
return Streams
.stream(storage.iterator())
.map(FluidStackHooksFabric::fromFabric)
.toList();
}
@Override
public void update() {
if (context != null)
context.sync();
}
@Override
public long getCapacity() {
Oritech.LOGGER.warn("tried to access capacity of external container");
return 0L; // this should not be used I guess?
}
}
// this is used by other mods to interact with the oritech single slot fluid containers (machines/items)
public static class SingleSlotContainerStorageWrapper extends SnapshotParticipant<FluidStack> implements Storage<FluidVariant> {
public final FluidApi.SingleSlotStorage container;
private Set<StorageView<FluidVariant>> contentView;
public static SingleSlotContainerStorageWrapper of(@Nullable FluidApi.SingleSlotStorage container) {
if (container == null) return null;
return new SingleSlotContainerStorageWrapper(container);
}
public SingleSlotContainerStorageWrapper(FluidApi.SingleSlotStorage container) {
this.container = container;
}
@Override
public boolean supportsInsertion() {
return container.supportsInsertion();
}
@Override
public long insert(FluidVariant fluidVariant, long amount, TransactionContext transaction) {
updateSnapshots(transaction);
transaction.addCloseCallback((transactionContext, result) -> {
if (result.wasCommitted()) {
container.update();
}
});
return container.insert(FluidStackHooksFabric.fromFabric(fluidVariant, amount), false);
}
@Override
public boolean supportsExtraction() {
return container.supportsExtraction();
}
@Override
public long extract(FluidVariant fluidVariant, long amount, TransactionContext transaction) {
updateSnapshots(transaction);
transaction.addCloseCallback((transactionContext, result) -> {
if (result.wasCommitted()) {
container.update();
}
});
return container.extract(FluidStackHooksFabric.fromFabric(fluidVariant, amount), false);
}
@Override
public @NotNull Iterator<StorageView<FluidVariant>> iterator() {
if (contentView != null) return contentView.iterator();
contentView = Collections.singleton(new StorageView<FluidVariant>() {
@Override
public long extract(FluidVariant fluidVariant, long amount, TransactionContext transaction) {
return SingleSlotContainerStorageWrapper.this.extract(fluidVariant, amount, transaction);
}
@Override
public boolean isResourceBlank() {
return container.getStack().getFluid().equals(Fluids.EMPTY);
}
@Override
public FluidVariant getResource() {
return FluidStackHooksFabric.toFabric(container.getStack());
}
@Override
public long getAmount() {
return container.getStack().getAmount();
}
@Override
public long getCapacity() {
return container.getCapacity();
}
}.getUnderlyingView());
return contentView.iterator();
}
@Override
protected FluidStack createSnapshot() {
return container.getStack();
}
@Override
protected void readSnapshot(FluidStack fluidVariantResourceAmount) {
container.setStack(fluidVariantResourceAmount);
}
}
// this is used by other mods to interact with the oritech in/out fluid containers
public static class InOutFluidContainerStorageWrapper extends SnapshotParticipant<Pair<FluidStack, FluidStack>> implements Storage<FluidVariant> {
public final FluidApi.InOutSlotStorage container;
private List<@NotNull StorageView<FluidVariant>> storageViews;
public static InOutFluidContainerStorageWrapper of(FluidApi.InOutSlotStorage container) {
if (container == null) return null;
return new InOutFluidContainerStorageWrapper(container);
}
private InOutFluidContainerStorageWrapper(FluidApi.InOutSlotStorage container) {
this.container = container;
}
@Override
public boolean supportsInsertion() {
return container.supportsInsertion();
}
@Override
public long insert(FluidVariant fluidVariant, long amount, TransactionContext transaction) {
updateSnapshots(transaction);
transaction.addCloseCallback((transactionContext, result) -> {
if (result.wasCommitted()) {
container.update();
}
});
return container.insert(FluidStackHooksFabric.fromFabric(fluidVariant, amount), false);
}
@Override
public boolean supportsExtraction() {
return container.supportsExtraction();
}
@Override
public long extract(FluidVariant fluidVariant, long amount, TransactionContext transaction) {
updateSnapshots(transaction);
transaction.addCloseCallback((transactionContext, result) -> {
if (result.wasCommitted()) {
container.update();
}
});
return container.extract(FluidStackHooksFabric.fromFabric(fluidVariant, amount), false);
}
@Override
public @NotNull Iterator<StorageView<FluidVariant>> iterator() {
if (storageViews != null) return storageViews.iterator();
storageViews = List.of(new StorageView<FluidVariant>() {
// in storage
@Override
public long extract(FluidVariant fluidVariant, long l, TransactionContext transactionContext) {
return 0;
}
@Override
public boolean isResourceBlank() {
return container.getInStack().getFluid().equals(Fluids.EMPTY);
}
@Override
public FluidVariant getResource() {
return FluidStackHooksFabric.toFabric(container.getInStack());
}
@Override
public long getAmount() {
return container.getInStack().getAmount();
}
@Override
public long getCapacity() {
return container.getCapacity();
}
}.getUnderlyingView(), new StorageView<FluidVariant>() {
// out storage
@Override
public long extract(FluidVariant fluidVariant, long l, TransactionContext transactionContext) {
return InOutFluidContainerStorageWrapper.this.extract(fluidVariant, l, transactionContext);
}
@Override
public boolean isResourceBlank() {
return container.getOutStack().getFluid().equals(Fluids.EMPTY);
}
@Override
public FluidVariant getResource() {
return FluidStackHooksFabric.toFabric(container.getOutStack());
}
@Override
public long getAmount() {
return container.getOutStack().getAmount();
}
@Override
public long getCapacity() {
return container.getCapacity();
}
}.getUnderlyingView());
return storageViews.iterator();
}
@Override
protected Pair<FluidStack, FluidStack> createSnapshot() {
return new Pair<>(container.getInStack(), container.getOutStack());
}
@Override
protected void readSnapshot(Pair<FluidStack, FluidStack> snapshot) {
container.setInStack(snapshot.getLeft());
container.setOutStack(snapshot.getRight());
}
}
public static class DelegatedContainerStorageWrapper extends SnapshotParticipant<List<FluidStack>> implements Storage<FluidVariant> {
private final DelegatingFluidStorage storage;
public static DelegatedContainerStorageWrapper of(DelegatingFluidStorage storage) {
if (storage == null) return null;
return new DelegatedContainerStorageWrapper(storage);
}
private DelegatedContainerStorageWrapper(DelegatingFluidStorage storage) {
this.storage = storage;
}
@Override
public boolean supportsInsertion() {
return storage.supportsInsertion();
}
@Override
public long insert(FluidVariant fluidVariant, long l, TransactionContext transaction) {
updateSnapshots(transaction);
transaction.addCloseCallback((transactionContext, result) -> {
if (result.wasCommitted()) {
storage.update();
}
});
return storage.insert(FluidStackHooksFabric.fromFabric(fluidVariant, l), false);
}
@Override
public boolean supportsExtraction() {
return storage.supportsExtraction();
}
@Override
public long extract(FluidVariant fluidVariant, long l, TransactionContext transaction) {
updateSnapshots(transaction);
transaction.addCloseCallback((transactionContext, result) -> {
if (result.wasCommitted()) {
storage.update();
}
});
return storage.extract(FluidStackHooksFabric.fromFabric(fluidVariant, l), false);
}
@Override
public @NotNull Iterator<StorageView<FluidVariant>> iterator() {
return storage.getContent().stream().map(stack -> new StorageView<FluidVariant>() {
@Override
public long extract(FluidVariant fluidVariant, long l, TransactionContext transactionContext) {
return DelegatedContainerStorageWrapper.this.extract(fluidVariant, l, transactionContext);
}
@Override
public boolean isResourceBlank() {
return stack.isEmpty();
}
@Override
public FluidVariant getResource() {
return FluidStackHooksFabric.toFabric(stack);
}
@Override
public long getAmount() {
return stack.getAmount();
}
@Override
public long getCapacity() {
return storage.getCapacity();
}
}.getUnderlyingView()).iterator();
}
@Override
protected List<FluidStack> createSnapshot() {
return storage.getContent();
}
@Override
protected void readSnapshot(List<FluidStack> fluidStacks) {
storage.setContent(fluidStacks);
}
}
}