diff --git a/src/api/java/appeng/api/config/LockCraftingMode.java b/src/api/java/appeng/api/config/LockCraftingMode.java new file mode 100644 index 000000000..17e7f5d58 --- /dev/null +++ b/src/api/java/appeng/api/config/LockCraftingMode.java @@ -0,0 +1,26 @@ +package appeng.api.config; + +public enum LockCraftingMode { + /** + * Crafting is never locked. + */ + NONE, + /** + * After pushing a pattern to an adjacent machine, the pattern provider will not accept further crafts until a + * redstone pulse is received. + */ + LOCK_UNTIL_PULSE, + /** + * Crafting is locked while the pattern provider is receiving a redstone signal. + */ + LOCK_WHILE_HIGH, + /** + * Crafting is locked while the pattern provider is not receiving a redstone signal. + */ + LOCK_WHILE_LOW, + /** + * After pushing a pattern to an adjacent machine, the pattern provider will not accept further crafts until the + * primary pattern result is returned to the network through the pattern provider. + */ + LOCK_UNTIL_RESULT +} diff --git a/src/api/java/appeng/api/config/Settings.java b/src/api/java/appeng/api/config/Settings.java index 7fe7de1aa..80d965e3f 100644 --- a/src/api/java/appeng/api/config/Settings.java +++ b/src/api/java/appeng/api/config/Settings.java @@ -57,6 +57,8 @@ public enum Settings IO_DIRECTION( EnumSet.of( RelativeDirection.LEFT, RelativeDirection.RIGHT ) ), + UNLOCK(EnumSet.allOf(LockCraftingMode.class)), + BLOCK( EnumSet.of( YesNo.YES, YesNo.NO ) ), OPERATION_MODE( EnumSet.allOf( OperationMode.class ) ), diff --git a/src/api/java/appeng/api/networking/crafting/ICraftingPatternDetails.java b/src/api/java/appeng/api/networking/crafting/ICraftingPatternDetails.java index 11e4ee367..34bf3e262 100644 --- a/src/api/java/appeng/api/networking/crafting/ICraftingPatternDetails.java +++ b/src/api/java/appeng/api/networking/crafting/ICraftingPatternDetails.java @@ -77,6 +77,14 @@ public interface ICraftingPatternDetails */ IAEItemStack[] getCondensedOutputs(); + /** + * The primary output of this pattern. The pattern will only be used to craft the primary output; the others are + * just byproducts. + */ + default IAEItemStack getPrimaryOutput() { + return getOutputs()[0]; + } + /** * @return a list of the outputs, will include nulls. */ diff --git a/src/main/java/appeng/block/misc/BlockInterface.java b/src/main/java/appeng/block/misc/BlockInterface.java index 8bab2e728..e2a3a8e9f 100644 --- a/src/main/java/appeng/block/misc/BlockInterface.java +++ b/src/main/java/appeng/block/misc/BlockInterface.java @@ -25,12 +25,14 @@ import appeng.block.AEBaseTileBlock; import appeng.core.sync.GuiBridge; import appeng.tile.misc.TileInterface; import appeng.util.Platform; +import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.block.properties.IProperty; import net.minecraft.block.properties.PropertyBool; import net.minecraft.block.state.IBlockState; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; import net.minecraft.util.math.BlockPos; @@ -93,4 +95,12 @@ public class BlockInterface extends AEBaseTileBlock { ((TileInterface) rotatable).setSide(axis); } } + + @Override + public void neighborChanged(IBlockState state, World worldIn, BlockPos pos, Block blockIn, BlockPos fromPos) { + TileEntity tileEntity = this.getTileEntity(worldIn, pos); + if (tileEntity != null) { + ((TileInterface) tileEntity).updateRedstoneState(); + } + } } diff --git a/src/main/java/appeng/client/gui/implementations/GuiInterface.java b/src/main/java/appeng/client/gui/implementations/GuiInterface.java index ee501be3c..6c8c4d6fe 100644 --- a/src/main/java/appeng/client/gui/implementations/GuiInterface.java +++ b/src/main/java/appeng/client/gui/implementations/GuiInterface.java @@ -19,6 +19,7 @@ package appeng.client.gui.implementations; +import appeng.api.config.LockCraftingMode; import appeng.api.config.Settings; import appeng.api.config.YesNo; import appeng.client.gui.widgets.GuiImgButton; @@ -41,6 +42,7 @@ import java.io.IOException; public class GuiInterface extends GuiUpgradeable { private GuiTabButton priority; + private GuiImgButton UnlockMode; private GuiImgButton BlockMode; private GuiToggleButton interfaceMode; @@ -57,7 +59,10 @@ public class GuiInterface extends GuiUpgradeable { this.BlockMode = new GuiImgButton(this.guiLeft - 18, this.guiTop + 8, Settings.BLOCK, YesNo.NO); this.buttonList.add(this.BlockMode); - this.interfaceMode = new GuiToggleButton(this.guiLeft - 18, this.guiTop + 26, 84, 85, GuiText.InterfaceTerminal.getLocal(), GuiText.InterfaceTerminalHint.getLocal()); + this.UnlockMode = new GuiImgButton(this.guiLeft - 18,this.guiTop + 26 ,Settings.UNLOCK, LockCraftingMode.NONE); + this.buttonList.add(this.UnlockMode); + + this.interfaceMode = new GuiToggleButton(this.guiLeft - 18, this.guiTop + 44, 84, 85, GuiText.InterfaceTerminal.getLocal(), GuiText.InterfaceTerminalHint.getLocal()); this.buttonList.add(this.interfaceMode); } @@ -67,6 +72,10 @@ public class GuiInterface extends GuiUpgradeable { this.BlockMode.set(((ContainerInterface) this.cvb).getBlockingMode()); } + if (this.UnlockMode != null) { + this.UnlockMode.set(((ContainerInterface) this.cvb).getUnlockMode()); + } + if (this.interfaceMode != null) { this.interfaceMode.setState(((ContainerInterface) this.cvb).getInterfaceTerminalMode() == YesNo.YES); } @@ -106,6 +115,10 @@ public class GuiInterface extends GuiUpgradeable { if (btn == this.BlockMode) { NetworkHandler.instance().sendToServer(new PacketConfigButton(this.BlockMode.getSetting(), backwards)); } + + if (btn == this.UnlockMode) { + NetworkHandler.instance.sendToServer(new PacketConfigButton(this.UnlockMode.getSetting(), backwards)); + } } } diff --git a/src/main/java/appeng/client/gui/widgets/GuiImgButton.java b/src/main/java/appeng/client/gui/widgets/GuiImgButton.java index 638cf5681..4c242adab 100644 --- a/src/main/java/appeng/client/gui/widgets/GuiImgButton.java +++ b/src/main/java/appeng/client/gui/widgets/GuiImgButton.java @@ -160,6 +160,12 @@ public class GuiImgButton extends GuiButton implements ITooltip { this.registerApp(16 * 15, Settings.SCHEDULING_MODE, SchedulingMode.DEFAULT, ButtonToolTips.SchedulingMode, ButtonToolTips.SchedulingModeDefault); this.registerApp(16 * 15 + 1, Settings.SCHEDULING_MODE, SchedulingMode.ROUNDROBIN, ButtonToolTips.SchedulingMode, ButtonToolTips.SchedulingModeRoundRobin); this.registerApp(16 * 15 + 2, Settings.SCHEDULING_MODE, SchedulingMode.RANDOM, ButtonToolTips.SchedulingMode, ButtonToolTips.SchedulingModeRandom); + + this.registerApp(10, Settings.UNLOCK,LockCraftingMode.NONE,ButtonToolTips.LockCraftingMode, ButtonToolTips.LockCraftingModeNone); + this.registerApp(7, Settings.UNLOCK,LockCraftingMode.LOCK_UNTIL_RESULT,ButtonToolTips.LockCraftingMode, ButtonToolTips.LockCraftingUntilResultReturned); + this.registerApp(0, Settings.UNLOCK, LockCraftingMode.LOCK_WHILE_LOW, ButtonToolTips.LockCraftingMode, ButtonToolTips.LockCraftingWhileRedstoneLow); + this.registerApp(1, Settings.UNLOCK, LockCraftingMode.LOCK_WHILE_HIGH, ButtonToolTips.LockCraftingMode, ButtonToolTips.LockCraftingWhileRedstoneHigh); + this.registerApp(2, Settings.UNLOCK, LockCraftingMode.LOCK_UNTIL_PULSE, ButtonToolTips.LockCraftingMode, ButtonToolTips.LockCraftingUntilRedstonePulse); } } diff --git a/src/main/java/appeng/container/implementations/ContainerInterface.java b/src/main/java/appeng/container/implementations/ContainerInterface.java index 68fda38a5..3b2c436d1 100644 --- a/src/main/java/appeng/container/implementations/ContainerInterface.java +++ b/src/main/java/appeng/container/implementations/ContainerInterface.java @@ -19,10 +19,7 @@ package appeng.container.implementations; -import appeng.api.config.SecurityPermissions; -import appeng.api.config.Settings; -import appeng.api.config.Upgrades; -import appeng.api.config.YesNo; +import appeng.api.config.*; import appeng.api.util.IConfigManager; import appeng.container.guisync.GuiSync; import appeng.container.slot.*; @@ -40,6 +37,9 @@ public class ContainerInterface extends ContainerUpgradeable implements IOptiona public YesNo bMode = YesNo.NO; @GuiSync(4) + public LockCraftingMode lMode = LockCraftingMode.NONE; + + @GuiSync(5) public YesNo iTermMode = YesNo.YES; @GuiSync(7) @@ -107,9 +107,14 @@ public class ContainerInterface extends ContainerUpgradeable implements IOptiona @Override protected void loadSettingsFromHost(final IConfigManager cm) { this.setBlockingMode((YesNo) cm.getSetting(Settings.BLOCK)); + this.setUnlockMode((LockCraftingMode) cm.getSetting(Settings.UNLOCK)); this.setInterfaceTerminalMode((YesNo) cm.getSetting(Settings.INTERFACE_TERMINAL)); } + public LockCraftingMode getUnlockMode() {return this.lMode;} + + public void setUnlockMode(final LockCraftingMode mode) {this.lMode = mode;} + public YesNo getBlockingMode() { return this.bMode; } diff --git a/src/main/java/appeng/core/localization/ButtonToolTips.java b/src/main/java/appeng/core/localization/ButtonToolTips.java index 42f65e46a..2cb561838 100644 --- a/src/main/java/appeng/core/localization/ButtonToolTips.java +++ b/src/main/java/appeng/core/localization/ButtonToolTips.java @@ -98,6 +98,13 @@ public enum ButtonToolTips { Blocking, NonBlocking, + LockCraftingMode, + LockCraftingModeNone, + LockCraftingUntilRedstonePulse, + LockCraftingWhileRedstoneHigh, + LockCraftingWhileRedstoneLow, + LockCraftingUntilResultReturned, + LevelType, LevelType_Energy, LevelType_Item, diff --git a/src/main/java/appeng/fluids/helper/DualityFluidInterface.java b/src/main/java/appeng/fluids/helper/DualityFluidInterface.java index 73a3422b3..e9edb98a0 100644 --- a/src/main/java/appeng/fluids/helper/DualityFluidInterface.java +++ b/src/main/java/appeng/fluids/helper/DualityFluidInterface.java @@ -48,6 +48,7 @@ import appeng.api.util.IConfigManager; import appeng.capabilities.Capabilities; import appeng.core.settings.TickRates; import appeng.fluids.util.AEFluidInventory; +import appeng.fluids.util.AEFluidStack; import appeng.fluids.util.IAEFluidInventory; import appeng.fluids.util.IAEFluidTank; import appeng.helpers.ICustomNameObject; @@ -473,7 +474,12 @@ public class DualityFluidInterface implements IGridTickable, IStorageMonitorable } @Override - public void onFluidInventoryChanged(final IAEFluidTank inventory, final int slot) { + public void onFluidInventoryChanged(IAEFluidTank inv, int slot) { + onFluidInventoryChanged(inv, slot, null, null, null); + } + + @Override + public void onFluidInventoryChanged(final IAEFluidTank inventory, final int slot, InvOperation operation, FluidStack added, FluidStack removed) { if (this.isWorking == slot) { return; } @@ -486,6 +492,9 @@ public class DualityFluidInterface implements IGridTickable, IStorageMonitorable this.notifyNeighbors(); } } else if (inventory == this.tanks) { + if (added != null) { + iHost.onStackReturnNetwork(AEFluidStack.fromFluidStack(added)); + } this.saveChanges(); final boolean had = this.hasWorkToDo(); diff --git a/src/main/java/appeng/fluids/helper/IFluidInterfaceHost.java b/src/main/java/appeng/fluids/helper/IFluidInterfaceHost.java index 1f4ae59f6..9469d2eac 100644 --- a/src/main/java/appeng/fluids/helper/IFluidInterfaceHost.java +++ b/src/main/java/appeng/fluids/helper/IFluidInterfaceHost.java @@ -21,9 +21,11 @@ package appeng.fluids.helper; import appeng.api.implementations.IUpgradeableHost; import appeng.api.networking.security.IActionHost; +import appeng.api.storage.data.IAEFluidStack; import appeng.me.helpers.IGridProxyable; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; +import net.minecraftforge.fluids.FluidStack; import java.util.EnumSet; @@ -36,4 +38,7 @@ public interface IFluidInterfaceHost extends IActionHost, IGridProxyable, IUpgra TileEntity getTileEntity(); void saveChanges(); + + default void onStackReturnNetwork(IAEFluidStack stack) { + } } diff --git a/src/main/java/appeng/fluids/parts/PartFluidFormationPlane.java b/src/main/java/appeng/fluids/parts/PartFluidFormationPlane.java index ce4283333..f9cdd3ef5 100644 --- a/src/main/java/appeng/fluids/parts/PartFluidFormationPlane.java +++ b/src/main/java/appeng/fluids/parts/PartFluidFormationPlane.java @@ -29,6 +29,7 @@ import appeng.me.storage.MEInventoryHandler; import appeng.parts.automation.PartAbstractFormationPlane; import appeng.parts.automation.PlaneModels; import appeng.util.Platform; +import appeng.util.inv.InvOperation; import appeng.util.prioritylist.PrecisePriorityList; import net.minecraft.block.Block; import net.minecraft.block.BlockLiquid; diff --git a/src/main/java/appeng/fluids/parts/PartFluidLevelEmitter.java b/src/main/java/appeng/fluids/parts/PartFluidLevelEmitter.java index d1bf51ad7..13b800945 100644 --- a/src/main/java/appeng/fluids/parts/PartFluidLevelEmitter.java +++ b/src/main/java/appeng/fluids/parts/PartFluidLevelEmitter.java @@ -36,6 +36,7 @@ import appeng.parts.PartModel; import appeng.parts.automation.PartUpgradeable; import appeng.util.IConfigManagerHost; import appeng.util.Platform; +import appeng.util.inv.InvOperation; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; @@ -46,6 +47,7 @@ import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; +import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.capability.IFluidHandler; import java.util.Random; diff --git a/src/main/java/appeng/fluids/parts/PartFluidStorageBus.java b/src/main/java/appeng/fluids/parts/PartFluidStorageBus.java index f6308ded4..2303ff670 100644 --- a/src/main/java/appeng/fluids/parts/PartFluidStorageBus.java +++ b/src/main/java/appeng/fluids/parts/PartFluidStorageBus.java @@ -63,6 +63,7 @@ import appeng.parts.automation.PartUpgradeable; import appeng.tile.networking.TileCableBus; import appeng.util.ConfigManager; import appeng.util.Platform; +import appeng.util.inv.InvOperation; import appeng.util.prioritylist.FuzzyPriorityList; import appeng.util.prioritylist.PrecisePriorityList; import net.minecraft.entity.player.EntityPlayer; @@ -75,6 +76,7 @@ import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; import net.minecraft.world.IBlockAccess; +import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.capability.CapabilityFluidHandler; import net.minecraftforge.fluids.capability.IFluidHandler; @@ -543,6 +545,7 @@ public class PartFluidStorageBus extends PartUpgradeable implements IGridTickabl } // TODO: 1/28/2024 Unify both methods. + /** * Filters the changes to only include items that pass the storage filter. * Optimally, this should be handled by the underlying monitor. diff --git a/src/main/java/appeng/fluids/util/AEFluidInventory.java b/src/main/java/appeng/fluids/util/AEFluidInventory.java index 21e7c498a..df07a4be3 100644 --- a/src/main/java/appeng/fluids/util/AEFluidInventory.java +++ b/src/main/java/appeng/fluids/util/AEFluidInventory.java @@ -4,6 +4,7 @@ package appeng.fluids.util; import appeng.api.storage.data.IAEFluidStack; import appeng.core.AELog; import appeng.util.Platform; +import appeng.util.inv.InvOperation; import net.minecraft.nbt.NBTTagCompound; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.capability.IFluidTankProperties; @@ -37,24 +38,27 @@ public class AEFluidInventory implements IAEFluidTank { if (Objects.equals(this.fluids[slot], fluid)) { if (fluid != null && fluid.getStackSize() != this.fluids[slot].getStackSize()) { this.fluids[slot].setStackSize(fluid.getStackSize()); - this.onContentChanged(slot); + this.onContentChanged(slot, InvOperation.SET, fluid.getFluidStack(), null); } } else { + if (fluid == null) { + IAEFluidStack removeStack = this.fluids[slot].copy(); this.fluids[slot] = null; + this.onContentChanged(slot, InvOperation.SET, null, removeStack.getFluidStack()); } else { + IAEFluidStack removeStack = this.fluids[slot]; this.fluids[slot] = fluid.copy(); this.fluids[slot].setStackSize(fluid.getStackSize()); + this.onContentChanged(slot, InvOperation.SET, fluid.getFluidStack(), removeStack == null ? null : removeStack.getFluidStack()); } - - this.onContentChanged(slot); } } } - private void onContentChanged(final int slot) { + private void onContentChanged(final int slot, InvOperation operation, FluidStack added, FluidStack removed) { if (this.handler != null && Platform.isServer()) { - this.handler.onFluidInventoryChanged(this, slot); + this.handler.onFluidInventoryChanged(this, slot, operation, added, removed); } } @@ -107,7 +111,7 @@ public class AEFluidInventory implements IAEFluidTank { this.setFluidInSlot(slot, AEFluidStack.fromFluidStack(resource).setStackSize(amountToStore)); } else { fluid.setStackSize(fluid.getStackSize() + amountToStore); - this.onContentChanged(slot); + this.onContentChanged(slot, InvOperation.INSERT, resource, null); } } @@ -139,7 +143,7 @@ public class AEFluidInventory implements IAEFluidTank { if (fluid.getStackSize() <= 0) { this.fluids[slot] = null; } - this.onContentChanged(slot); + this.onContentChanged(slot, InvOperation.EXTRACT, null, new FluidStack(fluid.getFluid(), drained)); } return stack; } diff --git a/src/main/java/appeng/fluids/util/AEFluidTank.java b/src/main/java/appeng/fluids/util/AEFluidTank.java index 2ddb9fda9..f4edb45d5 100644 --- a/src/main/java/appeng/fluids/util/AEFluidTank.java +++ b/src/main/java/appeng/fluids/util/AEFluidTank.java @@ -21,6 +21,7 @@ package appeng.fluids.util; import appeng.api.storage.data.IAEFluidStack; import appeng.util.Platform; +import appeng.util.inv.InvOperation; import net.minecraft.tileentity.TileEntity; import net.minecraftforge.fluids.FluidTank; diff --git a/src/main/java/appeng/fluids/util/IAEFluidInventory.java b/src/main/java/appeng/fluids/util/IAEFluidInventory.java index 295f2c44b..de3ccc24e 100644 --- a/src/main/java/appeng/fluids/util/IAEFluidInventory.java +++ b/src/main/java/appeng/fluids/util/IAEFluidInventory.java @@ -19,7 +19,13 @@ package appeng.fluids.util; +import appeng.util.inv.InvOperation; +import net.minecraftforge.fluids.FluidStack; + @FunctionalInterface public interface IAEFluidInventory { void onFluidInventoryChanged(final IAEFluidTank inv, final int slot); + + default void onFluidInventoryChanged(final IAEFluidTank inv, final int slot, InvOperation operation, FluidStack added, FluidStack removed) { + } } diff --git a/src/main/java/appeng/helpers/DualityInterface.java b/src/main/java/appeng/helpers/DualityInterface.java index 05619132d..46eaed13b 100644 --- a/src/main/java/appeng/helpers/DualityInterface.java +++ b/src/main/java/appeng/helpers/DualityInterface.java @@ -20,10 +20,7 @@ package appeng.helpers; import appeng.api.AEApi; -import appeng.api.config.Actionable; -import appeng.api.config.Settings; -import appeng.api.config.Upgrades; -import appeng.api.config.YesNo; +import appeng.api.config.*; import appeng.api.implementations.ICraftingPatternItem; import appeng.api.implementations.IUpgradeableHost; import appeng.api.implementations.tiles.ICraftingMachine; @@ -53,6 +50,7 @@ import appeng.api.util.AEPartLocation; import appeng.api.util.DimensionalCoord; import appeng.api.util.IConfigManager; import appeng.capabilities.Capabilities; +import appeng.core.AELog; import appeng.core.settings.TickRates; import appeng.me.GridAccessException; import appeng.me.helpers.AENetworkProxy; @@ -67,10 +65,7 @@ import appeng.tile.inventory.AppEngInternalAEInventory; import appeng.tile.inventory.AppEngInternalInventory; import appeng.tile.inventory.AppEngInternalOversizedInventory; import appeng.tile.networking.TileCableBus; -import appeng.util.ConfigManager; -import appeng.util.IConfigManagerHost; -import appeng.util.InventoryAdaptor; -import appeng.util.Platform; +import appeng.util.*; import appeng.util.inv.*; import appeng.util.item.AEItemStack; import com.google.common.collect.ImmutableSet; @@ -101,6 +96,8 @@ import net.minecraftforge.items.wrapper.RangedWrapper; import javax.annotation.Nullable; import java.util.*; +import static appeng.api.config.LockCraftingMode.LOCK_UNTIL_PULSE; +import static appeng.api.config.LockCraftingMode.LOCK_UNTIL_RESULT; import static appeng.helpers.ItemStackHelper.*; @@ -135,6 +132,13 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn private boolean resetConfigCache = true; private IMEMonitor configCachedHandler; + private YesNo redstoneState = YesNo.UNDECIDED; + + @Nullable + private UnlockCraftingEvent unlockEvent; + @Nullable + private IAEItemStack unlockStack; + public DualityInterface(final AENetworkProxy networkProxy, final IInterfaceHost ih) { this.gridProxy = networkProxy; this.gridProxy.setFlags(GridFlags.REQUIRE_CHANNEL); @@ -142,6 +146,7 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn this.upgrades = new StackUpgradeInventory(this.gridProxy.getMachineRepresentation(), this, 4); this.cm.registerSetting(Settings.BLOCK, YesNo.NO); this.cm.registerSetting(Settings.INTERFACE_TERMINAL, YesNo.YES); + this.cm.registerSetting(Settings.UNLOCK, LockCraftingMode.NONE); this.iHost = ih; this.craftingTracker = new MultiCraftingTracker(this.iHost, 9); @@ -168,6 +173,7 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn if (this.isWorking == slot) { return; } + if (inv == this.config && (!removed.isEmpty() || !added.isEmpty())) { boolean cfg = hasConfig(); this.readConfig(); @@ -178,6 +184,9 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn } else if (inv == this.patterns && (!removed.isEmpty() || !added.isEmpty())) { this.updateCraftingList(); } else if (inv == this.storage && slot >= 0) { + if (added != ItemStack.EMPTY){ + iHost.onStackReturnNetwork(AEItemStack.fromItemStack(added)); + } final boolean had = this.hasWorkToDo(); this.updatePlan(slot); @@ -207,6 +216,18 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn this.craftingTracker.writeToNBT(data); data.setInteger("priority", this.priority); + if (unlockEvent == UnlockCraftingEvent.PULSE) { + data.setByte("unlockEvent", (byte) 1); + } else if (unlockEvent == UnlockCraftingEvent.RESULT) { + if (unlockStack != null) { + data.setByte("unlockEvent", (byte) 2); + NBTTagCompound unlockStackTag = new NBTTagCompound(); + unlockStack.writeToNBT(unlockStackTag); + data.setTag("unlockStack", unlockStackTag); + } else { + AELog.error("Saving MEInterface {}, locked waiting for stack, but stack is null!", iHost); + } + } final NBTTagList waitingToSend = new NBTTagList(); if (this.waitingToSend != null) { for (final ItemStack is : this.waitingToSend) { @@ -286,6 +307,27 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn this.cm.readFromNBT(data); this.readConfig(); this.updateCraftingList(); + + var unlockEventType = data.getByte("unlockEvent"); + + this.unlockEvent = switch (unlockEventType) { + case 0 -> null; + case 1 -> UnlockCraftingEvent.PULSE; + case 2 -> UnlockCraftingEvent.RESULT; + default -> { + AELog.error("Unknown unlock event type {} in NBT for MEInterface: {}", unlockEventType, data); + yield null; + } + }; + + if (this.unlockEvent == UnlockCraftingEvent.RESULT) { + this.unlockStack = AEItemStack.fromNBT(data.getCompoundTag("unlockStack")); + if (this.unlockStack == null) { + AELog.error("Could not load unlock stack for MEInterface from NBT: {}", data); + } + } else { + this.unlockStack = null; + } } private void addToSendList(final ItemStack is) { @@ -912,6 +954,11 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn if (this.getInstalledUpgrades(Upgrades.CRAFTING) == 0) { this.cancelCrafting(); } + + if (newValue == LockCraftingMode.NONE) { + resetCraftingLock(); + } + this.iHost.saveChanges(); } @@ -951,6 +998,10 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn final TileEntity tile = this.iHost.getTileEntity(); final World w = tile.getWorld(); + if (getCraftingLockedReason() != LockCraftingMode.NONE) { + return false; + } + if (this.visitedFaces.isEmpty()) { this.visitedFaces = this.iHost.getTargets(); } @@ -1003,6 +1054,7 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn addToSendListFacing(is, s); } } + onPushPatternSuccess(patternDetails); pushItemsOut(s); return true; @@ -1021,6 +1073,7 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn if (cm.acceptsPlans()) { visitedFaces.remove(s); if (cm.pushPattern(patternDetails, table, s.getOpposite())) { + onPushPatternSuccess(patternDetails); return true; } continue; @@ -1061,6 +1114,7 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn addToSendListFacing(is, s); } } + onPushPatternSuccess(patternDetails); pushItemsOut(s); return true; } @@ -1070,6 +1124,57 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn return false; } + public void resetCraftingLock() { + if (unlockEvent != null) { + unlockEvent = null; + unlockStack = null; + saveChanges(); + } + } + + private void onPushPatternSuccess(ICraftingPatternDetails pattern) { + resetCraftingLock(); + + LockCraftingMode lockMode = (LockCraftingMode) cm.getSetting(Settings.UNLOCK); + switch (lockMode) { + case LOCK_UNTIL_PULSE -> { + unlockEvent = UnlockCraftingEvent.PULSE; + saveChanges(); + } + case LOCK_UNTIL_RESULT -> { + unlockEvent = UnlockCraftingEvent.RESULT; + unlockStack = pattern.getPrimaryOutput(); + saveChanges(); + } + } + } + + /** + * Gets if the crafting lock is in effect and why. + * + * @return null if the lock isn't in effect + */ + public LockCraftingMode getCraftingLockedReason() { + var lockMode = cm.getSetting(Settings.UNLOCK); + if (lockMode == LockCraftingMode.LOCK_WHILE_LOW && !getRedstoneState()) { + // Crafting locked by redstone signal + return LockCraftingMode.LOCK_WHILE_LOW; + } else if (lockMode == LockCraftingMode.LOCK_WHILE_HIGH && getRedstoneState()) { + return LockCraftingMode.LOCK_WHILE_HIGH; + } else if (unlockEvent != null) { + // Crafting locked by waiting for unlock event + switch (unlockEvent) { + case PULSE -> { + return LOCK_UNTIL_PULSE; + } + case RESULT -> { + return LOCK_UNTIL_RESULT; + } + } + } + return LockCraftingMode.NONE; + } + @Override public boolean isBusy() { boolean busy = false; @@ -1384,6 +1489,55 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn return null; } + public void updateRedstoneState() { + // If we're waitingh for a pulse, update immediately + if (unlockEvent == UnlockCraftingEvent.PULSE && getRedstoneState()) { + unlockEvent = null; // Unlocked! + } else { + // Otherwise, just reset back to undecided + redstoneState = YesNo.UNDECIDED; + } + saveChanges(); // In any case, this needs to be changed since the state is now outdated + } + + /** + * @return Null if {@linkplain #getCraftingLockedReason()} is not {@link LockCraftingMode#LOCK_UNTIL_RESULT}. + */ + @org.jetbrains.annotations.Nullable + public IAEItemStack getUnlockStack() { + return unlockStack; + } + + public void onStackReturnedToNetwork(IAEItemStack stack) { + if (unlockEvent != UnlockCraftingEvent.RESULT) { + return; // If we're not waiting for the result, we don't care + } + + if (unlockStack == null) { + // Actually an error state... + AELog.error("MEInterface was waiting for RESULT, but no result was set"); + unlockEvent = null; + } else if (unlockStack.getItem().equals(stack.getItem())) { + var remainingAmount = unlockStack.getStackSize() - stack.getStackSize(); + if (remainingAmount <= 0) { + unlockEvent = null; + unlockStack = null; + } else { + unlockStack.setStackSize(remainingAmount); + } + } + } + + private boolean getRedstoneState() { + if (redstoneState == YesNo.UNDECIDED) { + var entity = this.iHost.getTileEntity(); + redstoneState = entity.getWorld().getRedstonePowerFromNeighbors(entity.getPos()) > 0 + ? YesNo.YES + : YesNo.NO; + } + return redstoneState == YesNo.YES; + } + private class InterfaceRequestSource extends MachineSource { private final InterfaceRequestContext context; diff --git a/src/main/java/appeng/helpers/IInterfaceHost.java b/src/main/java/appeng/helpers/IInterfaceHost.java index 7b8769d30..51f9cf225 100644 --- a/src/main/java/appeng/helpers/IInterfaceHost.java +++ b/src/main/java/appeng/helpers/IInterfaceHost.java @@ -22,6 +22,7 @@ package appeng.helpers; import appeng.api.implementations.IUpgradeableHost; import appeng.api.networking.crafting.ICraftingProvider; import appeng.api.networking.crafting.ICraftingRequester; +import appeng.api.storage.data.IAEItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; @@ -37,4 +38,8 @@ public interface IInterfaceHost extends ICraftingProvider, IUpgradeableHost, ICr TileEntity getTileEntity(); void saveChanges(); + + default void onStackReturnNetwork(IAEItemStack stack) { + getInterfaceDuality().onStackReturnedToNetwork(stack); + } } diff --git a/src/main/java/appeng/helpers/UnlockCraftingEvent.java b/src/main/java/appeng/helpers/UnlockCraftingEvent.java new file mode 100644 index 000000000..dcfe64ca9 --- /dev/null +++ b/src/main/java/appeng/helpers/UnlockCraftingEvent.java @@ -0,0 +1,6 @@ +package appeng.helpers; + +public enum UnlockCraftingEvent { + PULSE, + RESULT +} diff --git a/src/main/java/appeng/parts/misc/PartInterface.java b/src/main/java/appeng/parts/misc/PartInterface.java index 781443116..b33dc836b 100644 --- a/src/main/java/appeng/parts/misc/PartInterface.java +++ b/src/main/java/appeng/parts/misc/PartInterface.java @@ -54,6 +54,7 @@ import appeng.items.parts.PartModels; import appeng.parts.PartBasicState; import appeng.parts.PartModel; import appeng.tile.inventory.AppEngInternalInventory; +import appeng.tile.misc.TileInterface; import appeng.util.Platform; import appeng.util.SettingsFrom; import appeng.util.inv.IAEAppEngInventory; @@ -68,7 +69,9 @@ import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; +import net.minecraft.world.IBlockAccess; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.wrapper.PlayerMainInvWrapper; @@ -196,6 +199,11 @@ public class PartInterface extends PartBasicState implements IGridTickable, ISto this.duality.onChangeInventory(inv, slot, mc, removedStack, newStack); } + @Override + public void onStackReturnNetwork(IAEItemStack stack) { + this.duality.onStackReturnedToNetwork(stack); + } + @Override public DualityInterface getInterfaceDuality() { return this.duality; @@ -262,6 +270,14 @@ public class PartInterface extends PartBasicState implements IGridTickable, ISto } } + @Override + public void onNeighborChanged(IBlockAccess w, BlockPos pos, BlockPos neighbor) { + TileEntity tileEntity = getTileEntity(); + if (tileEntity instanceof TileInterface) { + ((TileInterface) tileEntity).updateRedstoneState(); + } + } + @Override public boolean hasCapability(Capability capabilityClass) { return this.duality.hasCapability(capabilityClass, this.getSide().getFacing()); diff --git a/src/main/java/appeng/tile/misc/TileInterface.java b/src/main/java/appeng/tile/misc/TileInterface.java index 8a2f9f4c2..6424eb9ee 100644 --- a/src/main/java/appeng/tile/misc/TileInterface.java +++ b/src/main/java/appeng/tile/misc/TileInterface.java @@ -221,6 +221,11 @@ public class TileInterface extends AENetworkInvTile implements IGridTickable, II this.duality.onChangeInventory(inv, slot, mc, removed, added); } + @Override + public void onStackReturnNetwork(IAEItemStack stack) { + this.duality.onStackReturnedToNetwork(stack); + } + @Override public DualityInterface getInterfaceDuality() { return this.duality; @@ -274,6 +279,10 @@ public class TileInterface extends AENetworkInvTile implements IGridTickable, II return this.duality.injectCraftedItems(link, items, mode); } + public void updateRedstoneState(){ + this.duality.updateRedstoneState(); + } + @Override public void jobStateChange(final ICraftingLink link) { this.duality.jobStateChange(link); diff --git a/src/main/java/appeng/util/ConfigManager.java b/src/main/java/appeng/util/ConfigManager.java index 289f1f6c7..93d765ffe 100644 --- a/src/main/java/appeng/util/ConfigManager.java +++ b/src/main/java/appeng/util/ConfigManager.java @@ -25,6 +25,7 @@ import appeng.api.config.StorageFilter; import appeng.api.util.IConfigManager; import appeng.core.AELog; import net.minecraft.nbt.NBTTagCompound; +import org.jetbrains.annotations.Nullable; import java.util.EnumMap; import java.util.Map; diff --git a/src/main/resources/assets/appliedenergistics2/lang/en_us.lang b/src/main/resources/assets/appliedenergistics2/lang/en_us.lang index 71726f2ff..0710950fd 100644 --- a/src/main/resources/assets/appliedenergistics2/lang/en_us.lang +++ b/src/main/resources/assets/appliedenergistics2/lang/en_us.lang @@ -327,6 +327,12 @@ gui.tooltips.appliedenergistics2.Disabled=Disabled gui.tooltips.appliedenergistics2.Enable=Enabled gui.tooltips.appliedenergistics2.Blocking=Do not push crafting items if inventory contains items. gui.tooltips.appliedenergistics2.NonBlocking=Ignore the contents of the target inventory. +gui.tooltips.appliedenergistics2.LockCraftingMode=Lock Crafting +gui.tooltips.appliedenergistics2.LockCraftingModeNone=Never +gui.tooltips.appliedenergistics2.LockCraftingUntilRedstonePulse=Until redstone pulse is received +gui.tooltips.appliedenergistics2.LockCraftingWhileRedstoneHigh=With redstone signal +gui.tooltips.appliedenergistics2.LockCraftingWhileRedstoneLow=Without redstone signal +gui.tooltips.appliedenergistics2.LockCraftingUntilResultReturned=Until primary crafting result is returned gui.tooltips.appliedenergistics2.Craft=Crafting Behavior gui.tooltips.appliedenergistics2.CraftOnly=Do not use stocked items, only craft items while exporting. gui.tooltips.appliedenergistics2.CraftEither=Use stocked items, or craft items while exporting. diff --git a/src/main/resources/assets/appliedenergistics2/lang/zh_cn.lang b/src/main/resources/assets/appliedenergistics2/lang/zh_cn.lang index 8f36e0920..d93d0bd02 100644 --- a/src/main/resources/assets/appliedenergistics2/lang/zh_cn.lang +++ b/src/main/resources/assets/appliedenergistics2/lang/zh_cn.lang @@ -324,6 +324,12 @@ gui.tooltips.appliedenergistics2.Disabled=禁用 gui.tooltips.appliedenergistics2.Enable=启用 gui.tooltips.appliedenergistics2.Blocking=当容器内有物品时不传送物品。 gui.tooltips.appliedenergistics2.NonBlocking=忽略目标容器的内容。 +gui.tooltips.appliedenergistics2.LockCraftingMode=合成锁定 +gui.tooltips.appliedenergistics2.LockCraftingModeNone=永不锁定 +gui.tooltips.appliedenergistics2.LockCraftingUntilRedstonePulse=接收到脉冲信号时解锁 +gui.tooltips.appliedenergistics2.LockCraftingWhileRedstoneHigh=有红石信号时锁定 +gui.tooltips.appliedenergistics2.LockCraftingWhileRedstoneLow=无红石信号时锁定 +gui.tooltips.appliedenergistics2.LockCraftingUntilResultReturned=接受到编码样板主产物时解锁 gui.tooltips.appliedenergistics2.Craft=输出时若有需要则合成物品。 gui.tooltips.appliedenergistics2.CraftOnly=不使用已存储物品,只使用合成物品。 gui.tooltips.appliedenergistics2.CraftEither=输出时使用已存储物品或合成物品。 diff --git a/src/main/resources/assets/appliedenergistics2/textures/guis/states.png b/src/main/resources/assets/appliedenergistics2/textures/guis/states.png index 8d03ff924..830ddb80d 100644 Binary files a/src/main/resources/assets/appliedenergistics2/textures/guis/states.png and b/src/main/resources/assets/appliedenergistics2/textures/guis/states.png differ