RyanHub – file viewer
filename: common/src/main/java/rearth/oritech/block/base/entity/FluidMultiblockGeneratorBlockEntity.java
branch: 1.21
back to repo
package rearth.oritech.block.base.entity;

import dev.architectury.fluid.FluidStack;
import dev.architectury.hooks.fluid.FluidStackHooks;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.fluid.Fluids;
import net.minecraft.inventory.Inventories;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.recipe.RecipeEntry;
import net.minecraft.registry.Registries;
import net.minecraft.registry.RegistryWrapper;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
import rearth.oritech.api.fluid.FluidApi;
import rearth.oritech.api.fluid.containers.SimpleFluidStorage;
import rearth.oritech.init.recipes.OritechRecipe;
import rearth.oritech.init.recipes.OritechRecipeType;
import rearth.oritech.network.NetworkContent;
import rearth.oritech.util.InventorySlotAssignment;
import rearth.oritech.util.StackContext;

import java.util.List;
import java.util.Optional;

public abstract class FluidMultiblockGeneratorBlockEntity extends MultiblockGeneratorBlockEntity implements FluidApi.BlockProvider {
    
    public final SimpleFluidStorage fluidStorage = new SimpleFluidStorage(4 * FluidStackHooks.bucketAmount(), this::markDirty) {
        @Override
        public long insert(FluidStack toInsert, boolean simulate) {
            if (toInsert.getFluid().equals(Fluids.WATER)) return 0L;    // to avoid mixups with players inserting water for boiler into main storage
            return super.insert(toInsert, simulate);
        }
    };
    
    public FluidMultiblockGeneratorBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state, int energyPerTick) {
        super(type, pos, state, energyPerTick);
    }
    
    @Override
    public void tick(World world, BlockPos pos, BlockState state, MachineBlockEntity blockEntity) {
        
        if (bucketInputAllowed() && !world.isClient && isActive(state)) {
            processBuckets();
        }
        
        super.tick(world, pos, state, blockEntity);
    }
    
    private void processBuckets() {
        
        var inStack = inventory.getStack(0);
        var canFill = this.fluidStorage.getAmount() < this.fluidStorage.getCapacity();
        
        if (!canFill || inStack.isEmpty() || inStack.getCount() > 1) return;
        
        var stackRef = new StackContext(inStack, updated -> inventory.setStack(0, updated));
        var candidate = FluidApi.ITEM.find(stackRef);
        if (candidate == null || !candidate.supportsExtraction()) return;
        
        var moved = FluidApi.transferFirst(candidate, fluidStorage, fluidStorage.getCapacity(), false);
        
        if (moved == 0) {
            // move stack
            var outStack = inventory.getStack(1);
            if (outStack.isEmpty()) {
                inventory.setStack(1, stackRef.getValue());
                inventory.setStack(0, ItemStack.EMPTY);
            } else if (outStack.getItem().equals(stackRef.getValue().getItem()) && outStack.getCount() < outStack.getMaxCount()) {
                outStack.increment(1);
                inventory.setStack(0, ItemStack.EMPTY);
            }
        }
    }
    
    @Override
    protected void tryConsumeInput() {
        
        if (isProducingSteam && (boilerStorage.getInStack().getAmount() == 0 || boilerStorage.getOutStack().getAmount() >= boilerStorage.getCapacity())) return;
        
        var recipeCandidate = getRecipe();
        if (recipeCandidate.isEmpty())
            currentRecipe = OritechRecipe.DUMMY;     // reset recipe when invalid or no input is given
        
        
        if (recipeCandidate.isPresent()) {
            // this is separate so that progress is not reset when out of energy
            var activeRecipe = recipeCandidate.get().value();
            currentRecipe = activeRecipe;
            consumeFluidRecipeInput(activeRecipe);
            
        }
    }
    
    protected void consumeFluidRecipeInput(OritechRecipe activeRecipe) {
        var recipeTime = (int) (currentRecipe.getTime() * getSpeedMultiplier() * (1 / getEfficiencyMultiplier()));
        progress = recipeTime;
        setCurrentMaxBurnTime(recipeTime);
        
        // remove inputs
        // correct amount and variant is already validated in getRecipe, so we can directly remove it
        var fluidStack = activeRecipe.getFluidInput();
        fluidStorage.extract(fluidStack, false);
    }
    
    // gets all recipe of target type, and only checks for matching liquids
    @Override
    protected Optional<RecipeEntry<OritechRecipe>> getRecipe() {
        return getRecipe(fluidStorage);
    }
    
    protected Optional<RecipeEntry<OritechRecipe>> getRecipe(SimpleFluidStorage checkedTank) {
        return getRecipe(checkedTank, world, getOwnRecipeType());
    }
    
    public static Optional<RecipeEntry<OritechRecipe>> getRecipe(FluidApi.SingleSlotStorage checkedTank, World world, OritechRecipeType ownType) {
        
        if (checkedTank.getStack().isEmpty()) return Optional.empty();
        
        var availableRecipes = world.getRecipeManager().listAllOfType(ownType);
        for (var recipeEntry : availableRecipes) {
            var recipe = recipeEntry.value();
            var recipeFluid = recipe.getFluidInput();
            if (recipeFluid.isFluidEqual(checkedTank.getStack()) && checkedTank.getStack().getAmount() >= recipeFluid.getAmount())
                return Optional.of(recipeEntry);
        }
        
        return Optional.empty();
    }
    
    @Override
    protected void writeNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registryLookup) {
        super.writeNbt(nbt, registryLookup);
        fluidStorage.writeNbt(nbt, "");
        Inventories.writeNbt(nbt, inventory.heldStacks, false, registryLookup);
    }
    
    @Override
    protected void readNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registryLookup) {
        super.readNbt(nbt, registryLookup);
        fluidStorage.readNbt(nbt, "");
        Inventories.readNbt(nbt, inventory.heldStacks, registryLookup);
    }
    
    @Override
    protected void sendNetworkEntry() {
        super.sendNetworkEntry();
        NetworkContent.MACHINE_CHANNEL.serverHandle(this).send(
          new NetworkContent.SingleVariantFluidSyncPacketAPI(pos, Registries.FLUID.getId(fluidStorage.getFluid()).toString(), fluidStorage.getAmount()));
    }
    
    @Override
    public boolean inputOptionsEnabled() {
        return false;
    }
    
    @Override
    public List<GuiSlot> getGuiSlots() {
        return List.of(new GuiSlot(0, 55, 35), new GuiSlot(1, 112, 35, true));
    }
    
    @Override
    public InventorySlotAssignment getSlotAssignments() {
        return new InventorySlotAssignment(0, 1, 1, 1);
    }
    
    public boolean bucketInputAllowed() {
        return true;
    }
    
    @Override
    public int getInventorySize() {
        return 2;
    }
    
    @Override
    public FluidApi.FluidStorage getFluidStorage(@Nullable Direction direction) {
        return fluidStorage;
    }
}