RyanHub – file viewer
filename: neoforge/src/main/java/rearth/oritech/neoforge/NeoforgeFluidApiImpl.java
branch: 1.21
back to repo
package rearth.oritech.neoforge;

import dev.architectury.fluid.FluidStack;
import dev.architectury.hooks.fluid.forge.FluidStackHooksForge;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.World;
import net.neoforged.neoforge.capabilities.Capabilities;
import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent;
import net.neoforged.neoforge.fluids.capability.IFluidHandler;
import net.neoforged.neoforge.fluids.capability.IFluidHandlerItem;
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.ArrayList;
import java.util.List;
import java.util.function.Supplier;

public class NeoforgeFluidApiImpl implements BlockFluidApi, ItemFluidApi {
    
    private final List<Supplier<BlockEntityType<?>>> registeredBlockEntities = new ArrayList<>();
    private final List<Supplier<Item>> registeredItems = new ArrayList<>();
    
    @Override
    public void registerBlockEntity(Supplier<BlockEntityType<?>> typeSupplier) {
        registeredBlockEntities.add(typeSupplier);
    }
    
    @SuppressWarnings("IfCanBeSwitch")
    public void registerEvent(RegisterCapabilitiesEvent event) {
        for (var supplied : registeredBlockEntities) {
            event.registerBlockEntity(Capabilities.FluidHandler.BLOCK, supplied.get(), (entity, direction) -> {
                
                var storage = ((FluidApi.BlockProvider) entity).getFluidStorage(direction);
                
                if (storage == null) return null;
                
                if (storage instanceof FluidApi.InOutSlotStorage inOutContainer) {
                    return InOutContainerStorageWrapper.of(inOutContainer);
                } else if (storage instanceof FluidApi.SingleSlotStorage singleContainer) {
                    return SingleSlotContainerStorageWrapper.of(singleContainer);
                } else if (storage instanceof DelegatingFluidStorage delegatingFluidStorage) {
                    return new DelegatingContainerStorageWrapper(delegatingFluidStorage);
                }
                
                Oritech.LOGGER.error("Error during fluid provider registration, unable to register a fluid container");
                Oritech.LOGGER.error("Erroring container type is: {}", entity);
                
                return null;
            });
        }
        
        
        for (var supplied : registeredItems) {
            event.registerItem(Capabilities.FluidHandler.ITEM,
              (stack, ignored) -> FluidContainerItemWrapper.of(((FluidApi.ItemProvider) stack.getItem()).getFluidStorage(stack), stack),
              supplied.get());
        }
    }
    
    @Override
    public void registerForItem(Supplier<Item> itemSupplier) {
        registeredItems.add(itemSupplier);
    }
    
    @Override
    public FluidApi.FluidStorage find(World world, BlockPos pos, @Nullable BlockState state, @Nullable BlockEntity entity, @Nullable Direction direction) {
        var candidate = world.getCapability(Capabilities.FluidHandler.BLOCK, pos, state, entity, direction);
        return switch (candidate) {
            case null -> null;
            case SingleSlotContainerStorageWrapper wrapper -> wrapper.container;
            case InOutContainerStorageWrapper wrapper -> wrapper.container.getStorageForDirection(direction);
            default -> new NeoforgeStorageWrapper(candidate);
        };
    }
    
    @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 candidate = stack.getValue().getCapability(Capabilities.FluidHandler.ITEM);
        if (candidate == null) return null;
        if (candidate instanceof SingleSlotContainerStorageWrapper wrapper && wrapper.container instanceof SimpleItemFluidStorage itemContainer) return itemContainer.withCallback(ignored -> stack.sync());
        return new NeoforgeItemStorageWrapper(candidate, stack);
    }
    
    // used to interact with tanks from other mods
    public static class NeoforgeStorageWrapper extends FluidApi.FluidStorage {
        
        private final IFluidHandler storage;
        
        public NeoforgeStorageWrapper(IFluidHandler storage) {
            this.storage = storage;
        }
        
        @Override
        public long insert(FluidStack toInsert, boolean simulate) {
            var action = simulate ? IFluidHandler.FluidAction.SIMULATE : IFluidHandler.FluidAction.EXECUTE;
            return storage.fill(FluidStackHooksForge.toForge(toInsert), action);
        }
        
        @Override
        public long extract(FluidStack toExtract, boolean simulate) {
            var action = simulate ? IFluidHandler.FluidAction.SIMULATE : IFluidHandler.FluidAction.EXECUTE;
            return storage.drain(FluidStackHooksForge.toForge(toExtract), action).getAmount();
        }
        
        @Override
        public List<FluidStack> getContent() {
            
            var content = new ArrayList<FluidStack>();
            for (int i = 0; i < storage.getTanks(); i++) {
                var tank = storage.getFluidInTank(i);
                content.add(FluidStackHooksForge.fromForge(tank));
            }
            
            return content;
        }
        
        @Override
        public void update() {
        }
        
        @Override
        public long getCapacity() {
            Oritech.LOGGER.warn("tried to access capacity of external container");
            return 0L;
        }
    }
    
    // used to interact with items from other mods
    public static class NeoforgeItemStorageWrapper extends NeoforgeStorageWrapper {
        
        private final StackContext stack;
        private final IFluidHandlerItem handler;
        
        public NeoforgeItemStorageWrapper(IFluidHandlerItem storage, StackContext stack) {
            super(storage);
            this.stack = stack;
            this.handler = storage;
        }
        
        @Override
        public void update() {
            super.update();
            stack.setValue(handler.getContainer());
            stack.sync();
        }
    }
    
    // this is used by other mods to interact with the oritech fluid containers
    public static class SingleSlotContainerStorageWrapper implements IFluidHandler {
        
        public final FluidApi.SingleSlotStorage container;
        
        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 int getTanks() {
            return 1;
        }
        
        @Override
        public net.neoforged.neoforge.fluids.@NotNull FluidStack getFluidInTank(int i) {
            return FluidStackHooksForge.toForge(container.getStack());
        }
        
        @Override
        public int getTankCapacity(int i) {
            return (int) container.getCapacity();
        }
        
        @Override
        public boolean isFluidValid(int i, net.neoforged.neoforge.fluids.@NotNull FluidStack fluidStack) {
            return true;
        }
        
        @Override
        public int fill(net.neoforged.neoforge.fluids.@NotNull FluidStack fluidStack, @NotNull FluidAction fluidAction) {
            
            var result = (int) container.insert(FluidStackHooksForge.fromForge(fluidStack), fluidAction.simulate());
            
            if (result > 0 && fluidAction.execute())
                container.update();
            
            return result;
        }
        
        @Override
        public net.neoforged.neoforge.fluids.@NotNull FluidStack drain(net.neoforged.neoforge.fluids.@NotNull FluidStack fluidStack, @NotNull FluidAction fluidAction) {
            var extractedAmount = container.extract(FluidStackHooksForge.fromForge(fluidStack), fluidAction.simulate());
            
            if (extractedAmount > 0 && fluidAction.execute())
                container.update();
            
            return new net.neoforged.neoforge.fluids.FluidStack(fluidStack.getFluid(), (int) extractedAmount);
        }
        
        @Override
        public net.neoforged.neoforge.fluids.@NotNull FluidStack drain(int i, @NotNull FluidAction fluidAction) {
            var extractedAmount =  container.extract(container.getStack().copyWithAmount(i), fluidAction.simulate());
            
            if (extractedAmount > 0 && fluidAction.execute())
                container.update();
            
            return new net.neoforged.neoforge.fluids.FluidStack(container.getStack().getFluid(), (int) extractedAmount);
        }
    }
    
    // this is used by other mods to access an oritech in/out container
    public static class InOutContainerStorageWrapper implements IFluidHandler {
        
        public final FluidApi.InOutSlotStorage container;
        
        public static InOutContainerStorageWrapper of(FluidApi.InOutSlotStorage container) {
            if (container == null) return null;
            return new InOutContainerStorageWrapper(container);
        }
        
        public InOutContainerStorageWrapper(FluidApi.InOutSlotStorage container) {
            this.container = container;
        }
        
        @Override
        public int getTanks() {
            return 2;
        }
        
        @Override
        public net.neoforged.neoforge.fluids.@NotNull FluidStack getFluidInTank(int i) {
            if (i > 1) return net.neoforged.neoforge.fluids.FluidStack.EMPTY;
            return FluidStackHooksForge.toForge(container.getContent().get(i));
        }
        
        @Override
        public int getTankCapacity(int i) {
            return (int) container.getCapacity();
        }
        
        @Override
        public boolean isFluidValid(int i, net.neoforged.neoforge.fluids.@NotNull FluidStack fluidStack) {
            return true;
        }
        
        @Override
        public int fill(net.neoforged.neoforge.fluids.@NotNull FluidStack fluidStack, @NotNull FluidAction fluidAction) {
            var result = (int) container.insert(FluidStackHooksForge.fromForge(fluidStack), fluidAction.simulate());
            
            if (result > 0 && fluidAction.execute())
                container.update();
            
            return result;
        }
        
        @Override
        public net.neoforged.neoforge.fluids.@NotNull FluidStack drain(net.neoforged.neoforge.fluids.@NotNull FluidStack fluidStack, @NotNull FluidAction fluidAction) {
            var extractedAmount = container.extract(FluidStackHooksForge.fromForge(fluidStack), fluidAction.simulate());
            
            if (extractedAmount > 0 && fluidAction.execute())
                container.update();
            
            return new net.neoforged.neoforge.fluids.FluidStack(fluidStack.getFluid(), (int) extractedAmount);
        }
        
        @Override
        public net.neoforged.neoforge.fluids.@NotNull FluidStack drain(int i, @NotNull FluidAction fluidAction) {
            var extractedAmount =  container.extract(container.getOutStack().copyWithAmount(i), fluidAction.simulate());
            
            if (extractedAmount > 0 && fluidAction.execute())
                container.update();
            
            return new net.neoforged.neoforge.fluids.FluidStack(container.getOutStack().getFluid(), (int) extractedAmount);
        }
    }
    
    public static class DelegatingContainerStorageWrapper implements IFluidHandler {
        
        private final DelegatingFluidStorage container;
        
        public static DelegatingContainerStorageWrapper of(DelegatingFluidStorage container) {
            if (container == null) return null;
            return new DelegatingContainerStorageWrapper(container);
        }
        
        private DelegatingContainerStorageWrapper(DelegatingFluidStorage container) {
            this.container = container;
        }
        
        @Override
        public int getTanks() {
            return container.getContent().size();
        }
        
        @Override
        public net.neoforged.neoforge.fluids.@NotNull FluidStack getFluidInTank(int i) {
            return FluidStackHooksForge.toForge(container.getContent().get(i));
        }
        
        @Override
        public int getTankCapacity(int i) {
            return (int) container.getCapacity();
        }
        
        @Override
        public boolean isFluidValid(int i, net.neoforged.neoforge.fluids.@NotNull FluidStack fluidStack) {
            return true;
        }
        
        @Override
        public int fill(net.neoforged.neoforge.fluids.@NotNull FluidStack fluidStack, FluidAction fluidAction) {
            var result = (int) container.insert(FluidStackHooksForge.fromForge(fluidStack), fluidAction.simulate());
            
            if (result > 0 && fluidAction.execute())
                container.update();
            
            return result;
        }
        
        @Override
        public net.neoforged.neoforge.fluids.@NotNull FluidStack drain(net.neoforged.neoforge.fluids.@NotNull FluidStack fluidStack, @NotNull FluidAction fluidAction) {
            var extractedAmount = container.extract(FluidStackHooksForge.fromForge(fluidStack), fluidAction.simulate());
            
            if (extractedAmount > 0 && fluidAction.execute())
                container.update();
            
            return new net.neoforged.neoforge.fluids.FluidStack(fluidStack.getFluid(), (int) extractedAmount);
        }
        
        @Override
        public net.neoforged.neoforge.fluids.@NotNull FluidStack drain(int i, @NotNull FluidAction fluidAction) {
            var extractedAmount =  container.extract(container.getContent().getLast().copyWithAmount(i), fluidAction.simulate());
            
            if (extractedAmount > 0 && fluidAction.execute())
                container.update();
            
            return new net.neoforged.neoforge.fluids.FluidStack(container.getContent().getLast().getFluid(), (int) extractedAmount);
        }
    }
    
    public static class FluidContainerItemWrapper extends SingleSlotContainerStorageWrapper implements IFluidHandlerItem {
        
        private final ItemStack stack;
        
        public static FluidContainerItemWrapper of(FluidApi.SingleSlotStorage container, ItemStack stack) {
            if (container == null || stack == null || stack.isEmpty()) return null;
            return new FluidContainerItemWrapper(container, stack);
        }
        
        public FluidContainerItemWrapper(FluidApi.SingleSlotStorage container, ItemStack stack) {
            super(container);
            this.stack = stack;
        }
        
        @Override
        public @NotNull ItemStack getContainer() {
            return stack;
        }
        
    }
}