From 0b03886f6f42c62d64a2bef3e6f6a9ebcaa83293 Mon Sep 17 00:00:00 2001 From: lc-1337 <62835225+lc-1337@users.noreply.github.com> Date: Thu, 1 Jan 2026 17:58:14 +0200 Subject: [PATCH] implement storage interceptor & fix locking card (#993) Co-authored-by: Martin Robertz --- .../storage/IStorageInterceptor.java | 14 ++ .../java/appeng/helpers/DualityInterface.java | 141 ++++++++++++------ .../waila/part/PartInterfaceDataProvider.java | 12 +- .../waila/tile/InterfaceDataProvider.java | 12 +- .../java/appeng/me/cache/NetworkMonitor.java | 27 +++- 5 files changed, 149 insertions(+), 57 deletions(-) create mode 100644 src/main/java/appeng/api/networking/storage/IStorageInterceptor.java diff --git a/src/main/java/appeng/api/networking/storage/IStorageInterceptor.java b/src/main/java/appeng/api/networking/storage/IStorageInterceptor.java new file mode 100644 index 000000000..b7f8ca05c --- /dev/null +++ b/src/main/java/appeng/api/networking/storage/IStorageInterceptor.java @@ -0,0 +1,14 @@ +package appeng.api.networking.storage; + +import appeng.api.config.Actionable; +import appeng.api.networking.security.BaseActionSource; +import appeng.api.storage.data.IAEStack; + +public interface IStorageInterceptor { + + boolean canAccept(IAEStack stack); + + IAEStack injectItems(IAEStack input, final Actionable type, final BaseActionSource src); + + boolean shouldRemoveInterceptor(IAEStack stack); +} diff --git a/src/main/java/appeng/helpers/DualityInterface.java b/src/main/java/appeng/helpers/DualityInterface.java index 681397cef..dfccc1b2f 100644 --- a/src/main/java/appeng/helpers/DualityInterface.java +++ b/src/main/java/appeng/helpers/DualityInterface.java @@ -73,6 +73,7 @@ import appeng.api.networking.events.MENetworkCraftingPushedPattern; import appeng.api.networking.security.BaseActionSource; import appeng.api.networking.security.IActionHost; import appeng.api.networking.security.MachineSource; +import appeng.api.networking.storage.IStorageInterceptor; import appeng.api.networking.ticking.IGridTickable; import appeng.api.networking.ticking.TickRateModulation; import appeng.api.networking.ticking.TickingRequest; @@ -121,7 +122,7 @@ import appeng.util.item.AEItemStack; import cpw.mods.fml.common.Loader; public class DualityInterface implements IGridTickable, IStorageMonitorable, IInventoryDestination, IAEAppEngInventory, - IConfigManagerHost, ICraftingProvider, IUpgradeableHost, IPriorityHost, IGridProxyable { + IConfigManagerHost, ICraftingProvider, IUpgradeableHost, IPriorityHost, IGridProxyable, IStorageInterceptor { public static final int NUMBER_OF_STORAGE_SLOTS = 9; public static final int NUMBER_OF_CONFIG_SLOTS = 9; @@ -161,7 +162,7 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn private YesNo redstoneState = YesNo.UNDECIDED; private UnlockCraftingEvent unlockEvent; - private List unlockStacks; + private List> unlockStacks; private int lastInputHash = 0; private final boolean isFluidInterface; private ScheduledReason scheduledReason = ScheduledReason.UNDEFINED; @@ -261,9 +262,9 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn if (unlockStacks != null && !unlockStacks.isEmpty()) { data.setByte("unlockEvent", (byte) 2); NBTTagList stackList = new NBTTagList(); - for (IAEItemStack stack : unlockStacks) { + for (IAEStack stack : unlockStacks) { NBTTagCompound stackTag = new NBTTagCompound(); - stack.writeToNBT(stackTag); + Platform.writeStackNBT(stack, stackTag, true); stackList.appendTag(stackTag); } data.setTag("unlockStacks", stackList); @@ -312,7 +313,7 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn NBTTagList stackList = data.getTagList("unlockStacks", 10); for (int index = 0; index < stackList.tagCount(); index++) { NBTTagCompound stackTag = stackList.getCompoundTagAt(index); - IAEItemStack unlockStack = AEItemStack.loadItemStackFromNBT(stackTag); + IAEStack unlockStack = Platform.readStackNBT(stackTag, true); if (unlockStack == null) { AELog.error("Could not load unlock stack for interface from NBT: {}", data); continue; @@ -322,6 +323,7 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn } this.unlockStacks.add(unlockStack); } + addInterception(); } else { this.unlockStacks = null; } @@ -823,7 +825,6 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn } else if (removed.stackSize != diff) { throw new IllegalStateException("bad attempt at managing inventory. ( removeItems )"); } - onStackReturnedToNetwork(AEItemStack.create(removed)); } } else if (this.craftingTracker.isBusy(x)) { changed = this.handleCrafting(x, adaptor, itemStack); @@ -1084,6 +1085,90 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn this.lastInputHash = 0; } + @Override + public boolean canAccept(IAEStack stack) { + if (unlockStacks != null && stack != null) { + for (IAEStack unlockStack : unlockStacks) { + if (stack.equals(unlockStack)) { + return true; + } + } + } + + return false; + } + + @Override + public IAEStack injectItems(IAEStack input, Actionable type, BaseActionSource src) { + if (type == Actionable.SIMULATE) return input; + if (unlockStacks != null && input != null) { + boolean changed = false; + + for (Iterator> iterator = unlockStacks.iterator(); iterator.hasNext();) { + IAEStack unlockStack = iterator.next(); + if (unlockStack.equals(input)) { + changed = true; + unlockStack.decStackSize(input.getStackSize()); + if (unlockStack.getStackSize() <= 0) { + iterator.remove(); + } + break; + } + } + + if (changed) { + saveChanges(); + } + } + + return input; + } + + private void addInterception() { + boolean hasItems = false; + boolean hasFluids = false; + + for (IAEStack aes : unlockStacks) { + if (aes.isItem()) { + hasItems = true; + } else if (aes.isFluid()) { + hasFluids = true; + } + } + + if (hasItems) { + try { + if (this.gridProxy.getStorage().getItemInventory() instanceof NetworkMonitornm) { + nm.addStorageInterceptor(this); + } + } catch (GridAccessException ignored) {} + } + + if (hasFluids) { + try { + if (this.gridProxy.getStorage().getFluidInventory() instanceof NetworkMonitornm) { + nm.addStorageInterceptor(this); + } + } catch (GridAccessException ignored) {} + } + } + + @Override + public boolean shouldRemoveInterceptor(IAEStack stack) { + if (unlockStacks != null) { + if (unlockStacks.isEmpty()) { + unlockEvent = null; + unlockStacks = null; + } else { + for (IAEStack unlockStack : unlockStacks) { + if (stack.isItem() && unlockStack.isItem()) return false; + if (stack.isFluid() && unlockStack.isFluid()) return false; + } + } + } + return true; + } + private static class VerifiedAcceptors { public TileEntity te; @@ -1583,9 +1668,13 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn if (unlockStacks == null) { unlockStacks = new ArrayList<>(); } - for (IAEItemStack output : pattern.getCondensedOutputs()) { + + for (IAEStack output : pattern.getCondensedAEOutputs()) { unlockStacks.add(output.copy()); } + + addInterception(); + saveChanges(); } } @@ -1620,46 +1709,10 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn /** * @return Null if {@linkplain #getCraftingLockedReason()} is not {@link LockCraftingMode#LOCK_UNTIL_RESULT}. */ - public List getUnlockStacks() { + public List> getUnlockStacks() { return unlockStacks; } - /** - * Called when an ItemStack has been pushed into the network from the internal buffer. Public to enable other - * interface types (mainly AE2FC) to work with locking return mode. - */ - public void onStackReturnedToNetwork(IAEItemStack returnedStack) { - if (unlockEvent != UnlockCraftingEvent.RESULT) { - return; // If we're not waiting for the result, we don't care - } - - if (unlockStacks == null) { - // Actually an error state... - AELog.error("interface was waiting for RESULT, but no result was set"); - unlockEvent = null; - return; - } - boolean changed = false; - for (Iterator iterator = unlockStacks.iterator(); iterator.hasNext();) { - IAEItemStack unlockStack = iterator.next(); - if (unlockStack.equals(returnedStack)) { - changed = true; - unlockStack.decStackSize(returnedStack.getStackSize()); - if (unlockStack.getStackSize() <= 0) { - iterator.remove(); - } - break; - } - } - if (unlockStacks.isEmpty()) { - unlockEvent = null; - unlockStacks = null; - } - if (changed) { - saveChanges(); - } - } - public void updateRedstoneState() { // reset cache to undecided redstoneState = YesNo.UNDECIDED; diff --git a/src/main/java/appeng/integration/modules/waila/part/PartInterfaceDataProvider.java b/src/main/java/appeng/integration/modules/waila/part/PartInterfaceDataProvider.java index 3b9f78f46..edf1c2d58 100644 --- a/src/main/java/appeng/integration/modules/waila/part/PartInterfaceDataProvider.java +++ b/src/main/java/appeng/integration/modules/waila/part/PartInterfaceDataProvider.java @@ -10,10 +10,10 @@ import net.minecraft.world.World; import appeng.api.config.LockCraftingMode; import appeng.api.parts.IPart; -import appeng.api.storage.data.IAEItemStack; +import appeng.api.storage.data.IAEStack; import appeng.core.localization.WailaText; import appeng.helpers.IInterfaceHost; -import appeng.util.item.AEItemStack; +import appeng.util.Platform; import mcp.mobius.waila.api.IWailaConfigHandler; import mcp.mobius.waila.api.IWailaDataAccessor; @@ -42,7 +42,7 @@ public final class PartInterfaceDataProvider extends BasePartWailaDataProvider { NBTTagList stackList = accessor.getNBTData().getTagList(NBT_LOCK_STACKS, 10); for (int index = 0; index < stackList.tagCount(); index++) { NBTTagCompound stackTag = stackList.getCompoundTagAt(index); - AEItemStack stack = (AEItemStack) AEItemStack.loadItemStackFromNBT(stackTag); + IAEStack stack = Platform.readStackNBT(stackTag); if (stack != null) { currentToolTip.add("> " + stack.getStackSize() + " " + stack.getDisplayName()); @@ -70,12 +70,12 @@ public final class PartInterfaceDataProvider extends BasePartWailaDataProvider { tag.setString(NBT_LOCK_REASON, interfaceDuality.getCraftingLockedReason().name()); LockCraftingMode lock = interfaceDuality.getCraftingLockedReason(); if (lock == LockCraftingMode.LOCK_UNTIL_RESULT) { - List unlockStacks = interfaceDuality.getUnlockStacks(); + List> unlockStacks = interfaceDuality.getUnlockStacks(); if (unlockStacks != null && !unlockStacks.isEmpty()) { NBTTagList stackList = new NBTTagList(); - for (IAEItemStack stack : unlockStacks) { + for (IAEStack stack : unlockStacks) { NBTTagCompound stackTag = new NBTTagCompound(); - stack.writeToNBT(stackTag); + Platform.writeStackNBT(stack, stackTag, true); stackList.appendTag(stackTag); } tag.setTag(NBT_LOCK_STACKS, stackList); diff --git a/src/main/java/appeng/integration/modules/waila/tile/InterfaceDataProvider.java b/src/main/java/appeng/integration/modules/waila/tile/InterfaceDataProvider.java index e71c0b42c..11b3d980c 100644 --- a/src/main/java/appeng/integration/modules/waila/tile/InterfaceDataProvider.java +++ b/src/main/java/appeng/integration/modules/waila/tile/InterfaceDataProvider.java @@ -10,11 +10,11 @@ import net.minecraft.tileentity.TileEntity; import net.minecraft.world.World; import appeng.api.config.LockCraftingMode; -import appeng.api.storage.data.IAEItemStack; +import appeng.api.storage.data.IAEStack; import appeng.core.localization.WailaText; import appeng.helpers.IInterfaceHost; import appeng.integration.modules.waila.BaseWailaDataProvider; -import appeng.util.item.AEItemStack; +import appeng.util.Platform; import mcp.mobius.waila.api.IWailaConfigHandler; import mcp.mobius.waila.api.IWailaDataAccessor; @@ -43,7 +43,7 @@ public final class InterfaceDataProvider extends BaseWailaDataProvider { NBTTagList stackList = accessor.getNBTData().getTagList(NBT_LOCK_STACKS, 10); for (int index = 0; index < stackList.tagCount(); index++) { NBTTagCompound stackTag = stackList.getCompoundTagAt(index); - AEItemStack stack = (AEItemStack) AEItemStack.loadItemStackFromNBT(stackTag); + IAEStack stack = Platform.readStackNBT(stackTag); if (stack != null) { currentToolTip.add("> " + stack.getStackSize() + " " + stack.getDisplayName()); @@ -71,12 +71,12 @@ public final class InterfaceDataProvider extends BaseWailaDataProvider { tag.setString(NBT_LOCK_REASON, interfaceDuality.getCraftingLockedReason().name()); LockCraftingMode lock = interfaceDuality.getCraftingLockedReason(); if (lock == LockCraftingMode.LOCK_UNTIL_RESULT) { - List unlockStacks = interfaceDuality.getUnlockStacks(); + List> unlockStacks = interfaceDuality.getUnlockStacks(); if (unlockStacks != null && !unlockStacks.isEmpty()) { NBTTagList stackList = new NBTTagList(); - for (IAEItemStack stack : unlockStacks) { + for (IAEStack stack : unlockStacks) { NBTTagCompound stackTag = new NBTTagCompound(); - stack.writeToNBT(stackTag); + Platform.writeStackNBT(stack, stackTag, true); stackList.appendTag(stackTag); } tag.setTag(NBT_LOCK_STACKS, stackList); diff --git a/src/main/java/appeng/me/cache/NetworkMonitor.java b/src/main/java/appeng/me/cache/NetworkMonitor.java index 595a7e691..3530fa3f3 100644 --- a/src/main/java/appeng/me/cache/NetworkMonitor.java +++ b/src/main/java/appeng/me/cache/NetworkMonitor.java @@ -13,9 +13,11 @@ package appeng.me.cache; import java.util.Collection; import java.util.Deque; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import javax.annotation.Nonnegative; import javax.annotation.Nonnull; @@ -29,6 +31,7 @@ import appeng.api.config.Actionable; import appeng.api.networking.IGrid; import appeng.api.networking.events.MENetworkStorageEvent; import appeng.api.networking.security.BaseActionSource; +import appeng.api.networking.storage.IStorageInterceptor; import appeng.api.storage.IMEInventoryHandler; import appeng.api.storage.IMEMonitor; import appeng.api.storage.IMEMonitorHandlerReceiver; @@ -65,6 +68,8 @@ public class NetworkMonitor> implements IMEMonitor { @Nonnegative private int localDepthSemaphore = 0; + private final Set storageInterceptors = new HashSet<>(); + public NetworkMonitor(final GridStorageCache cache, final StorageChannel chan) { this.myGridCache = cache; this.myChannel = chan; @@ -148,7 +153,19 @@ public class NetworkMonitor> implements IMEMonitor { } @Override - public T injectItems(final T input, final Actionable mode, final BaseActionSource src) { + public T injectItems(T input, final Actionable mode, final BaseActionSource src) { + + for (Iterator iterator = storageInterceptors.iterator(); iterator.hasNext();) { + final IStorageInterceptor isi = iterator.next(); + if (isi.canAccept(input)) { + input = (T) isi.injectItems(input, mode, src); + + if (isi.shouldRemoveInterceptor(input)) iterator.remove(); + + if (input == null) return null; + } + } + if (mode == Actionable.SIMULATE) { return this.getHandler().injectItems(input, mode, src); } @@ -326,4 +343,12 @@ public class NetworkMonitor> implements IMEMonitor { this.myGridCache.getGrid().postEvent(new MENetworkStorageEvent(this, this.myChannel)); } } + + public void addStorageInterceptor(IStorageInterceptor storageInterceptor) { + this.storageInterceptors.add(storageInterceptor); + } + + public void removeStorageInterceptor(IStorageInterceptor storageInterceptor) { + this.storageInterceptors.remove(storageInterceptor); + } }