diff --git a/dependencies.gradle b/dependencies.gradle index 0dca99f84c..8ab0083f57 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -6,7 +6,7 @@ dependencies { shadowImplementation name: 'OC-JNLua-Natives', version: '20220928.1', ext: 'jar' api("com.github.GTNewHorizons:AE2FluidCraft-Rework:1.5.95-gtnh:dev") - api("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-999-GTNH:dev") + api("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-1006-GTNH:dev") implementation("com.github.GTNewHorizons:GTNHLib:0.11.24:dev") {transitive = false} compileOnly("com.github.GTNewHorizons:Angelica:2.1.52:api") {transitive = false} diff --git a/src/main/scala/li/cil/oc/integration/appeng/ConverterPattern.scala b/src/main/scala/li/cil/oc/integration/appeng/ConverterPattern.scala index 769154711f..b2f84a4f71 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/ConverterPattern.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/ConverterPattern.scala @@ -26,6 +26,8 @@ object ConverterPattern extends Converter { output += "inputs" -> inputs output += "outputs" -> outputs output += "isCraftable" -> Boolean.box(encodedValue.getBoolean("crafting")) + output += "substitute" -> Boolean.box(encodedValue.getBoolean("substitute")) + output += "beSubstitute" -> Boolean.box(encodedValue.getBoolean("beSubstitute")) } } catch { case ignored: Throwable => diff --git a/src/main/scala/li/cil/oc/integration/appeng/DriverPartInterfaceTerminal.scala b/src/main/scala/li/cil/oc/integration/appeng/DriverPartInterfaceTerminal.scala index 857a7ae07c..ab52d4844e 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/DriverPartInterfaceTerminal.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/DriverPartInterfaceTerminal.scala @@ -5,7 +5,8 @@ import appeng.api.parts.IPartHost import appeng.api.util.{DimensionalCoord, IInterfaceViewable} import appeng.parts.AEBasePart import appeng.parts.p2p.PartP2PTunnel -import appeng.parts.reporting.PartInterfaceTerminal +import appeng.parts.reporting.{PartInterfaceTerminal, PartPatternTerminal} +import appeng.util.inv.WrapperInvSlot import li.cil.oc.api.driver import li.cil.oc.api.driver.{EnvironmentProvider, NamedBlock} import li.cil.oc.api.machine.{Arguments, Callback, Context} @@ -20,7 +21,7 @@ import net.minecraftforge.common.util.Constants.NBT import net.minecraftforge.common.util.ForgeDirection import java.util -import scala.collection.JavaConversions.asScalaSet +import scala.collection.JavaConversions.asScalaIterator import scala.collection.JavaConverters._ import scala.collection.mutable @@ -111,7 +112,7 @@ object DriverPartInterfaceTerminal extends driver.SidedBlock { case _ => ForgeDirection.UNKNOWN } val slot: Integer = args.get("slot") match { - case value: java.lang.Number => value.intValue + case value: java.lang.Number => value.intValue - 1 case _ => -1 } TransferData(location, side)(slot) @@ -121,16 +122,13 @@ object DriverPartInterfaceTerminal extends driver.SidedBlock { private case class TransferTask(source: TransferData, target: TransferData) private def findMachines(targets: Array[TransferData]) = { - val result = mutable.Map[TransferData, IInterfaceViewable]() - val it = allMachines.iterator + val result = mutable.Map[TransferData, PatternContainer]() + val it = allMachines while (it.hasNext && result.size < targets.length) { val machine = it.next() val location = machine.getLocation - val side = machine match { - case part: AEBasePart => part.getSide - case _ => ForgeDirection.UNKNOWN - } + val side = machine.getSide targets.find(t => t.location == location && t.side == side).foreach { matchedData => result(matchedData) = machine } @@ -177,18 +175,28 @@ object DriverPartInterfaceTerminal extends driver.SidedBlock { }.toArray } - private def allMachines: Iterable[IInterfaceViewable] = { + private def allMachines: Iterator[PatternContainer] = { val grid = getGrid - if (grid == null) return Array.empty[IInterfaceViewable] - AEApi.instance().registries().interfaceTerminal().getSupportedClasses.view - .flatMap(c => grid.getMachines(c).asScala) - .map(_.getMachine.asInstanceOf[IInterfaceViewable]) - .filter { m => - m.shouldDisplay && (m match { - case p: PartP2PTunnel[_] => !p.isOutput - case _ => true - }) - } + if (grid == null) { + Iterator.empty + } + else { + val interfaces = AEApi.instance().registries().interfaceTerminal().getSupportedClasses.iterator + .flatMap(c => grid.getMachines(c).asScala) + .map(_.getMachine) + .collect { + case m: IInterfaceViewable if m.shouldDisplay && (m match { + case p: PartP2PTunnel[_] => !p.isOutput + case _ => true + }) => new InterfaceAdapter(m) + } + val patternTerminals = grid.getMachines(classOf[PartPatternTerminal]).asScala.iterator + .map(_.getMachine) + .collect { + case t: PartPatternTerminal => new PatternTerminalAdapter(t) + } + interfaces ++ patternTerminals + } } private def getGrid = part.getActionableNode.getGrid @@ -201,8 +209,47 @@ object DriverPartInterfaceTerminal extends driver.SidedBlock { else null } - class InterfaceViewableArrayValue(it: Iterable[IInterfaceViewable]) extends AbstractValue { - def this() = this(Iterable.empty) + trait PatternContainer { + def getName: String + + def getPatterns: IInventory + + def numSlots: Int + + def getLocation: DimensionalCoord + + def getSide: ForgeDirection + } + + private class InterfaceAdapter(val machine: IInterfaceViewable) extends PatternContainer { + override def getName: String = machine.getName + + override def getPatterns: IInventory = machine.getPatterns + + override def numSlots: Int = machine.numSlots + + override def getLocation: DimensionalCoord = machine.getLocation + + override def getSide: ForgeDirection = machine match { + case part: AEBasePart => part.getSide + case _ => ForgeDirection.UNKNOWN + } + } + + private class PatternTerminalAdapter(val terminal: PartPatternTerminal) extends PatternContainer { + override def getName: String = terminal.getCustomName + + override def getPatterns: IInventory = new WrapperInvSlot(terminal.getInventoryByName("pattern")).getWrapper(1) + + override def numSlots: Int = 1 + + override def getLocation: DimensionalCoord = terminal.getLocation + + override def getSide: ForgeDirection = terminal.getSide + } + + private class InterfaceViewableArrayValue(it: Iterator[PatternContainer]) extends AbstractValue { + def this() = this(Iterator.empty) private case class InterfaceViewableInfo(name: String, location: DimensionalCoord, side: ForgeDirection, patterns: Array[ItemStack]) @@ -212,7 +259,7 @@ object DriverPartInterfaceTerminal extends driver.SidedBlock { "name" -> info.name, "location" -> info.location, "side" -> info.side, - "patterns" -> info.patterns.zipWithIndex.map(_.swap).toMap + "patterns" -> info.patterns.zipWithIndex.map { case (pattern, id) => (id + 1, pattern) }.toMap ) } else { Map( @@ -226,12 +273,9 @@ object DriverPartInterfaceTerminal extends driver.SidedBlock { private var array = it.map { machine => val name = machine.getName val location = machine.getLocation - val side = machine match { - case part: AEBasePart => part.getSide - case _ => ForgeDirection.UNKNOWN - } + val side = machine.getSide val patterns = machine.getPatterns - val patternsArray = Array.tabulate(machine.rows() * machine.rowSize()) { index => + val patternsArray = Array.tabulate(machine.numSlots) { index => val stack = patterns.getStackInSlot(index) if (stack == null) null else stack.copy() } diff --git a/src/main/scala/li/cil/oc/integration/appeng/DriverPartPatternTerminal.scala b/src/main/scala/li/cil/oc/integration/appeng/DriverPartPatternTerminal.scala new file mode 100644 index 0000000000..844801452f --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/appeng/DriverPartPatternTerminal.scala @@ -0,0 +1,175 @@ +package li.cil.oc.integration.appeng + +import appeng.api.parts.IPatternTerminal.PatternEncodeListener +import appeng.api.parts.{IPartHost, IPatternTerminal} +import appeng.api.storage.StorageName +import appeng.parts.reporting.PartPatternTerminal +import li.cil.oc.api.driver +import li.cil.oc.api.driver.NamedBlock +import li.cil.oc.api.machine.{Arguments, Callback, Context} +import li.cil.oc.api.network.{ManagedEnvironment, Node} +import li.cil.oc.integration.ManagedTileEntityEnvironment +import li.cil.oc.integration.appeng.internal.PartTerminalPatternEnvironment +import li.cil.oc.server.driver.Registry +import li.cil.oc.util.ExtendedArguments.extendedArguments +import li.cil.oc.util.ResultWrapper.result +import net.minecraft.item.ItemStack +import net.minecraft.world.World +import net.minecraftforge.common.util.ForgeDirection + +import scala.collection.mutable +import scala.reflect.ClassTag + +object DriverPartPatternTerminal extends driver.SidedBlock { + override def worksWith(world: World, x: Int, y: Int, z: Int, side: ForgeDirection): Boolean = + world.getTileEntity(x, y, z) match { + case container: IPartHost => ForgeDirection.VALID_DIRECTIONS.map(container.getPart).exists(_.isInstanceOf[PartPatternTerminal]) + case _ => false + } + + override def createEnvironment(world: World, x: Int, y: Int, z: Int, side: ForgeDirection): ManagedEnvironment = { + val host = world.getTileEntity(x, y, z).asInstanceOf[IPartHost] + new Environment(host) + } + + final class Environment(val host: IPartHost)(implicit val tag: ClassTag[PartPatternTerminal]) extends ManagedTileEntityEnvironment[IPartHost](host, "me_pattern_terminal") with NamedBlock with PartTerminalPatternEnvironment[PartPatternTerminal] { + override def preferredName = "me_pattern_terminal" + + override def priority = 0 + + private val listeners = mutable.ArrayBuffer[(PartPatternTerminal, PatternEncodeListener)]() + private def bindListeners(): Unit = { + unbindListeners() + val location = Registry.convert(result(host.getLocation)) + ForgeDirection.VALID_DIRECTIONS.foreach { side => + host.getPart(side) match { + case part: PartPatternTerminal => + val listener = new PatternEncodeListener { + override def onEncoded(terminal: IPatternTerminal, pattern: ItemStack): Unit = { + val args: Array[AnyRef] = Array.concat(result("me_pattern_encoded"), location, result(side.ordinal)) + node.sendToReachable("computer.signal", args: _*) + } + } + part.addPatternEncodeListeners(listener) + listeners += ((part, listener)) + case _ => + } + } + } + private def unbindListeners(): Unit = { + listeners.foreach { case (part, listener) => + part.removePatternEncodeListeners(listener) + } + listeners.clear() + } + override def onConnect(node: Node): Unit = { + super.onConnect(node) + if (node == this.node) { + bindListeners() + } + } + + override def onDisconnect(node: Node): Unit = { + super.onDisconnect(node) + if (node == this.node) { + unbindListeners() + } + } + + @Callback(doc = "function(side:number):boolean -- Return crafting mode.") + def isCraftingMode(context: Context, args: Arguments): Array[AnyRef] = { + val part = getPart(args.checkSideAny(0)) + result(part.isCraftingRecipe) + } + + @Callback(doc = "function(side:number, mode:boolean) -- Set crafting mode.") + def setCraftingMode(context: Context, args: Arguments): Array[AnyRef] = { + val part = getPart(args.checkSideAny(0)) + part.setCraftingRecipe(args.checkBoolean(1)) + result(null) + } + + @Callback(doc = "function(side:number):boolean -- Return substitution state.") + def isSubstitution(context: Context, args: Arguments): Array[AnyRef] = { + val part = getPart(args.checkSideAny(0)) + result(part.isSubstitution) + } + + @Callback(doc = "function(side:number, mode:boolean) -- Set if substitution.") + def setSubstitution(context: Context, args: Arguments): Array[AnyRef] = { + val part = getPart(args.checkSideAny(0)) + part.setSubstitution(args.checkBoolean(1)) + result(null) + } + + @Callback(doc = "function(side:number):boolean -- Return canBeSubstitution state.") + def isCanBeSubstitution(context: Context, args: Arguments): Array[AnyRef] = { + val part = getPart(args.checkSideAny(0)) + result(part.canBeSubstitution) + } + + @Callback(doc = "function(side:number, mode:boolean) -- Set if canBeSubstitution.") + def setCanBeSubstitution(context: Context, args: Arguments): Array[AnyRef] = { + val part = getPart(args.checkSideAny(0)) + part.setCanBeSubstitution(args.checkBoolean(1)) + result(null) + } + + @Callback(doc = "function(side:number, index:number[, detail:table, type:string]):boolean -- Set the pattern input.") + def setInterfacePatternInput(context: Context, args: Arguments): Array[AnyRef] = + setPatternSlot(context, args, "in") + + @Callback(doc = "function(side:number, index:number[, detail:table, type:string]):boolean -- Set the pattern input.") + def setInterfacePatternOutput(context: Context, args: Arguments): Array[AnyRef] = + setPatternSlot(context, args, "out") + + @Callback(doc = "function(side:number, index:number):boolean -- Get the terminal pattern input stack.") + def getTerminalPatternInput(context: Context, args: Arguments): Array[AnyRef] = { + val part = getPart(args.checkSideAny(0)) + val index = args.checkInteger(1) + val inv = part.getAEInventoryByName(StorageName.CRAFTING_INPUT) + result(inv.getAEStackInSlot(index)) + } + + @Callback(doc = "function(side:number, index:number[, detail:table, type:string]):boolean -- Set the terminal pattern input stack.") + def setTerminalPatternInput(context: Context, args: Arguments): Array[AnyRef] = { + val part = getPart(args.checkSideAny(0)) + val index = args.checkInteger(1) - 1 + val stack = getAEStack(context, args, 0) + val inv = part.getAEInventoryByName(StorageName.CRAFTING_INPUT) + inv.putAEStackInSlot(index, stack) + result(null) + } + + @Callback(doc = "function(side:number, index:number):boolean -- Get the terminal pattern output stack.") + def getTerminalPatternOutput(context: Context, args: Arguments): Array[AnyRef] = { + val part = getPart(args.checkSideAny(0)) + val index = args.checkInteger(1) + val inv = part.getAEInventoryByName(StorageName.CRAFTING_OUTPUT) + result(inv.getAEStackInSlot(index)) + } + + @Callback(doc = "function(side:number):table -- Get the pattern in the terminal.") + def getTerminalPattern(context: Context, args: Arguments): Array[AnyRef] = { + val inv = getPatternInventory(context, args) + val stack = inv.getStackInSlot(1) + result(stack) + } + + @Callback(doc = "function(side:number, index:number[, detail:table, type:string]):boolean -- Set the terminal pattern input stack.") + def setTerminalPatternOutput(context: Context, args: Arguments): Array[AnyRef] = { + val part = getPart(args.checkSideAny(0)) + val index = args.checkInteger(1) - 1 + val stack = getAEStack(context, args, 0) + val inv = part.getAEInventoryByName(StorageName.CRAFTING_OUTPUT) + inv.putAEStackInSlot(index, stack) + result(null) + } + + @Callback(doc = "function(side:number):boolean -- Encode pattern.") + def encode(context: Context, args: Arguments): Array[AnyRef] = { + val part = getPart(args.checkSideAny(0)) + result(part.encode(part.getCustomName, host.getTile.getWorldObj)) + } + } +} diff --git a/src/main/scala/li/cil/oc/integration/appeng/ModAppEng.scala b/src/main/scala/li/cil/oc/integration/appeng/ModAppEng.scala index bbcebbc6d2..6e204342df 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/ModAppEng.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/ModAppEng.scala @@ -37,6 +37,7 @@ object ModAppEng extends ModProxy { Driver.add(DriverStorageBus) Driver.add(DriverPartInterface) Driver.add(DriverPartInterfaceTerminal) + Driver.add(DriverPartPatternTerminal) Driver.add(DriverBlockInterface) Driver.add(DriverUpgradeAE) diff --git a/src/main/scala/li/cil/oc/integration/appeng/internal/PatternEnvironment.scala b/src/main/scala/li/cil/oc/integration/appeng/internal/PatternEnvironment.scala index 422958014d..08dc13c671 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/internal/PatternEnvironment.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/internal/PatternEnvironment.scala @@ -3,6 +3,7 @@ package li.cil.oc.integration.appeng.internal import appeng.api.implementations.tiles.ISegmentedInventory import appeng.api.parts.IPart import appeng.api.storage.data.IAEStack +import appeng.parts.reporting.PartPatternTerminal import appeng.tile.misc.TileInterface import appeng.util.Platform import appeng.util.item.AEItemStack @@ -22,17 +23,22 @@ trait PatternEnvironment extends Environment { protected def offset: Int = 0 - private def getPatternNBT(context: Context, args: Arguments, tag: String) = { - val inv = getPatternInventory(context, args) - val slot = args.checkSlot(inv, offset) - val pattern = inv.getStackInSlot(args.checkSlot(inv, offset)) - val index = args.checkInteger(offset + 1) - 1 + protected def getPatternNBTInternal(inv: IInventory, slot: Int, index: Int, tag: String) = { + val pattern = inv.getStackInSlot(slot) if (index < 0 || index > 511) throw new IllegalArgumentException("Invalid index!") val encodedValue = pattern.getTagCompound if (encodedValue == null) throw new IllegalArgumentException("No pattern here!") val nbt = encodedValue.getTagList(tag, NBT.TAG_COMPOUND) + (pattern, encodedValue, nbt) + } + + protected def getPatternNBT(context: Context, args: Arguments, tag: String) = { + val inv = getPatternInventory(context, args) + val slot = args.checkSlot(inv, offset) + val index = args.checkInteger(offset + 1) - 1 + val (pattern, encodedValue, nbt) = getPatternNBTInternal(inv, slot, index, tag) (inv, index, slot, pattern, encodedValue, nbt) } @@ -49,11 +55,8 @@ trait PatternEnvironment extends Environment { }) } - // function(slot:number, index:number[, database:address, entry:number, size:number]):boolean - // function(slot:number, index:number[, detail:table, type:string]):boolean - def setPatternSlot(context: Context, args: Arguments, tag: String): Array[AnyRef] = { - val (inv, index, slot, pattern, encodedValue, inTag) = getPatternNBT(context, args, tag) - val stack: IAEStack[_] = if (args.count() <= offset + 2) null + def getAEStack(context: Context, args: Arguments, offset: Int): IAEStack[_] = { + if (args.count() <= offset + 2) null else if (args.isTable(offset + 2)) { val table = args.checkTable(offset + 2) val tp = args.checkString(offset + 3) @@ -62,6 +65,13 @@ trait PatternEnvironment extends Environment { else { AEItemStack.create(DatabaseAccess.getStackFromDatabase(node, args, offset + 2)) } + } + + // function(slot:number, index:number[, database:address, entry:number, size:number]):boolean + // function(slot:number, index:number[, detail:table, type:string]):boolean + def setPatternSlot(context: Context, args: Arguments, tag: String): Array[AnyRef] = { + val (inv, index, slot, pattern, encodedValue, inTag) = getPatternNBT(context, args, tag) + val stack: IAEStack[_] = getAEStack(context, args, offset) while (inTag.tagCount() <= index) inTag.appendTag(new NBTTagCompound()) @@ -95,4 +105,22 @@ trait PartPatternEnvironment[PartType <: ISegmentedInventory with IPart] extends val side = args.checkSideAny(0) getPart(side).getInventoryByName("patterns") } -} \ No newline at end of file +} + +trait PartTerminalPatternEnvironment[PartType <: PartPatternTerminal] extends PartPatternEnvironment[PartType] +{ + override def offset: Int = 0 + + override def getPatternInventory(context: Context, args: Arguments): IInventory = { + val side = args.checkSideAny(0) + getPart(side).getInventoryByName("pattern") + } + + override def getPatternNBT(context: Context, args: Arguments, tag: String) = { + val inv = getPatternInventory(context, args) + val slot = 1 + val index = args.checkInteger(offset + 1) - 1 + val (pattern, encodedValue, nbt) = getPatternNBTInternal(inv, slot, index, tag) + (inv, index, slot, pattern, encodedValue, nbt) + } +}