From fd92620312304e59e44fcee8df29a32e6a2d33b3 Mon Sep 17 00:00:00 2001 From: techno-sam <77073745+techno-sam@users.noreply.github.com> Date: Tue, 13 Aug 2024 05:09:43 -0700 Subject: [PATCH] fabric and forge fuel salepoint implementation --- .../createnumismatics/compat/Mods.java | 2 +- .../FluidSalepointTargetBehaviour.java | 2 +- .../IFilteringSalepointBehaviour.java | 25 ++ .../salepoint/states/FluidSalepointState.java | 14 + .../registry/NumismaticsBlocks.java | 10 +- ...PortableFuelInterfaceBlockEntityMixin.java | 228 ++++++++++++++++ .../main/resources/numismatics.mixins.json | 4 +- ...PortableFuelInterfaceBlockEntityMixin.java | 247 ++++++++++++++++++ .../main/resources/numismatics.mixins.json | 3 + gradle.properties | 2 +- 10 files changed, 531 insertions(+), 6 deletions(-) create mode 100644 common/src/main/java/dev/ithundxr/createnumismatics/content/salepoint/behaviours/IFilteringSalepointBehaviour.java create mode 100644 fabric/src/main/java/dev/ithundxr/createnumismatics/fabric/mixin/compat/PortableFuelInterfaceBlockEntityMixin.java create mode 100644 forge/src/main/java/dev/ithundxr/createnumismatics/forge/mixin/compat/PortableFuelInterfaceBlockEntityMixin.java diff --git a/common/src/main/java/dev/ithundxr/createnumismatics/compat/Mods.java b/common/src/main/java/dev/ithundxr/createnumismatics/compat/Mods.java index 7f755d4..ff4abec 100644 --- a/common/src/main/java/dev/ithundxr/createnumismatics/compat/Mods.java +++ b/common/src/main/java/dev/ithundxr/createnumismatics/compat/Mods.java @@ -35,7 +35,7 @@ public enum Mods { EMI("emi"), JEI("jei"), CREATEADDITION("createaddition"), - ; + RAILWAYS("railways"); public final boolean isLoaded; public final boolean requiredForDataGen; diff --git a/common/src/main/java/dev/ithundxr/createnumismatics/content/salepoint/behaviours/FluidSalepointTargetBehaviour.java b/common/src/main/java/dev/ithundxr/createnumismatics/content/salepoint/behaviours/FluidSalepointTargetBehaviour.java index 688c58f..77f357a 100644 --- a/common/src/main/java/dev/ithundxr/createnumismatics/content/salepoint/behaviours/FluidSalepointTargetBehaviour.java +++ b/common/src/main/java/dev/ithundxr/createnumismatics/content/salepoint/behaviours/FluidSalepointTargetBehaviour.java @@ -25,7 +25,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public abstract class FluidSalepointTargetBehaviour extends SalepointTargetBehaviour { +public abstract class FluidSalepointTargetBehaviour extends SalepointTargetBehaviour implements IFilteringSalepointBehaviour { public FluidSalepointTargetBehaviour(SmartBlockEntity be) { super(be); } diff --git a/common/src/main/java/dev/ithundxr/createnumismatics/content/salepoint/behaviours/IFilteringSalepointBehaviour.java b/common/src/main/java/dev/ithundxr/createnumismatics/content/salepoint/behaviours/IFilteringSalepointBehaviour.java new file mode 100644 index 0000000..9e63518 --- /dev/null +++ b/common/src/main/java/dev/ithundxr/createnumismatics/content/salepoint/behaviours/IFilteringSalepointBehaviour.java @@ -0,0 +1,25 @@ +/* + * Numismatics + * Copyright (c) 2024 The Railways Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package dev.ithundxr.createnumismatics.content.salepoint.behaviours; + +public interface IFilteringSalepointBehaviour { + default boolean canSetFilter(Object filter) { + return true; + } +} diff --git a/common/src/main/java/dev/ithundxr/createnumismatics/content/salepoint/states/FluidSalepointState.java b/common/src/main/java/dev/ithundxr/createnumismatics/content/salepoint/states/FluidSalepointState.java index 9270fc6..0941c9b 100644 --- a/common/src/main/java/dev/ithundxr/createnumismatics/content/salepoint/states/FluidSalepointState.java +++ b/common/src/main/java/dev/ithundxr/createnumismatics/content/salepoint/states/FluidSalepointState.java @@ -23,6 +23,8 @@ import dev.architectury.injectables.annotations.ExpectPlatform; import dev.ithundxr.createnumismatics.compat.computercraft.ComputerCraftProxy; import dev.ithundxr.createnumismatics.content.backend.ReasonHolder; +import dev.ithundxr.createnumismatics.content.salepoint.SalepointBlockEntity; +import dev.ithundxr.createnumismatics.content.salepoint.behaviours.IFilteringSalepointBehaviour; import dev.ithundxr.createnumismatics.content.salepoint.behaviours.SalepointTargetBehaviour; import dev.ithundxr.createnumismatics.content.salepoint.widgets.SalepointFluidConfigWidget; import dev.ithundxr.createnumismatics.content.salepoint.widgets.SalepointFluidDisplayWidget; @@ -86,6 +88,18 @@ public final boolean setFilter(MultiloaderFluidStack filter, Level salepointLeve if (!canChangeFilterTo(filter)) return false; + if (!filter.isEmpty() && salepointLevel.getBlockEntity(salepointPos) instanceof SalepointBlockEntity salepointBE) { + BlockPos targetedPos = salepointBE.getTargetedPos(); + if (targetedPos != null) { + var behaviour = getBehaviour(salepointLevel, targetedPos); + if (behaviour instanceof IFilteringSalepointBehaviour filteringSalepointBehaviour) { + if (!filteringSalepointBehaviour.canSetFilter(filter)) { + return false; + } + } + } + } + setFilterInternal(filter, salepointLevel, salepointPos, player); this.filter = filter.copy(); diff --git a/common/src/main/java/dev/ithundxr/createnumismatics/registry/NumismaticsBlocks.java b/common/src/main/java/dev/ithundxr/createnumismatics/registry/NumismaticsBlocks.java index e5da3bb..fafcaae 100644 --- a/common/src/main/java/dev/ithundxr/createnumismatics/registry/NumismaticsBlocks.java +++ b/common/src/main/java/dev/ithundxr/createnumismatics/registry/NumismaticsBlocks.java @@ -34,10 +34,12 @@ import dev.ithundxr.createnumismatics.content.vendor.VendorBlock; import dev.ithundxr.createnumismatics.multiloader.CommonTags; import net.minecraft.client.renderer.RenderType; +import net.minecraft.core.BlockPos; import net.minecraft.world.item.Rarity; -import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.block.SoundType; import net.minecraft.world.level.block.state.BlockBehaviour.Properties; +import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.MapColor; import net.minecraft.world.level.storage.loot.LootPool; import net.minecraft.world.level.storage.loot.entries.LootItem; @@ -142,7 +144,7 @@ public class NumismaticsBlocks { .initialProperties(SharedProperties::softMetal) .properties(p -> p.strength(1.0F, 3600000.0F)) // Unexplodable .properties(Properties::requiresCorrectToolForDrops) - .properties(p -> p.isRedstoneConductor(Blocks::never)) + .properties(p -> p.isRedstoneConductor(NumismaticsBlocks::never)) .transform(pickaxeOnly()) .lang("Salepoint") .transform(BuilderTransformers.salepoint()) @@ -154,4 +156,8 @@ public static void register() { // load the class and register everything Numismatics.LOGGER.info("Registering blocks for " + Numismatics.NAME); } + + private static boolean never(BlockState state, BlockGetter blockGetter, BlockPos pos) { + return false; + } } diff --git a/fabric/src/main/java/dev/ithundxr/createnumismatics/fabric/mixin/compat/PortableFuelInterfaceBlockEntityMixin.java b/fabric/src/main/java/dev/ithundxr/createnumismatics/fabric/mixin/compat/PortableFuelInterfaceBlockEntityMixin.java new file mode 100644 index 0000000..5416852 --- /dev/null +++ b/fabric/src/main/java/dev/ithundxr/createnumismatics/fabric/mixin/compat/PortableFuelInterfaceBlockEntityMixin.java @@ -0,0 +1,228 @@ +/* + * Numismatics + * Copyright (c) 2024 The Railways Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package dev.ithundxr.createnumismatics.fabric.mixin.compat; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.railwayteam.railways.content.fuel.LiquidFuelTrainHandler; +import com.railwayteam.railways.content.fuel.psi.PortableFuelInterfaceBlockEntity; +import com.railwayteam.railways.content.fuel.psi.PortableFuelInterfaceBlockEntity.InterfaceFluidHandler; +import com.simibubi.create.content.contraptions.actors.psi.PortableStorageInterfaceBlockEntity; +import com.simibubi.create.foundation.blockEntity.behaviour.BlockEntityBehaviour; +import dev.ithundxr.createnumismatics.Numismatics; +import dev.ithundxr.createnumismatics.annotation.mixin.ConditionalMixin; +import dev.ithundxr.createnumismatics.compat.Mods; +import dev.ithundxr.createnumismatics.content.salepoint.behaviours.FluidSalepointTargetBehaviour; +import dev.ithundxr.createnumismatics.content.salepoint.behaviours.SalepointTargetBehaviour; +import dev.ithundxr.createnumismatics.content.salepoint.containers.fabric.InvalidatableWrappingFluidBufferTank; +import dev.ithundxr.createnumismatics.content.salepoint.states.ISalepointState; +import dev.ithundxr.createnumismatics.fabric.mixin.WrappedStorageAccessor; +import dev.ithundxr.createnumismatics.multiloader.fluid.MultiloaderFluidStack; +import dev.ithundxr.createnumismatics.multiloader.fluid.fabric.MultiloaderFluidStackImpl; +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.transaction.Transaction; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; + +import java.util.List; + +@ConditionalMixin(mods = Mods.RAILWAYS) +@Mixin(PortableFuelInterfaceBlockEntity.class) +@SuppressWarnings("UnstableApiUsage") +public abstract class PortableFuelInterfaceBlockEntityMixin extends PortableStorageInterfaceBlockEntity { + + @Shadow(remap = false) protected InterfaceFluidHandler capability; + @Unique + private FluidSalepointTargetBehaviour railway$salepointBehaviour; + + @Unique + @Nullable + private Storage railway$contraptionStorage; + + private PortableFuelInterfaceBlockEntityMixin(BlockEntityType type, BlockPos pos, BlockState state) { + super(type, pos, state); + } + + @WrapOperation( + method = "startTransferringTo", + at = @At( + value = "INVOKE", + target = "Lcom/railwayteam/railways/content/fuel/psi/PortableFuelInterfaceBlockEntity$InterfaceFluidHandler;setWrapped(Lnet/fabricmc/fabric/api/transfer/v1/storage/Storage;)V" + ), + remap = false + ) + @SuppressWarnings("unchecked") + private void keepControl(InterfaceFluidHandler instance, Storage wrapped, Operation original) { + Storage existingWrapped = ((WrappedStorageAccessor) capability).getWrapped(); + if (!(existingWrapped instanceof InvalidatableWrappingFluidBufferTank)) { + original.call(instance, wrapped); + } + railway$contraptionStorage = wrapped; + } + + @WrapOperation( + method = "stopTransferring", + at = @At( + value = "INVOKE", + target = "Lcom/railwayteam/railways/content/fuel/psi/PortableFuelInterfaceBlockEntity$InterfaceFluidHandler;setWrapped(Lnet/fabricmc/fabric/api/transfer/v1/storage/Storage;)V" + ), + remap = false + ) + @SuppressWarnings("unchecked") + private void keepControl2(InterfaceFluidHandler instance, Storage wrapped, Operation original) { + Storage existingWrapped = ((WrappedStorageAccessor) capability).getWrapped(); + if (!(existingWrapped instanceof InvalidatableWrappingFluidBufferTank)) { + original.call(instance, wrapped); + } + railway$contraptionStorage = null; + } + + @Override + public boolean canTransfer() { + return super.canTransfer() || railway$salepointBehaviour.isControlledBySalepoint(); + } + + @Override + public void addBehaviours(List behaviours) { + super.addBehaviours(behaviours); + railway$salepointBehaviour = new FluidSalepointTargetBehaviour(this) { + private boolean underControl = false; + + @Override + protected boolean isUnderControlInternal(@NotNull ISalepointState state) { + return underControl; // id checks done by super + } + + @Override + @SuppressWarnings("unchecked") + protected void ensureUnderControlInternal(@NotNull ISalepointState state) { + ((WrappedStorageAccessor) capability).setWrapped((InvalidatableWrappingFluidBufferTank) state.getBuffer()); + + if (!underControl) { + underControl = true; + notifyUpdate(); + } + } + + @Override + @SuppressWarnings("unchecked") + protected void relinquishControlInternal(@NotNull ISalepointState state) { + if (railway$contraptionStorage != null) { + ((WrappedStorageAccessor) capability).setWrapped(railway$contraptionStorage); + } else { + ((WrappedStorageAccessor) capability).setWrapped(Storage.empty()); + } + + if (underControl) { + underControl = false; + notifyUpdate(); + } + } + + @Override + public boolean hasSpaceFor(@NotNull MultiloaderFluidStack object) { + if (railway$contraptionStorage == null) + return false; + + if (!railway$contraptionStorage.supportsInsertion()) + return false; + + try (Transaction transaction = Transaction.openOuter()) { + if (railway$contraptionStorage.insert(((MultiloaderFluidStackImpl) object).getType(), object.getAmount(), transaction) != object.getAmount()) { + return false; + } + } + + return true; + } + + @Override + public boolean doPurchase(@NotNull MultiloaderFluidStack object, @NotNull PurchaseProvider purchaseProvider) { + if (railway$contraptionStorage == null) + return false; + + if (!hasSpaceFor(object)) + return false; + + List extracted = purchaseProvider.extract(); + try (Transaction transaction = Transaction.openOuter()) { + for (MultiloaderFluidStack stack : extracted) { + if (railway$contraptionStorage.insert(((MultiloaderFluidStackImpl) stack).getType(), stack.getAmount(), transaction) != stack.getAmount()) { + Numismatics.LOGGER.error("Failed to insert fluid into contraption storage, despite having space."); + return false; + } + } + transaction.commit(); + } + + return true; + } + + @Override + public void read(@NotNull CompoundTag nbt, boolean clientPacket) { + super.read(nbt, clientPacket); + + underControl = nbt.getBoolean("SalepointUnderControl"); + } + + @Override + public void write(@NotNull CompoundTag nbt, boolean clientPacket) { + super.write(nbt, clientPacket); + + nbt.putBoolean("SalepointUnderControl", underControl); + } + + @Override + public boolean canSetFilter(Object filter) { + if (!super.canSetFilter(filter)) + return false; + + if (!(filter instanceof MultiloaderFluidStackImpl fs)) + return false; + + return LiquidFuelTrainHandler.isFuel(fs.getFluid()); + } + }; + + behaviours.add(railway$salepointBehaviour); + } + + @Mixin(InterfaceFluidHandler.class) + private static class InterfaceFluidHandlerMixin { + @WrapOperation( + method = "insert(Lnet/fabricmc/fabric/api/transfer/v1/fluid/FluidVariant;JLnet/fabricmc/fabric/api/transfer/v1/transaction/TransactionContext;)J", + at = @At( + value = "INVOKE", + target = "Lcom/railwayteam/railways/content/fuel/psi/PortableFuelInterfaceBlockEntity;isConnected()Z" + ) + ) + private boolean fakeConnect(PortableFuelInterfaceBlockEntity instance, Operation original) { + return original.call(instance) || instance.getBehaviour(SalepointTargetBehaviour.TYPE).isControlledBySalepoint(); + } + } +} diff --git a/fabric/src/main/resources/numismatics.mixins.json b/fabric/src/main/resources/numismatics.mixins.json index 690f1e8..aaf0555 100644 --- a/fabric/src/main/resources/numismatics.mixins.json +++ b/fabric/src/main/resources/numismatics.mixins.json @@ -16,7 +16,9 @@ "WrappedStorageAccessor", "compat.PortableEnergyInterfaceBlockEntityMixin", "compat.PortableEnergyInterfaceBlockEntityMixin$InterfaceEnergyHandlerAccessor", - "compat.PortableEnergyInterfaceMovementMixin" + "compat.PortableEnergyInterfaceMovementMixin", + "compat.PortableFuelInterfaceBlockEntityMixin", + "compat.PortableFuelInterfaceBlockEntityMixin$InterfaceFluidHandlerMixin" ], "injectors": { "defaultRequire": 1 diff --git a/forge/src/main/java/dev/ithundxr/createnumismatics/forge/mixin/compat/PortableFuelInterfaceBlockEntityMixin.java b/forge/src/main/java/dev/ithundxr/createnumismatics/forge/mixin/compat/PortableFuelInterfaceBlockEntityMixin.java new file mode 100644 index 0000000..3f3a966 --- /dev/null +++ b/forge/src/main/java/dev/ithundxr/createnumismatics/forge/mixin/compat/PortableFuelInterfaceBlockEntityMixin.java @@ -0,0 +1,247 @@ +/* + * Numismatics + * Copyright (c) 2024 The Railways Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package dev.ithundxr.createnumismatics.forge.mixin.compat; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; +import com.railwayteam.railways.content.fuel.LiquidFuelTrainHandler; +import com.railwayteam.railways.content.fuel.psi.PortableFuelInterfaceBlockEntity; +import com.railwayteam.railways.content.fuel.psi.PortableFuelInterfaceBlockEntity.InterfaceFluidHandler; +import com.simibubi.create.content.contraptions.Contraption; +import com.simibubi.create.content.contraptions.actors.psi.PortableStorageInterfaceBlockEntity; +import com.simibubi.create.foundation.blockEntity.behaviour.BlockEntityBehaviour; +import com.simibubi.create.foundation.fluid.CombinedTankWrapper; +import dev.ithundxr.createnumismatics.Numismatics; +import dev.ithundxr.createnumismatics.annotation.mixin.ConditionalMixin; +import dev.ithundxr.createnumismatics.compat.Mods; +import dev.ithundxr.createnumismatics.content.salepoint.behaviours.FluidSalepointTargetBehaviour; +import dev.ithundxr.createnumismatics.content.salepoint.behaviours.SalepointTargetBehaviour; +import dev.ithundxr.createnumismatics.content.salepoint.containers.forge.InvalidatableWrappingFluidBufferTank; +import dev.ithundxr.createnumismatics.content.salepoint.states.ISalepointState; +import dev.ithundxr.createnumismatics.multiloader.fluid.MultiloaderFluidStack; +import dev.ithundxr.createnumismatics.multiloader.fluid.forge.MultiloaderFluidStackImpl; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.common.util.LazyOptional; +import net.minecraftforge.fluids.capability.IFluidHandler; +import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction; +import net.minecraftforge.fluids.capability.templates.FluidTank; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.gen.Accessor; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.List; +import java.util.Objects; + +@ConditionalMixin(mods = Mods.RAILWAYS) +@Mixin(PortableFuelInterfaceBlockEntity.class) +public abstract class PortableFuelInterfaceBlockEntityMixin extends PortableStorageInterfaceBlockEntity { + + @Shadow protected LazyOptional capability; + + @Unique + private FluidSalepointTargetBehaviour railway$salepointBehaviour; + + @Unique + @Nullable + private IFluidHandler railway$contraptionStorage; + + private PortableFuelInterfaceBlockEntityMixin(BlockEntityType type, BlockPos pos, BlockState state) { + super(type, pos, state); + } + + @Inject( + method = "startTransferringTo", + at = @At( + value = "INVOKE", + target = "Lnet/minecraftforge/common/util/LazyOptional;invalidate()V" + ), + remap = false + ) + private void keepControl(Contraption contraption, float distance, CallbackInfo ci, + @Local(name = "oldcap") LazyOptional oldcap, + @Local(name = "finalCtw") CombinedTankWrapper finalCtw) { + railway$contraptionStorage = finalCtw; + + oldcap.ifPresent(fluidHandler -> { + IFluidHandler existingWrapped = ((InterfaceFluidHandlerAccessor) fluidHandler).getWrapped(); + if (existingWrapped instanceof InvalidatableWrappingFluidBufferTank) { + capability.ifPresent(newFluidHandler -> { + ((InterfaceFluidHandlerAccessor) newFluidHandler).setWrapped(existingWrapped); + }); + } + }); + } + + @Inject( + method = "stopTransferring", + at = @At( + value = "INVOKE", + target = "Lnet/minecraftforge/common/util/LazyOptional;invalidate()V" + ), + remap = false + ) + private void keepControl2(CallbackInfo ci, @Local(name = "oldcap") LazyOptional oldcap) { + railway$contraptionStorage = null; + + oldcap.ifPresent(fluidHandler -> { + IFluidHandler existingWrapped = ((InterfaceFluidHandlerAccessor) fluidHandler).getWrapped(); + if (existingWrapped instanceof InvalidatableWrappingFluidBufferTank) { + capability.ifPresent(newFluidHandler -> { + ((InterfaceFluidHandlerAccessor) newFluidHandler).setWrapped(existingWrapped); + }); + } + }); + } + + @Override + public boolean canTransfer() { + return super.canTransfer() || railway$salepointBehaviour.isControlledBySalepoint(); + } + + @Override + public void addBehaviours(List behaviours) { + super.addBehaviours(behaviours); + railway$salepointBehaviour = new FluidSalepointTargetBehaviour(this) { + private boolean underControl = false; + + @Override + protected boolean isUnderControlInternal(@NotNull ISalepointState state) { + return underControl; // id checks done by super + } + + @Override + protected void ensureUnderControlInternal(@NotNull ISalepointState state) { + capability.ifPresent(fluidHandler -> { + ((InterfaceFluidHandlerAccessor) fluidHandler).setWrapped((InvalidatableWrappingFluidBufferTank) state.getBuffer()); + }); + + if (!underControl) { + underControl = true; + notifyUpdate(); + } + } + + @Override + protected void relinquishControlInternal(@NotNull ISalepointState state) { + capability.ifPresent(fluidHandler -> { + ((InterfaceFluidHandlerAccessor) fluidHandler).setWrapped(Objects.requireNonNullElseGet( + railway$contraptionStorage, + () -> new FluidTank(0) + )); + }); + + if (underControl) { + underControl = false; + notifyUpdate(); + } + } + + @Override + public boolean hasSpaceFor(@NotNull MultiloaderFluidStack object) { + if (railway$contraptionStorage == null) + return false; + + return railway$contraptionStorage.fill(((MultiloaderFluidStackImpl) object).getWrapped(), FluidAction.SIMULATE) == object.getAmount(); + } + + @Override + public boolean doPurchase(@NotNull MultiloaderFluidStack object, @NotNull PurchaseProvider purchaseProvider) { + if (railway$contraptionStorage == null) + return false; + + if (!hasSpaceFor(object)) + return false; + + List extracted = purchaseProvider.extract(); + for (MultiloaderFluidStack fluidStack : extracted) { + if (railway$contraptionStorage.fill(((MultiloaderFluidStackImpl) fluidStack).getWrapped(), FluidAction.EXECUTE) != fluidStack.getAmount()) { + Numismatics.LOGGER.error("Failed to insert fluid into contraption storage, despite having space."); + return false; + } + } + + return true; + } + + @Override + public void read(@NotNull CompoundTag nbt, boolean clientPacket) { + super.read(nbt, clientPacket); + + underControl = nbt.getBoolean("SalepointUnderControl"); + } + + @Override + public void write(@NotNull CompoundTag nbt, boolean clientPacket) { + super.write(nbt, clientPacket); + + nbt.putBoolean("SalepointUnderControl", underControl); + } + + @Override + public boolean canSetFilter(Object filter) { + if (!super.canSetFilter(filter)) + return false; + + if (!(filter instanceof MultiloaderFluidStackImpl fs)) + return false; + + return LiquidFuelTrainHandler.isFuel(fs.getFluid()); + } + }; + + behaviours.add(railway$salepointBehaviour); + } + + @Mixin(InterfaceFluidHandler.class) + private interface InterfaceFluidHandlerAccessor { + @Accessor("wrapped") + IFluidHandler getWrapped(); + + @Accessor("wrapped") + void setWrapped(IFluidHandler wrapped); + } + + @Mixin(InterfaceFluidHandler.class) + private static class InterfaceFluidHandlerMixin { + @Shadow @Final + PortableFuelInterfaceBlockEntity this$0; + + @WrapOperation( + method = "fill", + at = @At( + value = "INVOKE", + target = "Lcom/railwayteam/railways/content/fuel/psi/PortableFuelInterfaceBlockEntity$InterfaceFluidHandler;isConnected()Z" + ) + ) + private boolean fakeConnect(InterfaceFluidHandler instance, Operation original) { + return original.call(instance) || this$0.getBehaviour(SalepointTargetBehaviour.TYPE).isControlledBySalepoint(); + } + } +} diff --git a/forge/src/main/resources/numismatics.mixins.json b/forge/src/main/resources/numismatics.mixins.json index 9ee84c7..3e779a8 100644 --- a/forge/src/main/resources/numismatics.mixins.json +++ b/forge/src/main/resources/numismatics.mixins.json @@ -18,6 +18,9 @@ "compat.PortableEnergyInterfaceBlockEntityMixin", "compat.PortableEnergyInterfaceBlockEntityMixin$InterfaceEnergyHandlerAccessor", "compat.PortableEnergyInterfaceBlockEntityMixin$InterfaceEnergyHandlerMixin", + "compat.PortableFuelInterfaceBlockEntityMixin", + "compat.PortableFuelInterfaceBlockEntityMixin$InterfaceFluidHandlerAccessor", + "compat.PortableFuelInterfaceBlockEntityMixin$InterfaceFluidHandlerMixin", "self.BrassDepositorBlockEntityCapabilities", "self.ComputerBehaviourCapabilities", "self.VendorBlockEntityCapabilities" diff --git a/gradle.properties b/gradle.properties index ce9348b..e2f54ef 100644 --- a/gradle.properties +++ b/gradle.properties @@ -71,7 +71,7 @@ jade_version = 11.10.0+fabric # Steam 'n' Rails snr_version = 1.6.4 -enable_snr = false +enable_snr = true # Carry On - https://github.com/Tschipp/CarryOn carryon_forge_version = 2.1.2.7