From ee06bb2a529b3a4d49036c2d2df2ea04f1475d4f Mon Sep 17 00:00:00 2001 From: hinyb <40139991+hinyb@users.noreply.github.com> Date: Mon, 29 Dec 2025 01:03:28 +0800 Subject: [PATCH 01/11] feat(appeng): add allFluids and network_changed event subscription. --- .../integration/appeng/NetworkControl.scala | 82 +++++++++++++++---- 1 file changed, 67 insertions(+), 15 deletions(-) diff --git a/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala b/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala index 107a59137c..9fbb20194b 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala @@ -7,9 +7,10 @@ import appeng.api.networking.IGridNode import appeng.api.networking.crafting.{CraftingItemList, ICraftingLink, ICraftingRequester} import appeng.api.networking.security.{BaseActionSource, IActionHost, MachineSource} import appeng.api.networking.storage.IBaseMonitor -import appeng.api.storage.IMEMonitorHandlerReceiver -import appeng.api.storage.data.{IAEFluidStack, IAEItemStack, IItemList} +import appeng.api.storage.{IMEMonitor, IMEMonitorHandlerReceiver} +import appeng.api.storage.data.{IAEFluidStack, IAEItemStack, IAEStack, IItemList} import appeng.api.util.AECableType +import appeng.me.GridAccessException import appeng.me.cluster.implementations.CraftingCPUCluster import appeng.me.helpers.IGridProxyable import appeng.tile.crafting.TileCraftingMonitorTile @@ -43,6 +44,7 @@ import javax.annotation.Nonnull import scala.collection.convert.WrapAsJava._ import scala.collection.convert.WrapAsScala._ import scala.collection.mutable +import scala.collection.mutable.ArrayBuffer import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future import scala.language.existentials @@ -133,9 +135,14 @@ trait NetworkControl[AETile >: Null <: TileEntity with IGridProxyable with IActi .toArray) } - @Callback(doc = "function():userdata -- Get an iterator object for the list of the items in the network.") + @Callback(doc = "function([subscribe:bool]):userdata -- Get an iterator object for the list of the items in the network. [Event: network_item_changed]") def allItems(context: Context, args: Arguments): Array[AnyRef] = { - result(new NetworkContents(tile)) + result(new ItemNetworkContents(tile, node, args.optBoolean(0, false))) + } + + @Callback(doc = "function([subscribe:bool]):userdata -- Get an iterator object for the list of the fluids in the network. [Event: network_fluid_changed]") + def allFluids(context: Context, args: Arguments): Array[AnyRef] = { + result(new FluidNetworkContents(tile, node, args.optBoolean(0, false))) } @Callback(doc = "function(filter:table, dbAddress:string[, startSlot:number[, count:number]]): Boolean -- Store items in the network matching the specified filter in the database with the specified address.") @@ -369,7 +376,7 @@ object NetworkControl { result(aeStack.getItemStack) } } - + private def getCpu = { if (cpu == null && controller != null) { var i = 0 @@ -453,25 +460,39 @@ object NetworkControl { } //noinspection ConvertNullInitializerToUnderscore - class NetworkContents(var controller: TileEntity with IGridProxyable with IActionHost) extends AbstractValue with IMEMonitorHandlerReceiver[IAEItemStack] + abstract class NetworkContents[T <: IAEStack[T]](var controller: TileEntity with IGridProxyable with IActionHost, var node: Node, var subscribe: Boolean) extends AbstractValue with IMEMonitorHandlerReceiver[T] { - def this() = this(null) - if (controller != null) - controller.getProxy.getStorage.getItemInventory.addListener(this, null) + def this() = this(null, null, false) + + def getInventory: IMEMonitor[T] + + def withInventory[R](action: IMEMonitor[T] => R): Option[R] = { + for { + _ <- Option(controller) + inv <- Option(getInventory) + } yield action(inv) + } + + def convertItem(stack: IAEStack[_]): java.util.HashMap[String, AnyRef] + val event_name: String = "network_changed" + + withInventory(_.addListener(this, null)) + private var addedListener = true - private var items : IItemList[IAEItemStack] = null - private var itemIterator : java.util.Iterator[IAEItemStack] = null + private var items : IItemList[T] = null + private var itemIterator : java.util.Iterator[T] = null private var index = 0 override def call(context: Context, arguments: Arguments): Array[AnyRef] = { if (controller == null) return null if (!addedListener) { - controller.getProxy.getStorage.getItemInventory.addListener(this, null) + withInventory(_.addListener(this, null)) addedListener = true } if (items == null) { - items = controller.getProxy.getStorage.getItemInventory.getStorageList + val list: Option[IItemList[T]] = withInventory(_.getStorageList) + items = list.orNull if (items != null) itemIterator = items.iterator if (itemIterator != null) @@ -485,7 +506,7 @@ object NetworkControl { if (!this.itemIterator.hasNext) return null index += 1 - Array[AnyRef](convert(itemIterator.next(), controller)) + result(convertItem(itemIterator.next())) } override def load(nbt: NBTTagCompound) { @@ -511,11 +532,42 @@ object NetworkControl { override def onListUpdate(): Unit = { this.items = null } - override def postChange(monitor: IBaseMonitor[IAEItemStack], change: lang.Iterable[IAEItemStack], actionSource: BaseActionSource): Unit = { + override def postChange(monitor: IBaseMonitor[T], change: lang.Iterable[T], actionSource: BaseActionSource): Unit = { + if (subscribe && node != null) { + val flatArgs = ArrayBuffer[Object](event_name) + flatArgs ++= change.map(convertItem(_)) + node.sendToReachable("computer.signal", flatArgs: _*) + } this.items = null } } + class ItemNetworkContents(controller: TileEntity with IGridProxyable with IActionHost, node: Node, subscribe: Boolean) extends NetworkContents[IAEItemStack](controller, node, subscribe) { + override val event_name: String = "network_item_changed" + + def getInventory: IMEMonitor[IAEItemStack] = { + try return controller.getProxy.getStorage.getItemInventory + catch { + case ignored: GridAccessException => + } + return null + } + override def convertItem(stack: IAEStack[_]): java.util.HashMap[String, AnyRef] = convert(stack.asInstanceOf[IAEItemStack], controller) + } + + class FluidNetworkContents(controller: TileEntity with IGridProxyable with IActionHost, node: Node, subscribe: Boolean) extends NetworkContents[IAEFluidStack](controller, node, subscribe) { + override val event_name: String = "network_fluid_changed" + + override def getInventory: IMEMonitor[IAEFluidStack] = { + try return controller.getProxy.getStorage.getFluidInventory + catch { + case ignored: GridAccessException => + } + return null + } + override def convertItem(stack: IAEStack[_]): java.util.HashMap[String, AnyRef] = convert(stack.asInstanceOf[IAEFluidStack], controller) + } + def aeCraftItem(aeItem: IAEItemStack, tile: TileEntity with IGridProxyable): IAEItemStack = { val patterns = tile.getProxy.getCrafting.getCraftingFor(aeItem, null, 0, tile.getWorldObj) patterns.find(pattern => pattern.getOutputs.exists(_.isSameType(aeItem))) match { From cde2eb3f14428e2b20b9f4a118214ac9d9f7df07 Mon Sep 17 00:00:00 2001 From: hinyb <40139991+hinyb@users.noreply.github.com> Date: Wed, 31 Dec 2025 09:56:06 +0800 Subject: [PATCH 02/11] fix: resolve thread-safety issues in allItems and allFluids. --- .../integration/appeng/NetworkControl.scala | 40 ++++++++----------- 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala b/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala index 9fbb20194b..d9c8dc236a 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala @@ -476,33 +476,25 @@ object NetworkControl { def convertItem(stack: IAEStack[_]): java.util.HashMap[String, AnyRef] val event_name: String = "network_changed" - withInventory(_.addListener(this, null)) - - private var addedListener = true private var items : IItemList[T] = null private var itemIterator : java.util.Iterator[T] = null private var index = 0 + private def updateItems(): Unit = { + val list: Option[IItemList[T]] = withInventory(_.getStorageList) + items = list.orNull + if (items != null) { + itemIterator = items.iterator + itemIterator.drop(index) + } + } + + updateItems() + withInventory(_.addListener(this, null)) + override def call(context: Context, arguments: Arguments): Array[AnyRef] = { - if (controller == null) + if (controller == null || itemIterator == null) return null - if (!addedListener) { - withInventory(_.addListener(this, null)) - addedListener = true - } - if (items == null) { - val list: Option[IItemList[T]] = withInventory(_.getStorageList) - items = list.orNull - if (items != null) - itemIterator = items.iterator - if (itemIterator != null) - for (_ <- 1 to index) { - if (itemIterator.hasNext) - itemIterator.next - } - } - if (this.itemIterator == null && this.items != null) - this.itemIterator = items.iterator if (!this.itemIterator.hasNext) return null index += 1 @@ -511,7 +503,7 @@ object NetworkControl { override def load(nbt: NBTTagCompound) { super.load(nbt) - addedListener = false + withInventory(_.addListener(this, null)) loadController(nbt, c => controller = c) } @@ -530,7 +522,7 @@ object NetworkControl { override def isValid(verificationToken: Any): Boolean = valid override def onListUpdate(): Unit = { - this.items = null + updateItems() } override def postChange(monitor: IBaseMonitor[T], change: lang.Iterable[T], actionSource: BaseActionSource): Unit = { if (subscribe && node != null) { @@ -538,7 +530,7 @@ object NetworkControl { flatArgs ++= change.map(convertItem(_)) node.sendToReachable("computer.signal", flatArgs: _*) } - this.items = null + updateItems() } } From 8e830a5b6f5b18905fd637e34b2f67b04a49a8cc Mon Sep 17 00:00:00 2001 From: hinyb <40139991+hinyb@users.noreply.github.com> Date: Wed, 7 Jan 2026 02:38:57 +0800 Subject: [PATCH 03/11] feat(appeng): add fluid support. --- .../integration/appeng/NetworkControl.scala | 102 ++++++++++++------ 1 file changed, 70 insertions(+), 32 deletions(-) diff --git a/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala b/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala index d9c8dc236a..ac85c85e4f 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala @@ -1,26 +1,23 @@ package li.cil.oc.integration.appeng -import java.lang import appeng.api.AEApi import appeng.api.config.Actionable import appeng.api.networking.IGridNode import appeng.api.networking.crafting.{CraftingItemList, ICraftingLink, ICraftingRequester} import appeng.api.networking.security.{BaseActionSource, IActionHost, MachineSource} import appeng.api.networking.storage.IBaseMonitor -import appeng.api.storage.{IMEMonitor, IMEMonitorHandlerReceiver} import appeng.api.storage.data.{IAEFluidStack, IAEItemStack, IAEStack, IItemList} +import appeng.api.storage.{IMEMonitor, IMEMonitorHandlerReceiver} import appeng.api.util.AECableType import appeng.me.GridAccessException import appeng.me.cluster.implementations.CraftingCPUCluster import appeng.me.helpers.IGridProxyable import appeng.tile.crafting.TileCraftingMonitorTile import appeng.util.IterationCounter -import appeng.util.item.AEItemStack +import appeng.util.item.{AEFluidStack, AEItemStack} import com.google.common.collect.ImmutableSet import li.cil.oc.OpenComputers -import li.cil.oc.api.machine.Arguments -import li.cil.oc.api.machine.Callback -import li.cil.oc.api.machine.Context +import li.cil.oc.api.machine.{Arguments, Callback, Context} import li.cil.oc.api.network.Node import li.cil.oc.api.prefab.AbstractValue import li.cil.oc.common.EventHandler @@ -39,7 +36,9 @@ import net.minecraft.tileentity.TileEntity import net.minecraftforge.common.DimensionManager import net.minecraftforge.common.util.Constants.NBT import net.minecraftforge.common.util.ForgeDirection +import net.minecraftforge.fluids.{FluidRegistry, FluidStack} +import java.lang import javax.annotation.Nonnull import scala.collection.convert.WrapAsJava._ import scala.collection.convert.WrapAsScala._ @@ -56,9 +55,13 @@ trait NetworkControl[AETile >: Null <: TileEntity with IGridProxyable with IActi def node: Node - private def allItems: Iterable[IAEItemStack] = tile.getProxy.getStorage.getItemInventory.getStorageList - private def getAvailableItem(@Nonnull request: IAEItemStack, iteration: Int): IAEItemStack = tile.getProxy.getStorage.getItemInventory.getAvailableItem(request, iteration) - private def allCraftables: Iterable[IAEItemStack] = allItems.collect{ case aeItem if aeItem.isCraftable => aeCraftItem(aeItem, tile) } + private def allItems: IItemList[IAEItemStack] = tile.getProxy.getStorage.getItemInventory.getStorageList + + private def getAvailableItem(@Nonnull request: IAEItemStack, iteration: Int = IterationCounter.fetchNewId()): IAEItemStack = tile.getProxy.getStorage.getItemInventory.getAvailableItem(request, iteration) + + private def allFluids: IItemList[IAEFluidStack] = tile.getProxy.getStorage.getFluidInventory.getStorageList + + private def getAvailableFluid(@Nonnull request: IAEFluidStack, iteration: Int = IterationCounter.fetchNewId()): IAEFluidStack = tile.getProxy.getStorage.getFluidInventory.getAvailableItem(request, iteration) @Callback(doc = "function():table -- Get a list of tables representing the available CPUs in the network.") def getCpus(context: Context, args: Arguments): Array[AnyRef] = { @@ -82,9 +85,18 @@ trait NetworkControl[AETile >: Null <: TileEntity with IGridProxyable with IActi val filter = args.optTable(0, Map.empty[AnyRef, AnyRef]).collect { case (key: String, value: AnyRef) => (key, value) } - result(allCraftables - .collect{ case aeCraftItem if filter.isEmpty || matches(convert(aeCraftItem, tile), filter) => new NetworkControl.Craftable(tile, aeCraftItem) } - .toArray) + + val builder = mutable.ArrayBuilder.make[AnyRef] + (allItems.view ++ allFluids.view).foreach({ s => + if (s.isCraftable) { + val c = asCraft(s, tile).asInstanceOf[IAEStack[_ <: IAEStack[_]]] + if (matches(convert(c, tile), filter)) { + builder += new Craftable(tile, c) + } + } + }) + + result(builder.result()) } @Callback(doc = "function([filter:table]):table -- Get a list of the stored items in the network.") @@ -135,6 +147,18 @@ trait NetworkControl[AETile >: Null <: TileEntity with IGridProxyable with IActi .toArray) } + @Callback(doc = "function([name:string]):table -- Get a list of the stored fluids in the network.") + def getFluidInNetwork(context: Context, args: Arguments): Array[AnyRef] = { + FluidRegistry.getFluid(args.checkString(0)) match { + case null => result(null) + case fluid => + getAvailableFluid(AEFluidStack.create(new FluidStack(fluid, 0))) match { + case null => result(null) + case fluid => result(convert(fluid, tile)) + } + } + } + @Callback(doc = "function([subscribe:bool]):userdata -- Get an iterator object for the list of the items in the network. [Event: network_item_changed]") def allItems(context: Context, args: Arguments): Array[AnyRef] = { result(new ItemNetworkContents(tile, node, args.optBoolean(0, false))) @@ -152,7 +176,7 @@ trait NetworkControl[AETile >: Null <: TileEntity with IGridProxyable with IActi } DatabaseAccess.withDatabase(node, args.checkString(1), database => { val items = allItems - .collect{ case aeItem if matches(convert(aeItem, tile), filter) => aePotentialItem(aeItem, tile)}.toArray + .collect { case aeItem if matches(convert(aeItem, tile), filter) => aePotentialItem(aeItem, tile) }.toArray val offset = args.optSlot(database.data, 2, 0) val count = args.optInteger(3, Int.MaxValue) min (database.size - offset) min items.length var slot = offset @@ -174,7 +198,7 @@ trait NetworkControl[AETile >: Null <: TileEntity with IGridProxyable with IActi @Callback(doc = "function():table -- Get a list of the stored fluids in the network.") def getFluidsInNetwork(context: Context, args: Arguments): Array[AnyRef] = - result(tile.getProxy.getStorage.getFluidInventory.getStorageList.filter(isFluidVisible).map(fs => convert(fs, tile)).toArray) + result(allFluids.filter(isFluidVisible).map(fs => convert(fs, tile)).toArray) @Callback(doc = "function():number -- Get the average power injection into the network.") def getAvgPowerInjection(context: Context, args: Arguments): Array[AnyRef] = @@ -199,7 +223,7 @@ trait NetworkControl[AETile >: Null <: TileEntity with IGridProxyable with IActi private def matches(stack: java.util.HashMap[String, AnyRef], filter: scala.collection.mutable.Map[String, AnyRef]): Boolean = { if (stack == null) return false filter.forall { - case (key: String, value: AnyRef) => + case (key: String, value: AnyRef) => val stack_value = stack.get(key) if (stack_value == null) false else value match { @@ -216,7 +240,7 @@ trait NetworkControl[AETile >: Null <: TileEntity with IGridProxyable with IActi object NetworkControl { //noinspection ScalaUnusedSymbol - class Craftable(var controller: TileEntity with IGridProxyable with IActionHost, var stack: IAEItemStack) extends AbstractValue with ICraftingRequester { + class Craftable(var controller: TileEntity with IGridProxyable with IActionHost, var stack: IAEStack[_ <: IAEStack[_]]) extends AbstractValue with ICraftingRequester { def this() = this(null, null) private val links = mutable.Set.empty[ICraftingLink] @@ -243,7 +267,10 @@ object NetworkControl { // ----------------------------------------------------------------------- // @Callback(doc = "function():table -- Returns the item stack representation of the crafting result.") - def getItemStack(context: Context, args: Arguments): Array[AnyRef] = Array(stack.getItemStack) + def getStack(context: Context, args: Arguments): Array[AnyRef] = stack match { + case item: IAEItemStack => result(item.getItemStack) + case fluid: IAEFluidStack => result(fluid.getFluidStack) + } @Callback(doc = "function([amount:int[, prioritizePower:boolean[, cpuName:string]]]):userdata -- Requests the item to be crafted, returning an object that allows tracking the crafting status.") def request(context: Context, args: Arguments): Array[AnyRef] = { @@ -252,7 +279,7 @@ object NetworkControl { } val count = args.optInteger(0, 1) - val request = stack.copy + val request = stack.copy.asInstanceOf[IAEStack[_]] request.setStackSize(count) val craftingGrid = controller.getProxy.getCrafting @@ -319,7 +346,7 @@ object NetworkControl { } //noinspection ScalaUnusedSymbol - class Cpu(var controller: TileEntity with IGridProxyable, var index : Int, var cpu : CraftingCPUCluster) extends AbstractValue { + class Cpu(var controller: TileEntity with IGridProxyable, var index: Int, var cpu: CraftingCPUCluster) extends AbstractValue { def this() = this(null, 0, null) @Callback(doc = "function():boolean -- Cancel this CPU current crafting job.") @@ -397,6 +424,7 @@ object NetworkControl { nbt.setInteger("index", index) saveController(controller, nbt) } + override def load(nbt: NBTTagCompound) { super.load(nbt) index = nbt.getInteger("index") @@ -460,24 +488,24 @@ object NetworkControl { } //noinspection ConvertNullInitializerToUnderscore - abstract class NetworkContents[T <: IAEStack[T]](var controller: TileEntity with IGridProxyable with IActionHost, var node: Node, var subscribe: Boolean) extends AbstractValue with IMEMonitorHandlerReceiver[T] - { + abstract class NetworkContents[T <: IAEStack[T]](var controller: TileEntity with IGridProxyable with IActionHost, var node: Node, var subscribe: Boolean) extends AbstractValue with IMEMonitorHandlerReceiver[T] { def this() = this(null, null, false) def getInventory: IMEMonitor[T] def withInventory[R](action: IMEMonitor[T] => R): Option[R] = { for { - _ <- Option(controller) + _ <- Option(controller) inv <- Option(getInventory) } yield action(inv) } def convertItem(stack: IAEStack[_]): java.util.HashMap[String, AnyRef] + val event_name: String = "network_changed" - private var items : IItemList[T] = null - private var itemIterator : java.util.Iterator[T] = null + private var items: IItemList[T] = null + private var itemIterator: java.util.Iterator[T] = null private var index = 0 private def updateItems(): Unit = { @@ -524,6 +552,7 @@ object NetworkControl { override def onListUpdate(): Unit = { updateItems() } + override def postChange(monitor: IBaseMonitor[T], change: lang.Iterable[T], actionSource: BaseActionSource): Unit = { if (subscribe && node != null) { val flatArgs = ArrayBuffer[Object](event_name) @@ -544,6 +573,7 @@ object NetworkControl { } return null } + override def convertItem(stack: IAEStack[_]): java.util.HashMap[String, AnyRef] = convert(stack.asInstanceOf[IAEItemStack], controller) } @@ -557,14 +587,15 @@ object NetworkControl { } return null } + override def convertItem(stack: IAEStack[_]): java.util.HashMap[String, AnyRef] = convert(stack.asInstanceOf[IAEFluidStack], controller) } - def aeCraftItem(aeItem: IAEItemStack, tile: TileEntity with IGridProxyable): IAEItemStack = { - val patterns = tile.getProxy.getCrafting.getCraftingFor(aeItem, null, 0, tile.getWorldObj) - patterns.find(pattern => pattern.getOutputs.exists(_.isSameType(aeItem))) match { - case Some(pattern) => pattern.getOutputs.find(_.isSameType(aeItem)).get - case _ => aeItem.copy.setStackSize(0) // Should not be possible, but hey... + def asCraft(stack: IAEStack[_], tile: TileEntity with IGridProxyable): IAEStack[_] = { + tile.getProxy.getCrafting.getCraftingFor(stack, null, 0, tile.getWorldObj).view.map(a => a.getAEOutputs.find(_.isSameType(stack)).get).headOption.getOrElse[IAEStack[_]] { + val result = stack.copy().asInstanceOf[IAEStack[_ <: IAEStack[_]]] + result.setStackSize(0) + result } } @@ -572,7 +603,7 @@ object NetworkControl { if (aeItem.getStackSize > 0 || !aeItem.isCraftable) aeItem else - aeCraftItem(aeItem, tile) + asCraft(aeItem, tile).asInstanceOf[IAEItemStack] } def hashConvert(value: java.util.HashMap[_, _]) = { @@ -581,10 +612,17 @@ object NetworkControl { hash } + def convert(aeItem: IAEStack[_ <: IAEStack[_]], tile: TileEntity with IGridProxyable): java.util.HashMap[String, AnyRef] = { + aeItem match { + case item: IAEItemStack => convert(item, tile) + case fluid: IAEFluidStack => convert(fluid, tile) + } + } + def convert(aeItem: IAEItemStack, tile: TileEntity with IGridProxyable): java.util.HashMap[String, AnyRef] = { val potentialItem = aePotentialItem(aeItem, tile) val result = Registry.convert(Array[AnyRef](potentialItem.getItemStack)) - .collect { case hash: java.util.HashMap[_,_] => hashConvert(hash) } + .collect { case hash: java.util.HashMap[_, _] => hashConvert(hash) } if (result.length > 0) { val hash = result(0) // it would have been nice to put these fields in a registry convert @@ -608,7 +646,7 @@ object NetworkControl { null } - private def loadController(nbt: NBTTagCompound, f : TileEntity with IGridProxyable with IActionHost => Unit ) : Unit = { + private def loadController(nbt: NBTTagCompound, f: TileEntity with IGridProxyable with IActionHost => Unit): Unit = { if (nbt.hasKey("dimension")) { val dimension = nbt.getInteger("dimension") val x = nbt.getInteger("x") From 7b6ba237e47b011bc08aa9723a02323995fa61f0 Mon Sep 17 00:00:00 2001 From: hinyb <40139991+hinyb@users.noreply.github.com> Date: Mon, 26 Jan 2026 23:17:47 +0800 Subject: [PATCH 04/11] fix: fluid stacks and NetworkContents failing to persist. --- .../oc/integration/appeng/NetworkControl.scala | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala b/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala index ac85c85e4f..f94e3dce5f 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala @@ -13,7 +13,7 @@ import appeng.me.GridAccessException import appeng.me.cluster.implementations.CraftingCPUCluster import appeng.me.helpers.IGridProxyable import appeng.tile.crafting.TileCraftingMonitorTile -import appeng.util.IterationCounter +import appeng.util.{IterationCounter, Platform} import appeng.util.item.{AEFluidStack, AEItemStack} import com.google.common.collect.ImmutableSet import li.cil.oc.OpenComputers @@ -331,7 +331,12 @@ object NetworkControl { override def load(nbt: NBTTagCompound) { super.load(nbt) - stack = AEItemStack.loadItemStackFromNBT(nbt) + if (nbt.hasKey("StackType")) { + stack = Platform.readStackNBT(nbt, true).asInstanceOf[IAEStack[_ <: IAEStack[_]]] + } + else { + stack = AEItemStack.loadItemStackFromNBT(nbt) + } loadController(nbt, c => controller = c) links ++= nbt.getTagList("links", NBT.TAG_COMPOUND).map( (nbt: NBTTagCompound) => AEApi.instance.storage.loadCraftingLink(nbt, this)) @@ -339,7 +344,7 @@ object NetworkControl { override def save(nbt: NBTTagCompound) { super.save(nbt) - stack.writeToNBT(nbt) + Platform.writeStackNBT(stack, nbt, true) saveController(controller, nbt) nbt.setNewTagList("links", links.map(_.writeToNBT _)) } @@ -564,6 +569,8 @@ object NetworkControl { } class ItemNetworkContents(controller: TileEntity with IGridProxyable with IActionHost, node: Node, subscribe: Boolean) extends NetworkContents[IAEItemStack](controller, node, subscribe) { + def this() = this(null, null, false) + override val event_name: String = "network_item_changed" def getInventory: IMEMonitor[IAEItemStack] = { @@ -578,6 +585,8 @@ object NetworkControl { } class FluidNetworkContents(controller: TileEntity with IGridProxyable with IActionHost, node: Node, subscribe: Boolean) extends NetworkContents[IAEFluidStack](controller, node, subscribe) { + def this() = this(null, null, false) + override val event_name: String = "network_fluid_changed" override def getInventory: IMEMonitor[IAEFluidStack] = { From 06be2878cbc50145a6dd071a7aac664b5bb5d001 Mon Sep 17 00:00:00 2001 From: hinyb <40139991+hinyb@users.noreply.github.com> Date: Sun, 15 Feb 2026 20:33:58 +0800 Subject: [PATCH 05/11] feat: full fluid and essentia support via new Stack Type API. BREAKING CHANGE: update the parameter order of `setPatternSlot` to align with `storeInterfacePattern` and `clearInterfacePattern` --- dependencies.gradle | 8 +- .../integration/appeng/AEStackFactory.scala | 73 ++++++++++ .../li/cil/oc/integration/appeng/AEUtil.scala | 2 +- .../integration/appeng/ConverterAEStack.scala | 15 ++ .../appeng/ConverterCellInventory.java | 4 +- .../integration/appeng/ConverterPattern.java | 62 -------- .../integration/appeng/ConverterPattern.scala | 36 +++++ .../appeng/DriverBlockInterface.scala | 136 ++---------------- .../integration/appeng/DriverController.scala | 3 +- .../integration/appeng/DriverExportBus.scala | 130 ++++++++--------- .../integration/appeng/DriverImportBus.scala | 32 +++-- .../appeng/DriverPartInterface.scala | 45 ++++-- .../integration/appeng/DriverUpgradeAE.scala | 6 +- .../cil/oc/integration/appeng/ModAppEng.scala | 14 +- .../integration/appeng/NetworkControl.scala | 112 +++++++-------- .../appeng/PartEnvironmentBase.scala | 65 --------- .../cil/oc/integration/appeng/UpgradeAE.scala | 30 ++-- .../appeng/WirelessHandlerUpgradeAE.scala | 5 +- .../internal/InterfaceEnvironmentBase.scala | 106 ++++++++++++++ .../appeng/internal/PartEnvironmentBase.scala | 82 +++++++++++ .../appeng/internal/PatternEnvironment.scala | 108 ++++++++++++++ .../ConvertAEEssentiaStack.scala | 32 +++++ .../ConvertAspectCraftable.scala | 5 +- .../DriverEssentiaExportBus.scala | 49 +++---- .../DriverEssentiaImportBus.scala | 20 ++- .../ModThaumicEnergistics.scala | 9 +- .../thaumicenergistics/NetworkControl.scala | 28 ++-- .../PartEnvironmentBase.scala | 78 ---------- .../li/cil/oc/integration/util/MapUtils.scala | 40 ++++++ .../vanilla/ConverterFluidStack.scala | 17 ++- .../vanilla/ConverterItemStack.scala | 25 +++- .../scala/li/cil/oc/util/DatabaseAccess.scala | 20 +++ 32 files changed, 846 insertions(+), 551 deletions(-) create mode 100644 src/main/scala/li/cil/oc/integration/appeng/AEStackFactory.scala create mode 100644 src/main/scala/li/cil/oc/integration/appeng/ConverterAEStack.scala delete mode 100644 src/main/scala/li/cil/oc/integration/appeng/ConverterPattern.java create mode 100644 src/main/scala/li/cil/oc/integration/appeng/ConverterPattern.scala delete mode 100644 src/main/scala/li/cil/oc/integration/appeng/PartEnvironmentBase.scala create mode 100644 src/main/scala/li/cil/oc/integration/appeng/internal/InterfaceEnvironmentBase.scala create mode 100644 src/main/scala/li/cil/oc/integration/appeng/internal/PartEnvironmentBase.scala create mode 100644 src/main/scala/li/cil/oc/integration/appeng/internal/PatternEnvironment.scala create mode 100644 src/main/scala/li/cil/oc/integration/thaumicenergistics/ConvertAEEssentiaStack.scala delete mode 100644 src/main/scala/li/cil/oc/integration/thaumicenergistics/PartEnvironmentBase.scala create mode 100644 src/main/scala/li/cil/oc/integration/util/MapUtils.scala diff --git a/dependencies.gradle b/dependencies.gradle index 996b710cf6..58d4ee85c6 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -5,9 +5,9 @@ dependencies { shadowImplementation name: 'OC-JNLua', version: '20230530.0', ext: 'jar' shadowImplementation name: 'OC-JNLua-Natives', version: '20220928.1', ext: 'jar' - api("com.github.GTNewHorizons:AE2FluidCraft-Rework:1.5.39-gtnh:dev") + api("com.github.GTNewHorizons:AE2FluidCraft-Rework:1.5.58-gtnh:dev") compileOnly("com.github.GTNewHorizons:Angelica:1.0.0-beta68a:api") {transitive = false} - api("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-betea-777-GTNH:dev") + api("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-817-GTNH:dev") compileOnly("com.github.GTNewHorizons:Avaritiaddons:1.9.3-GTNH:dev") {transitive = false} compileOnly("com.github.GTNewHorizons:BloodMagic:1.8.8:dev") {transitive = false} compileOnly("com.github.GTNewHorizons:BuildCraft:7.1.48:dev") {transitive = false} @@ -27,7 +27,9 @@ dependencies { compileOnly("com.github.GTNewHorizons:ProjectRed:4.12.8-GTNH:dev") {transitive = false} compileOnly("com.github.GTNewHorizons:Railcraft:9.17.12:dev") {transitive = false} compileOnly("com.github.GTNewHorizons:StructureLib:1.4.25:dev") {transitive = false} - compileOnly("com.github.GTNewHorizons:ThaumicEnergistics:1.7.18-GTNH:dev") {transitive = false} + compileOnly("com.github.GTNewHorizons:ThaumicEnergistics:1.7.27-GTNH:dev") {transitive = false} + runtimeOnly("thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev") {transitive = false} + runtimeOnly("com.github.GTNewHorizons:ThaumicEnergistics:1.7.27-GTNH:dev") {transitive = false} compileOnly("com.github.GTNewHorizons:TinkersMechworks:0.4.1:dev") {transitive = false} compileOnly("com.github.GTNewHorizons:waila:1.9.15:dev") {transitive = false} compileOnly("com.github.GTNewHorizons:WirelessRedstone-CBE:1.7.1:dev") {transitive = false} diff --git a/src/main/scala/li/cil/oc/integration/appeng/AEStackFactory.scala b/src/main/scala/li/cil/oc/integration/appeng/AEStackFactory.scala new file mode 100644 index 0000000000..f0ea1db4f0 --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/appeng/AEStackFactory.scala @@ -0,0 +1,73 @@ +package li.cil.oc.integration.appeng + +import appeng.api.storage.data.{IAEStack, IAEStackType} +import appeng.util.item.{AEFluidStack, AEItemStack} +import li.cil.oc.api.driver.Converter +import li.cil.oc.integration.vanilla.{ConverterFluidStack, ConverterItemStack} + +import java.util +import scala.reflect.ClassTag + +object AEStackFactory { + case class AETypeEntry(stackType: IAEStackType[_ <: IAEStack[_ <: IAEStack[_ <: IAEStack[_]]]], converter: (IAEStack[_], util.Map[AnyRef, AnyRef]) => Unit, parser: util.Map[_, _] => IAEStack[_]) + + private var idRegistry = Map[String, AETypeEntry]() + private var classRegistry = Map[Class[_], AETypeEntry]() + + private class UnregisteredAETypeException(msg: String) extends RuntimeException(msg) + + def register[T <: IAEStack[T]](stackType: IAEStackType[T], converter: (IAEStack[_], util.Map[AnyRef, AnyRef]) => Unit, parser: util.Map[_, _] => T)(implicit tag: ClassTag[T]): Unit = { + val entry = AETypeEntry(stackType, converter, parser) + idRegistry += (stackType.getId -> entry) + classRegistry += (tag.runtimeClass -> entry) + } + + def parse[T <: IAEStack[T]](map: util.Map[_, _])(implicit tag: ClassTag[T]): T = { + classRegistry.get(tag.runtimeClass) match { + case Some(entry) => entry.parser(map).asInstanceOf[T] + case None => throw new UnregisteredAETypeException(s"Type ${tag.runtimeClass} hasn't been registered"); + } + } + + def parse(key: String, map: util.Map[_, _]): IAEStack[_] = { + idRegistry.get(key) match { + case Some(entry) => entry.parser(map) + case None => throw new UnregisteredAETypeException(s"Type $key hasn't been registered"); + } + } + + def convert(stack: IAEStack[_], map: util.Map[AnyRef, AnyRef]): Unit = { + classRegistry.get(stack.getClass) match { + case Some(entry) => entry.converter(stack, map) + case None => throw new UnregisteredAETypeException(s"Type ${stack.getClass} hasn't been registered"); + } + } + + def getEntry(key: String): Option[AETypeEntry] = idRegistry.get(key) + + def getEntry[T <: IAEStack[T]]()(implicit tag: ClassTag[T]): Option[AETypeEntry] = classRegistry.get(tag.runtimeClass) + + def getRegisteredTypes: Iterable[IAEStackType[_]] = idRegistry.values.map(_.stackType) +} + +object ConverterAEItemStack extends Converter { + override def convert(value: Any, output: util.Map[AnyRef, AnyRef]): Unit = value match { + case stack: AEItemStack => ConverterItemStack.convert(stack.getItemStack, output) + case _ => + } + + def parse(args: util.Map[_, _]): AEItemStack = { + AEItemStack.create(ConverterItemStack.parse(args)) + } +} + +object ConverterAEFluidStack extends Converter { + override def convert(value: Any, output: util.Map[AnyRef, AnyRef]): Unit = value match { + case stack: AEFluidStack => ConverterFluidStack.convert(stack.getFluidStack, output) + case _ => + } + + def parse(args: util.Map[_, _]): AEFluidStack = { + AEFluidStack.create(ConverterFluidStack.parse(args)) + } +} diff --git a/src/main/scala/li/cil/oc/integration/appeng/AEUtil.scala b/src/main/scala/li/cil/oc/integration/appeng/AEUtil.scala index e750f07b43..9a9298c8e4 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/AEUtil.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/AEUtil.scala @@ -3,10 +3,10 @@ package li.cil.oc.integration.appeng import appeng.api.AEApi import cpw.mods.fml.common.Loader import cpw.mods.fml.common.versioning.VersionRange +import li.cil.oc.api import li.cil.oc.common.item.Delegator import li.cil.oc.common.item.data.{DroneData, RobotData} import li.cil.oc.integration.Mods -import li.cil.oc.api import net.minecraft.item.ItemStack object AEUtil { diff --git a/src/main/scala/li/cil/oc/integration/appeng/ConverterAEStack.scala b/src/main/scala/li/cil/oc/integration/appeng/ConverterAEStack.scala new file mode 100644 index 0000000000..0784231de3 --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/appeng/ConverterAEStack.scala @@ -0,0 +1,15 @@ +package li.cil.oc.integration.appeng + +import appeng.api.storage.data.IAEStack +import li.cil.oc.api.driver.Converter + +import java.util + +object ConverterAEStack extends Converter { + override def convert(value: Any, output: util.Map[AnyRef, AnyRef]): Unit = { + value match { + case stack: IAEStack[_] => + AEStackFactory.convert(stack, output) + } + } +} \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/integration/appeng/ConverterCellInventory.java b/src/main/scala/li/cil/oc/integration/appeng/ConverterCellInventory.java index 8719d46367..6e3a4f3e9b 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/ConverterCellInventory.java +++ b/src/main/scala/li/cil/oc/integration/appeng/ConverterCellInventory.java @@ -6,12 +6,12 @@ import appeng.api.storage.ICellInventoryHandler; import appeng.api.storage.IMEInventoryHandler; import appeng.api.storage.StorageChannel; -import java.util.Map; - import appeng.util.IterationCounter; import li.cil.oc.api.driver.Converter; import net.minecraft.item.ItemStack; +import java.util.Map; + public final class ConverterCellInventory implements Converter { @Override public void convert(final Object value, final Map output) { diff --git a/src/main/scala/li/cil/oc/integration/appeng/ConverterPattern.java b/src/main/scala/li/cil/oc/integration/appeng/ConverterPattern.java deleted file mode 100644 index 3bf65d95bb..0000000000 --- a/src/main/scala/li/cil/oc/integration/appeng/ConverterPattern.java +++ /dev/null @@ -1,62 +0,0 @@ -package li.cil.oc.integration.appeng; - -import appeng.api.AEApi; -import appeng.helpers.PatternHelper; -import appeng.util.Platform; -import java.util.HashMap; -import java.util.Map; -import li.cil.oc.api.driver.Converter; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.nbt.NBTTagList; - -public final class ConverterPattern implements Converter { - @Override - public void convert(final Object value, final Map output) { - if (value instanceof ItemStack) { - ItemStack is = (ItemStack) value; - try { - final NBTTagCompound encodedValue = is.getTagCompound(); - if (encodedValue != null) { - final NBTTagList inTag = encodedValue.getTagList("in", 10); - final NBTTagList outTag = encodedValue.getTagList("out", 10); - - Map[] inputs = new Map[inTag.tagCount()]; - for (int i = 0; i < inTag.tagCount(); i++) { - inputs[i] = new HashMap<>(); - final NBTTagCompound tag = inTag.getCompoundTagAt(i); - final ItemStack inputItem = Platform.loadItemStackFromNBT(tag); - if (inputItem != null) { - inputs[i].put("name", inputItem.getItem().getItemStackDisplayName(inputItem)); - if (tag.getLong("Cnt") > 0) { - inputs[i].put("count", tag.getLong("Cnt")); - } else { - inputs[i].put("count", inputItem.stackSize); - } - } - } - - Map[] results = new Map[outTag.tagCount()]; - for (int i = 0; i < outTag.tagCount(); i++) { - results[i] = new HashMap<>(); - final NBTTagCompound tag = outTag.getCompoundTagAt(i); - final ItemStack outputItem = Platform.loadItemStackFromNBT(tag); - if (outputItem != null) { - results[i].put("name", outputItem.getItem().getItemStackDisplayName(outputItem)); - if (tag.getLong("Cnt") > 0) { - results[i].put("count", tag.getLong("Cnt")); - } else { - results[i].put("count", outputItem.stackSize); - } - } - } - output.put("inputs", inputs); - output.put("outputs", results); - output.put("isCraftable", encodedValue.getBoolean("crafting")); - } - } catch (final Throwable ignored) { - - } - } - } -} diff --git a/src/main/scala/li/cil/oc/integration/appeng/ConverterPattern.scala b/src/main/scala/li/cil/oc/integration/appeng/ConverterPattern.scala new file mode 100644 index 0000000000..769154711f --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/appeng/ConverterPattern.scala @@ -0,0 +1,36 @@ +package li.cil.oc.integration.appeng + +import appeng.util.Platform +import li.cil.oc.api.driver.Converter +import net.minecraft.item.ItemStack +import net.minecraft.nbt.{NBTTagCompound, NBTTagList} + +import java.util +import scala.collection.convert.WrapAsScala._ + +object ConverterPattern extends Converter { + override def convert(value: AnyRef, output: util.Map[AnyRef, AnyRef]): Unit = { + value match { + case is: ItemStack => + try { + val encodedValue: NBTTagCompound = is.getTagCompound + if (encodedValue != null) { + val inTag: NBTTagList = encodedValue.getTagList("in", 10) + val outTag: NBTTagList = encodedValue.getTagList("out", 10) + val inputs = Array.tabulate(inTag.tagCount()) { i => + Platform.readStackNBT(inTag.getCompoundTagAt(i)) + } + val outputs = Array.tabulate(outTag.tagCount()) { i => + Platform.readStackNBT(outTag.getCompoundTagAt(i)) + } + output += "inputs" -> inputs + output += "outputs" -> outputs + output += "isCraftable" -> Boolean.box(encodedValue.getBoolean("crafting")) + } + } catch { + case ignored: Throwable => + } + case _ => + } + } +} diff --git a/src/main/scala/li/cil/oc/integration/appeng/DriverBlockInterface.scala b/src/main/scala/li/cil/oc/integration/appeng/DriverBlockInterface.scala index 69f3f50b56..a37785ade4 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/DriverBlockInterface.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/DriverBlockInterface.scala @@ -1,21 +1,15 @@ package li.cil.oc.integration.appeng import appeng.tile.misc.TileInterface -import li.cil.oc.api.driver.EnvironmentProvider -import li.cil.oc.api.driver.NamedBlock -import li.cil.oc.api.internal.Database -import li.cil.oc.api.machine.Arguments -import li.cil.oc.api.machine.Callback -import li.cil.oc.api.machine.Context -import li.cil.oc.api.network.Component +import li.cil.oc.api.driver.{EnvironmentProvider, NamedBlock} +import li.cil.oc.api.machine.{Arguments, Callback, Context} import li.cil.oc.api.network.ManagedEnvironment import li.cil.oc.api.prefab.DriverSidedTileEntity import li.cil.oc.integration.ManagedTileEntityEnvironment -import li.cil.oc.util.DatabaseAccess +import li.cil.oc.integration.appeng.internal.{BlockInterfaceEnvironmentAE2, BlockPatternEnvironment} import li.cil.oc.util.ExtendedArguments._ import li.cil.oc.util.ResultWrapper.result import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound import net.minecraft.world.World import net.minecraftforge.common.util.ForgeDirection @@ -25,65 +19,33 @@ object DriverBlockInterface extends DriverSidedTileEntity { def createEnvironment(world: World, x: Int, y: Int, z: Int, side: ForgeDirection): ManagedEnvironment = new Environment(world.getTileEntity(x, y, z).asInstanceOf[TileInterface]) - final class Environment(val tile: TileInterface) extends ManagedTileEntityEnvironment[TileInterface](tile, "me_interface") with NamedBlock with NetworkControl[TileInterface] { + final class Environment(val tile: TileInterface) extends ManagedTileEntityEnvironment[TileInterface](tile, "me_interface") with NamedBlock with NetworkControl[TileInterface] with BlockInterfaceEnvironmentAE2 with BlockPatternEnvironment { override def preferredName = "me_interface" override def priority = 5 //noinspection ScalaUnusedSymbol @Callback(doc = "function([slot:number]):table -- Get the configuration of the interface.") - def getInterfaceConfiguration(context: Context, args: Arguments): Array[AnyRef] = { - val config = tileEntity.getInventoryByName("config") - val slot = args.optSlot(config, 0, 0) - val stack = config.getStackInSlot(slot) - result(stack) - } + def getInterfaceConfiguration(context: Context, args: Arguments): Array[AnyRef] = getConfig(context, args) @Callback(doc = "function([slot:number][, database:address, entry:number[, size:number]]):boolean -- Configure the interface.") - def setInterfaceConfiguration(context: Context, args: Arguments): Array[AnyRef] = { - val config = tileEntity.getInventoryByName("config") - val slot = if (args.isString(0)) 0 else args.optSlot(config, 0, 0) - config.setInventorySlotContents(slot, getStack(args)) - context.pause(0.5) - result(true) - } - - private def getStack(args: Arguments) = - if (args.count > 1) { - val (address, entry, size) = - if (args.isString(0)) (args.checkString(0), args.checkInteger(1), args.optInteger(2, 1)) - else (args.checkString(1), args.checkInteger(2), args.optInteger(3, 1)) - - node.network.node(address) match { - case component: Component => component.host match { - case database: Database => - val dbStack = database.getStackInSlot(entry - 1) - if (dbStack == null || size < 1) null - else { - dbStack.stackSize = size - dbStack - } - case _ => throw new IllegalArgumentException("not a database") - } - case _ => throw new IllegalArgumentException("no such component") - } - } - else null + def setInterfaceConfiguration(context: Context, args: Arguments): Array[AnyRef] = setConfig(context, args) //noinspection ScalaUnusedSymbol @Callback(doc = "function([slot:number]):table -- Get the given pattern in the interface.") def getInterfacePattern(context: Context, args: Arguments): Array[AnyRef] = { - val inv = tileEntity.getInventoryByName("patterns") + val inv = getPatternInventory(context, args) val slot = args.optSlot(inv, 0, 0) val stack = inv.getStackInSlot(slot) result(stack) } - @Callback(doc = "function(slot:number, database:address, entry:number, size:number, index:number):boolean -- Set the pattern input at the given index.") + // Note: change the index from 4 to 1 + @Callback(doc = "function(slot:number, index:number[, database:address, entry:number, size:number]):boolean OR function(slot:number, index:number[, detail:table, type:string]):boolean -- Set the pattern input at the given index.") def setInterfacePatternInput(context: Context, args: Arguments): Array[AnyRef] = setPatternSlot(context, args, "in") - @Callback(doc = "function(slot:number, database:address, entry:number, size:number, index:number):boolean -- Set the pattern output at the given index.") + @Callback(doc = "function(slot:number, index:number[, database:address, entry:number, size:number]):boolean OR function(slot:number, index:number[, detail:table, type:string]):boolean -- Set the pattern input at the given index.") def setInterfacePatternOutput(context: Context, args: Arguments): Array[AnyRef] = setPatternSlot(context, args, "out") @@ -97,84 +59,11 @@ object DriverBlockInterface extends DriverSidedTileEntity { @Callback(doc = "function(slot:number, index:number):boolean -- Clear pattern input at the given index.") def clearInterfacePatternInput(context: Context, args: Arguments): Array[AnyRef] = - clearInterfacePattern(context, args, "in") + setPatternSlot(context, args, "in") @Callback(doc = "function(slot:number, index:number):boolean -- Clear pattern output at the given index.") def clearInterfacePatternOutput(context: Context, args: Arguments): Array[AnyRef] = - clearInterfacePattern(context, args, "out") - - - private def storeInterfacePattern(context: Context, args: Arguments, tag: String): Array[AnyRef] = { - val inv = tileEntity.getInventoryByName("patterns") - val pattern = inv.getStackInSlot(args.checkSlot(inv, 0)) - val encodedValue = pattern.getTagCompound - if (encodedValue == null) - throw new IllegalArgumentException("No pattern here!") - val nbt = encodedValue.getTagList(tag, 10) - val index = args.checkInteger(1) - 1 - if (index < 0) - throw new IllegalArgumentException("Invalid index!") - val stackNBT = nbt.getCompoundTagAt(index) - val stack = ItemStack.loadItemStackFromNBT(stackNBT) - stack.stackSize = 1; - DatabaseAccess.withDatabase(node, args.checkString(2), database => { - val slot = args.optSlot(database.data, 3, 0) - database.setStackInSlot(slot, stack) - context.pause(0.1) - result(true) - }) - } - - private def clearInterfacePattern(context: Context, args: Arguments, tag: String): Array[AnyRef] = { - val inv = tileEntity.getInventoryByName("patterns") - val slot = args.checkSlot(inv, 0) - val pattern = inv.getStackInSlot(slot) - val encodedValue = pattern.getTagCompound - if (encodedValue == null) - throw new IllegalArgumentException("No pattern here!") - val nbt = encodedValue.getTagList(tag, 10) - val index = args.checkInteger(1) - 1 - if (index < 0) - throw new IllegalArgumentException("Invalid index!") - nbt.removeTag(index) - encodedValue.setTag(tag, nbt) - pattern.setTagCompound(encodedValue) - inv.setInventorySlotContents(slot, null) - inv.setInventorySlotContents(slot, pattern) - context.pause(0.1) - result(true) - } - - private def setPatternSlot(context: Context, args: Arguments, tag: String): Array[AnyRef] = { - val inv = tileEntity.getInventoryByName("patterns") - val slot = if (args.isString(0)) 0 else args.optSlot(inv, 0, 0) - val stack = getStack(args) - val index = args.checkInteger(4) - 1 - if (index < 0 || index > 511) - throw new IllegalArgumentException("Invalid index!") - val pattern = inv.getStackInSlot(slot) - val encodedValue = pattern.getTagCompound - if (encodedValue == null) - throw new IllegalArgumentException("No pattern here!") - val inTag = encodedValue.getTagList(tag, 10) - while (inTag.tagCount() <= index) - inTag.appendTag(new NBTTagCompound()) - if (stack != null) { - val nbt = new NBTTagCompound() - stack.writeToNBT(nbt) - nbt.setInteger("Count", stack.stackSize) - nbt.setLong("Cnt", stack.stackSize) - inTag.func_150304_a(index, nbt) - } - else - inTag.removeTag(index) - encodedValue.setTag(tag, inTag) - pattern.setTagCompound(encodedValue) - inv.setInventorySlotContents(slot, null) - inv.setInventorySlotContents(slot, pattern) - context.pause(0.1) - result(true) - } + setPatternSlot(context, args, "out") } object Provider extends EnvironmentProvider { @@ -183,5 +72,4 @@ object DriverBlockInterface extends DriverSidedTileEntity { classOf[Environment] else null } - } \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/integration/appeng/DriverController.scala b/src/main/scala/li/cil/oc/integration/appeng/DriverController.scala index 6b2bc98556..ea6525c85d 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/DriverController.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/DriverController.scala @@ -2,8 +2,7 @@ package li.cil.oc.integration.appeng import appeng.api.networking.security.IActionHost import appeng.me.helpers.IGridProxyable -import li.cil.oc.api.driver.EnvironmentProvider -import li.cil.oc.api.driver.NamedBlock +import li.cil.oc.api.driver.{EnvironmentProvider, NamedBlock} import li.cil.oc.api.network.ManagedEnvironment import li.cil.oc.api.prefab.DriverSidedTileEntity import li.cil.oc.integration.ManagedTileEntityEnvironment diff --git a/src/main/scala/li/cil/oc/integration/appeng/DriverExportBus.scala b/src/main/scala/li/cil/oc/integration/appeng/DriverExportBus.scala index 289b080e84..21865715dc 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/DriverExportBus.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/DriverExportBus.scala @@ -1,24 +1,19 @@ package li.cil.oc.integration.appeng import appeng.api.AEApi -import appeng.api.config.Actionable -import appeng.api.config.FuzzyMode -import appeng.api.config.Settings -import appeng.api.config.Upgrades +import appeng.api.config.{Actionable, FuzzyMode, Settings, Upgrades} import appeng.api.networking.security.MachineSource import appeng.api.parts.IPartHost +import appeng.api.storage.data.IAEItemStack import appeng.parts.automation.PartExportBus import li.cil.oc.api.driver -import li.cil.oc.api.driver.EnvironmentProvider -import li.cil.oc.api.driver.NamedBlock -import li.cil.oc.api.machine.Arguments -import li.cil.oc.api.machine.Callback -import li.cil.oc.api.machine.Context +import li.cil.oc.api.driver.{EnvironmentProvider, NamedBlock} +import li.cil.oc.api.machine.{Arguments, Callback, Context} import li.cil.oc.integration.ManagedTileEntityEnvironment -import li.cil.oc.util.BlockPosition +import li.cil.oc.integration.appeng.internal.PartSharedItemBusBase import li.cil.oc.util.ExtendedArguments._ -import li.cil.oc.util.InventoryUtils import li.cil.oc.util.ResultWrapper._ +import li.cil.oc.util.{BlockPosition, InventoryUtils} import net.minecraft.item.ItemStack import net.minecraft.world.World import net.minecraftforge.common.util.ForgeDirection @@ -28,80 +23,85 @@ import scala.collection.convert.WrapAsScala._ object DriverExportBus extends driver.SidedBlock { override def worksWith(world: World, x: Int, y: Int, z: Int, side: ForgeDirection) = world.getTileEntity(x, y, z) match { - case container: IPartHost => ForgeDirection.VALID_DIRECTIONS.map(container.getPart).filter(obj => { obj != null }).exists(_.isInstanceOf[PartExportBus]) + case container: IPartHost => ForgeDirection.VALID_DIRECTIONS.map(container.getPart).filter(obj => { + obj != null + }).exists(_.isInstanceOf[PartExportBus]) case _ => false } override def createEnvironment(world: World, x: Int, y: Int, z: Int, side: ForgeDirection) = new Environment(world.getTileEntity(x, y, z).asInstanceOf[IPartHost]) - final class Environment(val host: IPartHost) extends ManagedTileEntityEnvironment[IPartHost](host, "me_exportbus") with NamedBlock with PartEnvironmentBase { + final class Environment(val host: IPartHost) extends ManagedTileEntityEnvironment[IPartHost](host, "me_exportbus") with NamedBlock with PartSharedItemBusBase[PartExportBus] { override def preferredName = "me_exportbus" override def priority = 2 @Callback(doc = "function(side:number, [ slot:number]):boolean -- Get the configuration of the export bus pointing in the specified direction.") - def getExportConfiguration(context: Context, args: Arguments): Array[AnyRef] = getPartConfig[PartExportBus](context, args) + def getExportConfiguration(context: Context, args: Arguments): Array[AnyRef] = result(getPartConfig(context, args)) @Callback(doc = "function(side:number[, slot:number][, database:address, entry:number):boolean -- Configure the export bus pointing in the specified direction to export item stacks matching the specified descriptor.") - def setExportConfiguration(context: Context, args: Arguments): Array[AnyRef] = setPartConfig[PartExportBus](context, args) + def setExportConfiguration(context: Context, args: Arguments): Array[AnyRef] = { + setPartConfigByDatabase(context, args) + result(true) + } + + @Callback(doc = "function(side:number[, slot:number][, detail: table):boolean -- Configure the export bus pointing in the specified direction to export stacks matching the specified descriptor.") + def setExportConfigurationExtended(context: Context, args: Arguments): Array[AnyRef] = { + setPartConfig[IAEItemStack](context, args) + result(true) + } + + @Callback(doc = "function(side:number):number -- Get the number of valid slots in this export bus.") + def getExportSlotSize(context: Context, args: Arguments): Array[AnyRef] = result(getSlotSize(context, args)) @Callback(doc = "function(side:number, slot:number):boolean -- Make the export bus facing the specified direction perform a single export operation into the specified slot.") def exportIntoSlot(context: Context, args: Arguments): Array[AnyRef] = { val side = args.checkSideAny(0) - host.getPart(side) match { - case export: PartExportBus => - InventoryUtils.inventoryAt(BlockPosition(host.getLocation).offset(side)) match { - case Some(inventory) => - val targetSlot = args.checkSlot(inventory, 1) - val config = export.getInventoryByName("config") - val itemStorage = export.getProxy.getStorage.getItemInventory - var count = export.getInstalledUpgrades(Upgrades.SPEED) match { - case 1 => 8 - case 2 => 32 - case 3 => 64 - case 4 => 96 - case _ => 1 - } - // We need reflection here to avoid compiling against the return and - // argument type, which has changed in rv2-beta-20 or so. - val fuzzyMode = (try export.getConfigManager.getClass.getMethod("getSetting", classOf[Enum[_]]) catch { - case _: NoSuchMethodException => export.getConfigManager.getClass.getMethod("getSetting", classOf[Settings]) - }).invoke(export.getConfigManager, Settings.FUZZY_MODE).asInstanceOf[FuzzyMode] - val source = new MachineSource(export) - var didSomething = false - for (slot <- 0 until config.getSizeInventory if count > 0) { - val filter = AEApi.instance.storage.createItemStack(config.getStackInSlot(slot)) - val stacks = - if (export.getInstalledUpgrades(Upgrades.FUZZY) > 0) - itemStorage.getStorageList.findFuzzy(filter, fuzzyMode).toSeq - else - Seq(itemStorage.getStorageList.findPrecise(filter)) - for (ais <- stacks.filter(_ != null).map(_.copy()) if count > 0) { - val is = ais.getItemStack - is.stackSize = count - if (InventoryUtils.insertIntoInventorySlot(is, inventory, Option(side.getOpposite), targetSlot, count, simulate = true)) { - ais.setStackSize(count - is.stackSize) - val eais = AEApi.instance.storage.poweredExtraction(export.getProxy.getEnergy, itemStorage, ais, source) - if (eais != null) { - val eis = eais.getItemStack - count -= eis.stackSize - didSomething = true - InventoryUtils.insertIntoInventorySlot(eis, inventory, Option(side.getOpposite), targetSlot) - if (eis.stackSize > 0) { - eais.setStackSize(eis.stackSize) - itemStorage.injectItems(ais, Actionable.MODULATE, source) - } - } + val export = getPart(side) + InventoryUtils.inventoryAt(BlockPosition(host.getLocation).offset(side)) match { + case Some(inventory) => + val targetSlot = args.checkSlot(inventory, 1) + val config = export.getInventoryByName("config") + val itemStorage = export.getProxy.getStorage.getItemInventory + var count = export.calculateAmountToSend() + // We need reflection here to avoid compiling against the return and + // argument type, which has changed in rv2-beta-20 or so. + val fuzzyMode = (try export.getConfigManager.getClass.getMethod("getSetting", classOf[Enum[_]]) catch { + case _: NoSuchMethodException => export.getConfigManager.getClass.getMethod("getSetting", classOf[Settings]) + }).invoke(export.getConfigManager, Settings.FUZZY_MODE).asInstanceOf[FuzzyMode] + val source = new MachineSource(export) + var didSomething = false + for (slot <- 0 until config.getSizeInventory if count > 0) { + val filter = AEApi.instance.storage.createItemStack(config.getStackInSlot(slot)) + val stacks = + if (export.getInstalledUpgrades(Upgrades.FUZZY) > 0) + itemStorage.getStorageList.findFuzzy(filter, fuzzyMode).toSeq + else + Seq(itemStorage.getStorageList.findPrecise(filter)) + for (ais <- stacks.filter(_ != null).map(_.copy()) if count > 0) { + val is = ais.getItemStack + is.stackSize = count + if (InventoryUtils.insertIntoInventorySlot(is, inventory, Option(side.getOpposite), targetSlot, count, simulate = true)) { + ais.setStackSize(count - is.stackSize) + val eais = AEApi.instance.storage.poweredExtraction(export.getProxy.getEnergy, itemStorage, ais, source) + if (eais != null) { + val eis = eais.getItemStack + count -= eis.stackSize + didSomething = true + InventoryUtils.insertIntoInventorySlot(eis, inventory, Option(side.getOpposite), targetSlot) + if (eis.stackSize > 0) { + eais.setStackSize(eis.stackSize) + itemStorage.injectItems(ais, Actionable.MODULATE, source) } } } - if (didSomething) { - context.pause(0.25) - } - result(didSomething) - case _ => result(Unit, "no inventory") + } + } + if (didSomething) { + context.pause(0.25) } - case _ => result(Unit, "no export bus") + result(didSomething) + case _ => result(Unit, "no inventory") } } } diff --git a/src/main/scala/li/cil/oc/integration/appeng/DriverImportBus.scala b/src/main/scala/li/cil/oc/integration/appeng/DriverImportBus.scala index 0a91850019..2e8c2d9f70 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/DriverImportBus.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/DriverImportBus.scala @@ -1,14 +1,14 @@ package li.cil.oc.integration.appeng import appeng.api.parts.IPartHost +import appeng.api.storage.data.IAEItemStack import appeng.parts.automation.PartImportBus import li.cil.oc.api.driver -import li.cil.oc.api.driver.EnvironmentProvider -import li.cil.oc.api.driver.NamedBlock -import li.cil.oc.api.machine.Arguments -import li.cil.oc.api.machine.Callback -import li.cil.oc.api.machine.Context +import li.cil.oc.api.driver.{EnvironmentProvider, NamedBlock} +import li.cil.oc.api.machine.{Arguments, Callback, Context} import li.cil.oc.integration.ManagedTileEntityEnvironment +import li.cil.oc.integration.appeng.internal.PartSharedItemBusBase +import li.cil.oc.util.ResultWrapper.result import net.minecraft.item.ItemStack import net.minecraft.world.World import net.minecraftforge.common.util.ForgeDirection @@ -16,22 +16,36 @@ import net.minecraftforge.common.util.ForgeDirection object DriverImportBus extends driver.SidedBlock { override def worksWith(world: World, x: Int, y: Int, z: Int, side: ForgeDirection) = world.getTileEntity(x, y, z) match { - case container: IPartHost => ForgeDirection.VALID_DIRECTIONS.map(container.getPart).filter(obj => { obj != null }).exists(_.isInstanceOf[PartImportBus]) + case container: IPartHost => ForgeDirection.VALID_DIRECTIONS.map(container.getPart).filter(obj => { + obj != null + }).exists(_.isInstanceOf[PartImportBus]) case _ => false } override def createEnvironment(world: World, x: Int, y: Int, z: Int, side: ForgeDirection) = new Environment(world.getTileEntity(x, y, z).asInstanceOf[IPartHost]) - final class Environment(val host: IPartHost) extends ManagedTileEntityEnvironment[IPartHost](host, "me_importbus") with NamedBlock with PartEnvironmentBase { + final class Environment(val host: IPartHost) extends ManagedTileEntityEnvironment[IPartHost](host, "me_importbus") with NamedBlock with PartSharedItemBusBase[PartImportBus] { override def preferredName = "me_importbus" override def priority = 1 @Callback(doc = "function(side:number[, slot:number]):boolean -- Get the configuration of the import bus pointing in the specified direction.") - def getImportConfiguration(context: Context, args: Arguments): Array[AnyRef] = getPartConfig[PartImportBus](context, args) + def getImportConfiguration(context: Context, args: Arguments): Array[AnyRef] = result(getPartConfig(context, args)) @Callback(doc = "function(side:number[, slot:number][, database:address, entry:number]):boolean -- Configure the import bus pointing in the specified direction to import item stacks matching the specified descriptor.") - def setImportConfiguration(context: Context, args: Arguments): Array[AnyRef] = setPartConfig[PartImportBus](context, args) + def setImportConfiguration(context: Context, args: Arguments): Array[AnyRef] = { + setPartConfigByDatabase(context, args) + result(true) + } + + @Callback(doc = "function(side:number[, slot:number][, detail:table]):boolean -- Configure the import bus pointing in the specified direction to import stacks matching the specified descriptor.") + def setImportConfigurationExtended(context: Context, args: Arguments): Array[AnyRef] = { + setPartConfig[IAEItemStack](context, args) + result(true) + } + + @Callback(doc = "function(side:number):number -- Get the number of valid slots in this import bus.") + def getImportSlotSize(context: Context, args: Arguments): Array[AnyRef] = result(getSlotSize(context, args)) } object Provider extends EnvironmentProvider { diff --git a/src/main/scala/li/cil/oc/integration/appeng/DriverPartInterface.scala b/src/main/scala/li/cil/oc/integration/appeng/DriverPartInterface.scala index d997c3a9c0..3c65f94d96 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/DriverPartInterface.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/DriverPartInterface.scala @@ -3,12 +3,12 @@ package li.cil.oc.integration.appeng import appeng.api.parts.IPartHost import appeng.parts.misc.PartInterface import li.cil.oc.api.driver -import li.cil.oc.api.driver.EnvironmentProvider -import li.cil.oc.api.driver.NamedBlock -import li.cil.oc.api.machine.Arguments -import li.cil.oc.api.machine.Callback -import li.cil.oc.api.machine.Context +import li.cil.oc.api.driver.{EnvironmentProvider, NamedBlock} +import li.cil.oc.api.machine.{Arguments, Callback, Context} import li.cil.oc.integration.ManagedTileEntityEnvironment +import li.cil.oc.integration.appeng.internal.{PartInterfaceEnvironmentAE2, PartPatternEnvironment} +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 @@ -16,22 +16,49 @@ import net.minecraftforge.common.util.ForgeDirection object DriverPartInterface extends driver.SidedBlock { override def worksWith(world: World, x: Int, y: Int, z: Int, side: ForgeDirection) = world.getTileEntity(x, y, z) match { - case container: IPartHost => ForgeDirection.VALID_DIRECTIONS.map(container.getPart).filter(obj => { obj != null }).exists(_.isInstanceOf[PartInterface]) + case container: IPartHost => ForgeDirection.VALID_DIRECTIONS.map(container.getPart).filter(obj => { + obj != null + }).exists(_.isInstanceOf[PartInterface]) case _ => false } override def createEnvironment(world: World, x: Int, y: Int, z: Int, side: ForgeDirection) = new Environment(world.getTileEntity(x, y, z).asInstanceOf[IPartHost]) - final class Environment(val host: IPartHost) extends ManagedTileEntityEnvironment[IPartHost](host, "me_interface") with NamedBlock with PartEnvironmentBase { + final class Environment(val host: IPartHost) extends ManagedTileEntityEnvironment[IPartHost](host, "me_interface") with NamedBlock with PartInterfaceEnvironmentAE2 with PartPatternEnvironment[PartInterface] { override def preferredName = "me_interface" override def priority = 0 @Callback(doc = "function(side:number[, slot:number]):table -- Get the configuration of the interface pointing in the specified direction.") - def getInterfaceConfiguration(context: Context, args: Arguments): Array[AnyRef] = getPartConfig[PartInterface](context, args) + def getInterfaceConfiguration(context: Context, args: Arguments): Array[AnyRef] = getConfig(context, args) @Callback(doc = "function(side:number[, slot:number][, database:address, entry:number[, size:number]]):boolean -- Configure the interface pointing in the specified direction.") - def setInterfaceConfiguration(context: Context, args: Arguments): Array[AnyRef] = setPartConfig[PartInterface](context, args) + def setInterfaceConfiguration(context: Context, args: Arguments): Array[AnyRef] = setConfig(context, args) + + @Callback(doc = "function([slot:number]):table -- Get the given pattern in the interface.") + def getInterfacePattern(context: Context, args: Arguments): Array[AnyRef] = { + val inv = getPatternInventory(context, args) + val slot = args.optSlot(inv, 0, 0) + val stack = inv.getStackInSlot(slot) + result(stack) + } + + // Note: change the index from 4 to 1 + @Callback(doc = "function(slot:number, index:number[, database:address, entry:number, size:number]):boolean OR function(slot:number, index:number[, detail:table, type:string]):boolean -- Set the pattern input at the given index.") + def setInterfacePatternInput(context: Context, args: Arguments): Array[AnyRef] = + setPatternSlot(context, args, "in") + + @Callback(doc = "function(slot:number, index:number[, database:address, entry:number, size:number]):boolean OR function(slot:number, index:number[, detail:table, type:string]):boolean -- Set the pattern input at the given index.") + def setInterfacePatternOutput(context: Context, args: Arguments): Array[AnyRef] = + setPatternSlot(context, args, "out") + + @Callback(doc = "function(slot:number, index:number, database:address, entry:number):boolean -- Store pattern input at the given index to the database entry.") + def storeInterfacePatternInput(context: Context, args: Arguments): Array[AnyRef] = + storeInterfacePattern(context, args, "in") + + @Callback(doc = "function(slot:number, index:number, database:address, entry:number):boolean -- Store pattern output at the given index to the database entry.") + def storeInterfacePatternOutput(context: Context, args: Arguments): Array[AnyRef] = + storeInterfacePattern(context, args, "out") } object Provider extends EnvironmentProvider { diff --git a/src/main/scala/li/cil/oc/integration/appeng/DriverUpgradeAE.scala b/src/main/scala/li/cil/oc/integration/appeng/DriverUpgradeAE.scala index 1c1afe490f..ea336f1c53 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/DriverUpgradeAE.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/DriverUpgradeAE.scala @@ -1,14 +1,12 @@ package li.cil.oc.integration.appeng -import li.cil.oc.api -import li.cil.oc.Constants import li.cil.oc.api.driver.EnvironmentProvider import li.cil.oc.api.driver.item.HostAware import li.cil.oc.api.network.EnvironmentHost -import li.cil.oc.common.Slot -import li.cil.oc.common.Tier import li.cil.oc.common.item.Delegator +import li.cil.oc.common.{Slot, Tier} import li.cil.oc.integration.opencomputers.Item +import li.cil.oc.{Constants, api} import net.minecraft.item.ItemStack object DriverUpgradeAE extends Item with HostAware { 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 78210c217c..8e5201df95 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/ModAppEng.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/ModAppEng.scala @@ -1,19 +1,23 @@ package li.cil.oc.integration.appeng import appeng.api.AEApi +import appeng.api.storage.data.{IAEFluidStack, IAEItemStack} +import appeng.util.item.{AEFluidStackType, AEItemStackType} import cpw.mods.fml.common.registry.GameRegistry -import li.cil.oc.{Constants, api} import li.cil.oc.api.Driver import li.cil.oc.common.Tier import li.cil.oc.common.recipe.Recipes.addSubItem import li.cil.oc.common.tileentity.Print -import li.cil.oc.integration.ModProxy -import li.cil.oc.integration.Mods +import li.cil.oc.integration.{ModProxy, Mods} +import li.cil.oc.{Constants, api} object ModAppEng extends ModProxy { override def getMod = Mods.AppliedEnergistics2 override def initialize() { + AEStackFactory.register[IAEFluidStack](AEFluidStackType.FLUID_STACK_TYPE, ConverterAEFluidStack.convert, ConverterAEFluidStack.parse) + AEStackFactory.register[IAEItemStack](AEItemStackType.ITEM_STACK_TYPE, ConverterAEItemStack.convert, ConverterAEItemStack.parse) + api.IMC.registerWrenchTool("li.cil.oc.integration.appeng.EventHandlerAE2.useWrench") api.IMC.registerWrenchToolCheck("li.cil.oc.integration.appeng.EventHandlerAE2.isWrench") @@ -26,8 +30,10 @@ object ModAppEng extends ModProxy { Driver.add(DriverBlockInterface) Driver.add(DriverUpgradeAE) + Driver.add(ConverterAEItemStack) + Driver.add(ConverterAEFluidStack) Driver.add(new ConverterCellInventory) - Driver.add(new ConverterPattern) + Driver.add(ConverterPattern) Driver.add(DriverController.Provider) Driver.add(DriverExportBus.Provider) diff --git a/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala b/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala index f94e3dce5f..67f0db5baf 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala @@ -13,8 +13,8 @@ import appeng.me.GridAccessException import appeng.me.cluster.implementations.CraftingCPUCluster import appeng.me.helpers.IGridProxyable import appeng.tile.crafting.TileCraftingMonitorTile -import appeng.util.{IterationCounter, Platform} import appeng.util.item.{AEFluidStack, AEItemStack} +import appeng.util.{IterationCounter, Platform} import com.google.common.collect.ImmutableSet import li.cil.oc.OpenComputers import li.cil.oc.api.machine.{Arguments, Callback, Context} @@ -47,10 +47,15 @@ import scala.collection.mutable.ArrayBuffer import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future import scala.language.existentials +import scala.reflect.ClassTag //noinspection ScalaUnusedSymbol // Note to self: this class is used by ExtraCells (and potentially others), do not rename / drastically change it. -trait NetworkControl[AETile >: Null <: TileEntity with IGridProxyable with IActionHost] { +trait AETypes { + type AEStack = IAEStack[T] forSome {type T <: IAEStack[T]} +} + +trait NetworkControl[AETile >: Null <: TileEntity with IGridProxyable with IActionHost] extends AETypes { def tile: AETile def node: Node @@ -87,9 +92,12 @@ trait NetworkControl[AETile >: Null <: TileEntity with IGridProxyable with IActi } val builder = mutable.ArrayBuilder.make[AnyRef] - (allItems.view ++ allFluids.view).foreach({ s => + val allStacks: Iterable[IAEStack[_]] = AEStackFactory.getRegisteredTypes.flatMap { tp => + tile.getProxy.getStorage.getMEMonitor(tp).getStorageList.asInstanceOf[IItemList[AEStack]] + } + allStacks.foreach({ s => if (s.isCraftable) { - val c = asCraft(s, tile).asInstanceOf[IAEStack[_ <: IAEStack[_]]] + val c = asCraft(s, tile).asInstanceOf[AEStack] if (matches(convert(c, tile), filter)) { builder += new Craftable(tile, c) } @@ -176,12 +184,12 @@ trait NetworkControl[AETile >: Null <: TileEntity with IGridProxyable with IActi } DatabaseAccess.withDatabase(node, args.checkString(1), database => { val items = allItems - .collect { case aeItem if matches(convert(aeItem, tile), filter) => aePotentialItem(aeItem, tile) }.toArray + .collect { case aeItem if matches(convert(aeItem, tile), filter) => aePotential(aeItem, tile) }.toArray val offset = args.optSlot(database.data, 2, 0) val count = args.optInteger(3, Int.MaxValue) min (database.size - offset) min items.length var slot = offset for (i <- 0 until count) { - val stack = Option(items(i)).map(_.getItemStack.copy()).orNull + val stack = Option(items(i)).map(_.asInstanceOf[IAEItemStack].getItemStack.copy()).orNull while (database.getStackInSlot(slot) != null && slot < database.size) slot += 1 if (database.getStackInSlot(slot) == null) { database.setStackInSlot(slot, stack) @@ -237,10 +245,10 @@ trait NetworkControl[AETile >: Null <: TileEntity with IGridProxyable with IActi } } -object NetworkControl { +object NetworkControl extends AETypes { //noinspection ScalaUnusedSymbol - class Craftable(var controller: TileEntity with IGridProxyable with IActionHost, var stack: IAEStack[_ <: IAEStack[_]]) extends AbstractValue with ICraftingRequester { + class Craftable(var controller: TileEntity with IGridProxyable with IActionHost, var stack: AEStack) extends AbstractValue with ICraftingRequester { def this() = this(null, null) private val links = mutable.Set.empty[ICraftingLink] @@ -267,10 +275,7 @@ object NetworkControl { // ----------------------------------------------------------------------- // @Callback(doc = "function():table -- Returns the item stack representation of the crafting result.") - def getStack(context: Context, args: Arguments): Array[AnyRef] = stack match { - case item: IAEItemStack => result(item.getItemStack) - case fluid: IAEFluidStack => result(fluid.getFluidStack) - } + def getStack(context: Context, args: Arguments): Array[AnyRef] = result(stack) @Callback(doc = "function([amount:int[, prioritizePower:boolean[, cpuName:string]]]):userdata -- Requests the item to be crafted, returning an object that allows tracking the crafting status.") def request(context: Context, args: Arguments): Array[AnyRef] = { @@ -279,7 +284,7 @@ object NetworkControl { } val count = args.optInteger(0, 1) - val request = stack.copy.asInstanceOf[IAEStack[_]] + val request = stack.copy request.setStackSize(count) val craftingGrid = controller.getProxy.getCrafting @@ -332,7 +337,7 @@ object NetworkControl { override def load(nbt: NBTTagCompound) { super.load(nbt) if (nbt.hasKey("StackType")) { - stack = Platform.readStackNBT(nbt, true).asInstanceOf[IAEStack[_ <: IAEStack[_]]] + stack = Platform.readStackNBT(nbt, true).asInstanceOf[AEStack] } else { stack = AEItemStack.loadItemStackFromNBT(nbt) @@ -346,7 +351,9 @@ object NetworkControl { super.save(nbt) Platform.writeStackNBT(stack, nbt, true) saveController(controller, nbt) - nbt.setNewTagList("links", links.map(_.writeToNBT _)) + val tag = new NBTTagCompound() + links.foreach(_.writeToNBT(tag)) + nbt.setNewTagList("links", tag) } } @@ -405,7 +412,7 @@ object NetworkControl { if (aeStack == null) result(null, "Nothing is crafted") else - result(aeStack.getItemStack) + result(aeStack) } } @@ -493,10 +500,22 @@ object NetworkControl { } //noinspection ConvertNullInitializerToUnderscore - abstract class NetworkContents[T <: IAEStack[T]](var controller: TileEntity with IGridProxyable with IActionHost, var node: Node, var subscribe: Boolean) extends AbstractValue with IMEMonitorHandlerReceiver[T] { + abstract class NetworkContents[T <: IAEStack[T] : ClassTag](var controller: TileEntity with IGridProxyable with IActionHost, var node: Node, var subscribe: Boolean) extends AbstractValue with IMEMonitorHandlerReceiver[T] { def this() = this(null, null, false) - def getInventory: IMEMonitor[T] + def getInventory: IMEMonitor[T] = { + try { + val tp = AEStackFactory.getEntry[T]() + tp match { + case Some(t) => + controller.getProxy.getStorage.getMEMonitor(t.stackType).asInstanceOf[IMEMonitor[T]] + case None => null + } + } + catch { + case _: GridAccessException => null + } + } def withInventory[R](action: IMEMonitor[T] => R): Option[R] = { for { @@ -505,7 +524,9 @@ object NetworkControl { } yield action(inv) } - def convertItem(stack: IAEStack[_]): java.util.HashMap[String, AnyRef] + def convertItem(stack: IAEStack[_]): java.util.HashMap[String, AnyRef] = { + convert(stack.asInstanceOf[AEStack], controller) + } val event_name: String = "network_changed" @@ -568,51 +589,47 @@ object NetworkControl { } } - class ItemNetworkContents(controller: TileEntity with IGridProxyable with IActionHost, node: Node, subscribe: Boolean) extends NetworkContents[IAEItemStack](controller, node, subscribe) { + private class ItemNetworkContents(controller: TileEntity with IGridProxyable with IActionHost, node: Node, subscribe: Boolean) extends NetworkContents[IAEItemStack](controller, node, subscribe) { def this() = this(null, null, false) override val event_name: String = "network_item_changed" - def getInventory: IMEMonitor[IAEItemStack] = { - try return controller.getProxy.getStorage.getItemInventory + override def getInventory: IMEMonitor[IAEItemStack] = { + try + controller.getProxy.getStorage.getItemInventory catch { - case ignored: GridAccessException => + case _: GridAccessException => null } - return null } - - override def convertItem(stack: IAEStack[_]): java.util.HashMap[String, AnyRef] = convert(stack.asInstanceOf[IAEItemStack], controller) } - class FluidNetworkContents(controller: TileEntity with IGridProxyable with IActionHost, node: Node, subscribe: Boolean) extends NetworkContents[IAEFluidStack](controller, node, subscribe) { + private class FluidNetworkContents(controller: TileEntity with IGridProxyable with IActionHost, node: Node, subscribe: Boolean) extends NetworkContents[IAEFluidStack](controller, node, subscribe) { def this() = this(null, null, false) override val event_name: String = "network_fluid_changed" override def getInventory: IMEMonitor[IAEFluidStack] = { - try return controller.getProxy.getStorage.getFluidInventory + try + controller.getProxy.getStorage.getFluidInventory catch { - case ignored: GridAccessException => + case _: GridAccessException => null } - return null } - - override def convertItem(stack: IAEStack[_]): java.util.HashMap[String, AnyRef] = convert(stack.asInstanceOf[IAEFluidStack], controller) } def asCraft(stack: IAEStack[_], tile: TileEntity with IGridProxyable): IAEStack[_] = { tile.getProxy.getCrafting.getCraftingFor(stack, null, 0, tile.getWorldObj).view.map(a => a.getAEOutputs.find(_.isSameType(stack)).get).headOption.getOrElse[IAEStack[_]] { - val result = stack.copy().asInstanceOf[IAEStack[_ <: IAEStack[_]]] + val result = stack.copy().asInstanceOf[AEStack] result.setStackSize(0) result } } - def aePotentialItem(aeItem: IAEItemStack, tile: TileEntity with IGridProxyable): IAEItemStack = { + def aePotential(aeItem: AEStack, tile: TileEntity with IGridProxyable): AEStack = { if (aeItem.getStackSize > 0 || !aeItem.isCraftable) aeItem else - asCraft(aeItem, tile).asInstanceOf[IAEItemStack] + asCraft(aeItem, tile).asInstanceOf[AEStack] } def hashConvert(value: java.util.HashMap[_, _]) = { @@ -621,16 +638,9 @@ object NetworkControl { hash } - def convert(aeItem: IAEStack[_ <: IAEStack[_]], tile: TileEntity with IGridProxyable): java.util.HashMap[String, AnyRef] = { - aeItem match { - case item: IAEItemStack => convert(item, tile) - case fluid: IAEFluidStack => convert(fluid, tile) - } - } - - def convert(aeItem: IAEItemStack, tile: TileEntity with IGridProxyable): java.util.HashMap[String, AnyRef] = { - val potentialItem = aePotentialItem(aeItem, tile) - val result = Registry.convert(Array[AnyRef](potentialItem.getItemStack)) + def convert(aeItem: AEStack, tile: TileEntity with IGridProxyable): java.util.HashMap[String, AnyRef] = { + val potentialItem = aePotential(aeItem, tile) + val result = Registry.convert(Array[AnyRef](potentialItem)) .collect { case hash: java.util.HashMap[_, _] => hashConvert(hash) } if (result.length > 0) { val hash = result(0) @@ -643,18 +653,6 @@ object NetworkControl { null } - def convert(aeItem: IAEFluidStack, tile: TileEntity with IGridProxyable): java.util.HashMap[String, AnyRef] = { - val result = Registry.convert(Array[AnyRef](aeItem.getFluidStack)) - .collect { case hash: java.util.HashMap[_, _] => hashConvert(hash) } - if (result.length > 0) { - val hash = result(0) - hash.update("amount", Long.box(aeItem.getStackSize)) - hash.update("isCraftable", Boolean.box(aeItem.isCraftable)) - return hash - } - null - } - private def loadController(nbt: NBTTagCompound, f: TileEntity with IGridProxyable with IActionHost => Unit): Unit = { if (nbt.hasKey("dimension")) { val dimension = nbt.getInteger("dimension") diff --git a/src/main/scala/li/cil/oc/integration/appeng/PartEnvironmentBase.scala b/src/main/scala/li/cil/oc/integration/appeng/PartEnvironmentBase.scala deleted file mode 100644 index 06e1841508..0000000000 --- a/src/main/scala/li/cil/oc/integration/appeng/PartEnvironmentBase.scala +++ /dev/null @@ -1,65 +0,0 @@ -package li.cil.oc.integration.appeng - -import appeng.api.implementations.tiles.ISegmentedInventory -import appeng.api.parts.IPartHost -import li.cil.oc.api.internal.Database -import li.cil.oc.api.machine.Arguments -import li.cil.oc.api.machine.Context -import li.cil.oc.api.network.Component -import li.cil.oc.api.network.ManagedEnvironment -import li.cil.oc.util.ExtendedArguments._ -import li.cil.oc.util.ResultWrapper.result -import net.minecraftforge.common.util.ForgeDirection - -import scala.reflect.ClassTag - -trait PartEnvironmentBase extends ManagedEnvironment { - def host: IPartHost - - // function(side:number[, slot:number]):table - def getPartConfig[PartType <: ISegmentedInventory : ClassTag](context: Context, args: Arguments): Array[AnyRef] = { - val side = args.checkSideAny(0) - host.getPart(side) match { - case part: PartType => - val config = part.getInventoryByName("config") - val slot = args.optSlot(config, 1, 0) - val stack = config.getStackInSlot(slot) - result(stack) - case _ => result(Unit, "no matching part") - } - } - - // function(side:number[, slot:number][, database:address, entry:number[, size:number]]):boolean - def setPartConfig[PartType <: ISegmentedInventory : ClassTag](context: Context, args: Arguments): Array[AnyRef] = { - val side = args.checkSideAny(0) - host.getPart(side) match { - case part: PartType => - val config = part.getInventoryByName("config") - val slot = if (args.isString(1)) 0 else args.optSlot(config, 1, 0) - val stack = if (args.count > 2) { - val (address, entry, size) = - if (args.isString(1)) (args.checkString(1), args.checkInteger(2), args.optInteger(3, 1)) - else (args.checkString(2), args.checkInteger(3), args.optInteger(4, 1)) - - node.network.node(address) match { - case component: Component => component.host match { - case database: Database => - val dbStack = database.getStackInSlot(entry - 1) - if (dbStack == null || size < 1) null - else { - dbStack.stackSize = math.min(size, dbStack.getMaxStackSize) - dbStack - } - case _ => throw new IllegalArgumentException("not a database") - } - case _ => throw new IllegalArgumentException("no such component") - } - } - else null - config.setInventorySlotContents(slot, stack) - context.pause(0.5) - result(true) - case _ => result(Unit, "no matching part") - } - } -} diff --git a/src/main/scala/li/cil/oc/integration/appeng/UpgradeAE.scala b/src/main/scala/li/cil/oc/integration/appeng/UpgradeAE.scala index 1c639ebdf0..e3ca377f87 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/UpgradeAE.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/UpgradeAE.scala @@ -1,6 +1,5 @@ package li.cil.oc.integration.appeng -import java.util import appeng.api.AEApi import appeng.api.config.Actionable import appeng.api.implementations.tiles.IWirelessAccessPoint @@ -15,7 +14,6 @@ import li.cil.oc.Constants import li.cil.oc.api.Network import li.cil.oc.api.driver.DeviceInfo import li.cil.oc.api.driver.DeviceInfo.{DeviceAttribute, DeviceClass} -import li.cil.oc.api.network.EnvironmentHost import li.cil.oc.api.internal.{Agent, Database, Drone, Robot} import li.cil.oc.api.machine.{Arguments, Callback, Context} import li.cil.oc.api.network._ @@ -27,6 +25,7 @@ import net.minecraft.item.ItemStack import net.minecraftforge.common.util.ForgeDirection import net.minecraftforge.fluids.FluidContainerRegistry +import java.util import scala.collection.JavaConversions._ class UpgradeAE(val host: EnvironmentHost, val tier: Int) extends ManagedEnvironment with appeng.NetworkControl[TileSecurity] with DeviceInfo { @@ -49,16 +48,16 @@ class UpgradeAE(val host: EnvironmentHost, val tier: Int) extends ManagedEnviron val agent: Agent = host.asInstanceOf[Agent] def getComponent: ItemStack = host match { - case robot : Robot => robot.getStackInSlot(robot.componentSlot(node.address)) - case drone: Drone => - for (i <- drone.internalComponents) - Delegator.subItem(i) match { - case Some(_: ItemUpgradeAE) => return i - case _ => - } - null - case _ => null - } + case robot: Robot => robot.getStackInSlot(robot.componentSlot(node.address)) + case drone: Drone => + for (i <- drone.internalComponents) + Delegator.subItem(i) match { + case Some(_: ItemUpgradeAE) => return i + case _ => + } + null + case _ => null + } def getSecurity: IGridHost = { @@ -72,6 +71,7 @@ class UpgradeAE(val host: EnvironmentHost, val tier: Int) extends ManagedEnviron else null } + def checkRange(stack: ItemStack, sec: IGridHost): Boolean = { if (sec == null) return false val gridNode: IGridNode = sec.getGridNode(ForgeDirection.UNKNOWN) @@ -365,9 +365,9 @@ class UpgradeAE(val host: EnvironmentHost, val tier: Int) extends ManagedEnviron .0 else c.getItemDamage match { - case 0 => .6 - case 1 => .3 - case _ => .05 + case 0 =>.6 + case 1 =>.3 + case _ =>.05 } } diff --git a/src/main/scala/li/cil/oc/integration/appeng/WirelessHandlerUpgradeAE.scala b/src/main/scala/li/cil/oc/integration/appeng/WirelessHandlerUpgradeAE.scala index 9e2171bd6d..16ff8299ff 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/WirelessHandlerUpgradeAE.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/WirelessHandlerUpgradeAE.scala @@ -76,9 +76,10 @@ class WirelessHandlerUpgradeAE extends IWirelessTermHandler { override def hasInfinityRange(itemStack: ItemStack): Boolean = false } -object WirelessHandlerUpgradeAE -{ + +object WirelessHandlerUpgradeAE { lazy val instance = new WirelessHandlerUpgradeAE + def register() = { AEApi.instance.registries.wireless.registerWirelessHandler(instance) } diff --git a/src/main/scala/li/cil/oc/integration/appeng/internal/InterfaceEnvironmentBase.scala b/src/main/scala/li/cil/oc/integration/appeng/internal/InterfaceEnvironmentBase.scala new file mode 100644 index 0000000000..0ad1f5226a --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/appeng/internal/InterfaceEnvironmentBase.scala @@ -0,0 +1,106 @@ +package li.cil.oc.integration.appeng.internal + +import appeng.api.implementations.tiles.ISegmentedInventory +import appeng.api.parts.{IPart, IPartHost} +import li.cil.oc.api.machine.{Arguments, Context} +import li.cil.oc.api.network.{ManagedEnvironment, Node} +import li.cil.oc.integration.vanilla.ConverterItemStack +import li.cil.oc.util.DatabaseAccess +import li.cil.oc.util.ExtendedArguments.extendedArguments +import li.cil.oc.util.ResultWrapper.result +import net.minecraft.inventory.IInventory +import net.minecraft.item.ItemStack + +trait InterfaceEnvironmentBase[V] extends ManagedEnvironment { + def getConfig(context: Context, args: Arguments): Array[AnyRef] + + def setConfig(context: Context, args: Arguments): Array[AnyRef] + + protected def getConfigValue(args: Arguments, offset: Int): V +} + +trait PartInterfaceEnvironmentBase[V] extends InterfaceEnvironmentBase[V] { + def host: IPartHost + + override def getConfig(context: Context, args: Arguments): Array[AnyRef] = { + val side = args.checkSideAny(0) + val slot = args.optInteger(1, 0) + result(readFromPart(host.getPart(side), slot)) + } + + protected def readFromPart(part: IPart, slot: Int): AnyRef + + override def setConfig(context: Context, args: Arguments): Array[AnyRef] = { + val side = args.checkSideAny(0) + val (slot, offset) = if (args.isInteger(1)) (args.checkInteger(1), 2) else (0, 1) + val stack = getConfigValue(args, offset) + result(setToPart(host.getPart(side), slot, stack)) + } + + protected def setToPart(part: IPart, slot: Int, stack: V): AnyRef +} + +trait BlockInterfaceEnvironmentBase[V] extends InterfaceEnvironmentBase[V] { + def tile: AnyRef + + override def getConfig(context: Context, args: Arguments): Array[AnyRef] = { + val slot = args.optInteger(0, 0) + result(readFromTile(slot)) + } + + protected def readFromTile(slot: Int): AnyRef + + override def setConfig(context: Context, args: Arguments): Array[AnyRef] = { + val (slot, offset) = if (args.isInteger(0)) (args.checkInteger(0), 1) else (0, 0) + val stack = getConfigValue(args, offset) + result(setToTile(slot, stack)) + } + + protected def setToTile(slot: Int, stack: V): AnyRef +} + +trait AE2InterfaceUtils { + protected def getConfigInventory(inv: ISegmentedInventory): IInventory = + inv.getInventoryByName("config") + + protected def getConfigValue(node: Node, args: Arguments, offset: Int): ItemStack = + if (args.isTable(offset)) + ConverterItemStack.parse(args.checkTable(offset)) + else + DatabaseAccess.getStackFromDatabase(node, args, offset) +} + +trait PartInterfaceEnvironmentAE2 extends PartInterfaceEnvironmentBase[ItemStack] with AE2InterfaceUtils { + private def getConfigInventory(part: IPart): IInventory = part match { + case inv: ISegmentedInventory => getConfigInventory(inv: ISegmentedInventory) + case _ => throw new IllegalArgumentException("no matching part") + } + + override def readFromPart(part: IPart, slot: Int): AnyRef = { + getConfigInventory(part).getStackInSlot(slot) + } + + override def getConfigValue(args: Arguments, offset: Int): ItemStack = getConfigValue(node, args, offset) + + override def setToPart(part: IPart, slot: Int, stack: ItemStack): AnyRef = { + getConfigInventory(part).setInventorySlotContents(slot, stack) + result(true) + } +} + +trait BlockInterfaceEnvironmentAE2 extends BlockInterfaceEnvironmentBase[ItemStack] with AE2InterfaceUtils { + override def tile: ISegmentedInventory + + private def getConfigInventory: IInventory = getConfigInventory(tile) + + override def readFromTile(slot: Int): AnyRef = { + getConfigInventory.getStackInSlot(slot) + } + + override def getConfigValue(args: Arguments, offset: Int): ItemStack = getConfigValue(node, args, offset) + + override def setToTile(slot: Int, stack: ItemStack): AnyRef = { + getConfigInventory.setInventorySlotContents(slot, stack) + result(true) + } +} \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/integration/appeng/internal/PartEnvironmentBase.scala b/src/main/scala/li/cil/oc/integration/appeng/internal/PartEnvironmentBase.scala new file mode 100644 index 0000000000..0937bb9f04 --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/appeng/internal/PartEnvironmentBase.scala @@ -0,0 +1,82 @@ +package li.cil.oc.integration.appeng.internal + +import appeng.api.config.Upgrades +import appeng.api.parts.IPartHost +import appeng.api.storage.StorageName +import appeng.api.storage.data.IAEStack +import appeng.parts.automation.PartSharedItemBus +import appeng.tile.inventory.IIAEStackInventory +import appeng.util.item.AEItemStack +import li.cil.oc.api.machine.{Arguments, Context} +import li.cil.oc.api.network.ManagedEnvironment +import li.cil.oc.integration.appeng.AEStackFactory +import li.cil.oc.util.DatabaseAccess +import li.cil.oc.util.ExtendedArguments._ +import net.minecraftforge.common.util.ForgeDirection + +import scala.reflect.ClassTag + +trait PartEnvironmentBase[PartType <: IIAEStackInventory] extends ManagedEnvironment { + def host: IPartHost + + def getPart(side: ForgeDirection): PartType = { + host.getPart(side) match { + case part: PartType => part + case _ => throw new IllegalArgumentException("no matching part") + } + } + + private def getConfigAndValidate(part: PartType, slot: Int) = { + val config = part.getAEInventoryByName(StorageName.CONFIG) + if (slot < 0 || slot >= config.getSizeInventory) { + throw new IllegalArgumentException("invalid slot") + } + config + } + + def getPartConfig(part: PartType, slot: Int): IAEStack[_] = { + val config = getConfigAndValidate(part, slot) + config.getAEStackInSlot(slot) + } + + // function(side:number[, slot:number]):table + def getPartConfig(context: Context, args: Arguments): IAEStack[_] = { + val side = args.checkSideAny(0) + val part = getPart(side) + getPartConfig(part, args.optInteger(1, 0)) + } + + def setPartConfig(part: PartType, slot: Int, stack: IAEStack[_]): Unit = { + val config = getConfigAndValidate(part, slot) + config.putAEStackInSlot(slot, stack) + } + + def setPartConfig[T <: IAEStack[T]: ClassTag](context: Context, args: Arguments): Unit = { + val side = args.checkSideAny(0) + val part = getPart(side) + val (slot, offset) = if (args.isInteger(1)) (args.checkInteger(1), 2) else (0, 1) + val stack = AEStackFactory.parse[T](args.checkTable(offset)) + setPartConfig(part, slot, stack) + } + + // function(side:number[, slot:number][, database:address, entry:number[, size:number]]):boolean + def setPartConfigByDatabase(context: Context, args: Arguments): Unit = { + val stack: AEItemStack = if (!args.isString(2)) null + else AEItemStack.create(DatabaseAccess.getStackFromDatabase(node, args, 2)) + val side = args.checkSideAny(0) + val part = getPart(side) + val slot = args.optInteger(1, 0) + setPartConfig(part, slot, stack) + } +} + +trait PartSharedItemBusBase[PartType <: PartSharedItemBus[_]] extends PartEnvironmentBase[PartType] { + def getSlotSize(part: PartType): Int = + Math.min(1 + part.getInstalledUpgrades(Upgrades.CAPACITY) * 4, part.getAEInventoryByName(StorageName.CONFIG).getSizeInventory) + + def getSlotSize(context: Context, args: Arguments): Int = { + val side = args.checkSideAny(0) + val part = getPart(side) + getSlotSize(part) + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000..37bc2f4aca --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/appeng/internal/PatternEnvironment.scala @@ -0,0 +1,108 @@ +package li.cil.oc.integration.appeng.internal + +import appeng.api.implementations.tiles.ISegmentedInventory +import appeng.api.parts.IPartHost +import appeng.api.storage.data.IAEStack +import appeng.tile.misc.TileInterface +import appeng.util.Platform +import appeng.util.item.AEItemStack +import li.cil.oc.api.machine.{Arguments, Context} +import li.cil.oc.api.network.ManagedEnvironment +import li.cil.oc.integration.appeng.AEStackFactory +import li.cil.oc.util.DatabaseAccess +import li.cil.oc.util.ExtendedArguments.extendedArguments +import li.cil.oc.util.ResultWrapper.result +import net.minecraft.inventory.IInventory +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound +import net.minecraftforge.common.util.Constants.NBT +import net.minecraftforge.common.util.ForgeDirection + +trait PatternEnvironment extends ManagedEnvironment { + def getPatternInventory(context: Context, args: Arguments): IInventory + + 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 + 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) + (inv, index, slot, pattern, encodedValue, nbt) + } + + // function(slot:number, index:number, database:address, entry:number):boolean + def storeInterfacePattern(context: Context, args: Arguments, tag: String): Array[AnyRef] = { + val (inv, index, slot, pattern, encodedValue, nbt) = getPatternNBT(context, args, tag) + val stackNBT = nbt.getCompoundTagAt(index) + val stack = ItemStack.loadItemStackFromNBT(stackNBT) + stack.stackSize = 1; + DatabaseAccess.withDatabase(node, args.checkString(offset + 2), database => { + val slot = args.optSlot(database.data, offset + 3, 0) + database.setStackInSlot(slot, stack) + result(true) + }) + } + + // 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 + else if (args.isTable(offset + 2)) { + val table = args.checkTable(offset + 2) + val tp = args.checkString(offset + 3) + AEStackFactory.parse(tp, table) + } + else { + AEItemStack.create(DatabaseAccess.getStackFromDatabase(node, args, offset + 2)) + } + + while (inTag.tagCount() <= index) + inTag.appendTag(new NBTTagCompound()) + if (stack != null) { + val nbt = new NBTTagCompound() + Platform.writeStackNBT(stack, nbt) + inTag.func_150304_a(index, nbt) + } + else + inTag.removeTag(index) + encodedValue.setTag(tag, inTag) + pattern.setTagCompound(encodedValue) + inv.setInventorySlotContents(slot, null) + inv.setInventorySlotContents(slot, pattern) + result(true) + } +} + +trait BlockPatternEnvironment extends PatternEnvironment { + def tile: TileInterface + + override def getPatternInventory(context: Context, args: Arguments): IInventory = { + tile.getInventoryByName("patterns") + } +} + +trait PartPatternEnvironment[PartType <: ISegmentedInventory] extends PatternEnvironment { + def host: IPartHost + + override def offset: Int = 1 + + def getPart(side: ForgeDirection): PartType = { + host.getPart(side) match { + case part: PartType => part + case _ => throw new IllegalArgumentException("no matching part") + } + } + + override def getPatternInventory(context: Context, args: Arguments): IInventory = { + val side = args.checkSideAny(0) + getPart(side).getInventoryByName("patterns") + } +} \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/integration/thaumicenergistics/ConvertAEEssentiaStack.scala b/src/main/scala/li/cil/oc/integration/thaumicenergistics/ConvertAEEssentiaStack.scala new file mode 100644 index 0000000000..b7e2af873d --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/thaumicenergistics/ConvertAEEssentiaStack.scala @@ -0,0 +1,32 @@ +package li.cil.oc.integration.thaumicenergistics + +import li.cil.oc.api.driver.Converter +import li.cil.oc.integration.util.MapUtils.MapWrapper +import thaumcraft.api.aspects.Aspect +import thaumicenergistics.common.storage.AEEssentiaStack + +import java.util +import scala.collection.JavaConversions.mapAsScalaMap + +object ConvertAEEssentiaStack extends Converter { + override def convert(value: Any, output: util.Map[AnyRef, AnyRef]): Unit = { + value match { + case stack: AEEssentiaStack => + output += "name" -> stack.getAspect.getName + output += "amount" -> Long.box(stack.getStackSize) + output += "label" -> stack.getLocalizedName + case _ => + } + } + + def parse(args: util.Map[_, _]): AEEssentiaStack = { + val name = args.getString("name") match { + case Some(n) => n + case None => throw new IllegalArgumentException("aspect name expected") + } + val aspect = Aspect.getAspect(name) + if (aspect == null) throw new IllegalArgumentException("aspect not found") + val amount = args.getInt("amount").getOrElse(0) + new AEEssentiaStack(aspect, amount) + } +} diff --git a/src/main/scala/li/cil/oc/integration/thaumicenergistics/ConvertAspectCraftable.scala b/src/main/scala/li/cil/oc/integration/thaumicenergistics/ConvertAspectCraftable.scala index c05bff526c..f412ee46e7 100644 --- a/src/main/scala/li/cil/oc/integration/thaumicenergistics/ConvertAspectCraftable.scala +++ b/src/main/scala/li/cil/oc/integration/thaumicenergistics/ConvertAspectCraftable.scala @@ -1,12 +1,15 @@ package li.cil.oc.integration.thaumicenergistics -import java.util + import cpw.mods.fml.common.registry.GameRegistry import li.cil.oc.api.driver.Converter import net.minecraft.item.ItemStack + +import java.util import scala.collection.convert.WrapAsScala._ object ConvertAspectCraftable extends Converter { private val DistillationPattern = GameRegistry.findItem("thaumicenergistics", "crafting.aspect") + override def convert(value: scala.Any, output: util.Map[AnyRef, AnyRef]): Unit = value match { case stack: ItemStack if stack.getItem == DistillationPattern && stack.hasTagCompound => output += "aspect" -> stack.getTagCompound.getString("Aspect") diff --git a/src/main/scala/li/cil/oc/integration/thaumicenergistics/DriverEssentiaExportBus.scala b/src/main/scala/li/cil/oc/integration/thaumicenergistics/DriverEssentiaExportBus.scala index 1a1ab99590..f1878c0bbe 100644 --- a/src/main/scala/li/cil/oc/integration/thaumicenergistics/DriverEssentiaExportBus.scala +++ b/src/main/scala/li/cil/oc/integration/thaumicenergistics/DriverEssentiaExportBus.scala @@ -2,12 +2,11 @@ package li.cil.oc.integration.thaumicenergistics import appeng.api.parts.IPartHost import li.cil.oc.api.driver -import li.cil.oc.api.driver.EnvironmentProvider -import li.cil.oc.api.driver.NamedBlock -import li.cil.oc.api.machine.Arguments -import li.cil.oc.api.machine.Callback -import li.cil.oc.api.machine.Context +import li.cil.oc.api.driver.{EnvironmentProvider, NamedBlock} +import li.cil.oc.api.machine.{Arguments, Callback, Context} import li.cil.oc.integration.ManagedTileEntityEnvironment +import li.cil.oc.integration.appeng.AEStackFactory +import li.cil.oc.integration.appeng.internal.PartSharedItemBusBase import li.cil.oc.util.ExtendedArguments._ import li.cil.oc.util.ResultWrapper._ import net.minecraft.item.ItemStack @@ -15,57 +14,55 @@ import net.minecraft.world.World import net.minecraftforge.common.util.ForgeDirection import thaumicenergistics.api.ThEApi import thaumicenergistics.common.parts.PartEssentiaExportBus +import thaumicenergistics.common.storage.AEEssentiaStack object DriverEssentiaExportBus extends driver.SidedBlock { override def worksWith(world: World, x: Int, y: Int, z: Int, side: ForgeDirection) = world.getTileEntity(x, y, z) match { - case container: IPartHost => ForgeDirection.VALID_DIRECTIONS.map(container.getPart).filter(obj => { obj != null }).exists(_.isInstanceOf[PartEssentiaExportBus]) + case container: IPartHost => ForgeDirection.VALID_DIRECTIONS.map(container.getPart).filter(obj => { + obj != null + }).exists(_.isInstanceOf[PartEssentiaExportBus]) case _ => false } override def createEnvironment(world: World, x: Int, y: Int, z: Int, side: ForgeDirection) = new Environment(world, world.getTileEntity(x, y, z).asInstanceOf[IPartHost]) - final class Environment(val world: World, val host: IPartHost) extends ManagedTileEntityEnvironment[IPartHost](host, "essentia_exportbus") with NamedBlock with PartEnvironmentBase { + final class Environment(val world: World, val host: IPartHost) extends ManagedTileEntityEnvironment[IPartHost](host, "essentia_exportbus") with NamedBlock with PartSharedItemBusBase[PartEssentiaExportBus] { override def preferredName = "essentia_exportbus" override def priority = 2 @Callback(doc = "function(side:number[, slot:number]):string -- Get the configuration of the export bus pointing in the specified direction.") - def getExportConfiguration(context: Context, args: Arguments): Array[AnyRef] = getPartConfig[PartEssentiaExportBus](context, args) + def getExportConfiguration(context: Context, args: Arguments): Array[AnyRef] = result(getPartConfig(context, args)) @Callback(doc = "function(side:number[, slot:number][, aspect:string]):boolean -- Configure the export bus pointing in the specified direction to export essentia matching the specified type.") - def setExportConfiguration(context: Context, args: Arguments): Array[AnyRef] = setPartConfig[PartEssentiaExportBus](context, args) + def setExportConfiguration(context: Context, args: Arguments): Array[AnyRef] = { + setPartConfig[AEEssentiaStack](context, args) + result(true) + } @Callback(doc = "function(side:number):number -- Get the number of valid slots in this export bus.") - def getExportSlotSize(context: Context, args: Arguments): Array[AnyRef] = getSlotSize[PartEssentiaExportBus](context, args) + def getExportSlotSize(context: Context, args: Arguments): Array[AnyRef] = result(getSlotSize(context, args)) @Callback(doc = "function(side:number):boolean -- Get whether or not essentia exported into a void jar will allow voiding") def getVoidAllowed(context: Context, args: Arguments): Array[AnyRef] = { val side = args.checkSideAny(0) - host.getPart(side) match { - case part: PartEssentiaExportBus => - result(part.isVoidAllowed) - case _ => result(Unit, "no essentia export bus") - } + val part = getPart(side) + result(part.getVoidAllowed) } @Callback(doc = "function(side:number, allowed:boolean):boolean -- Set void mode") def setVoidAllowed(context: Context, args: Arguments): Array[AnyRef] = { val side = args.checkSideAny(0) val mode = args.checkBoolean(1) - host.getPart(side) match { - case part: PartEssentiaExportBus => - var didSomething = false - if (mode != part.isVoidAllowed) { - part.toggleVoidMode() - didSomething = true - } - - result(didSomething) - case _ => result(Unit, "no essentia export bus") + val part = getPart(side) + var didSomething = false + if (mode != part.getVoidAllowed) { + part.toggleVoidAllowed() + didSomething = true } + result(didSomething) } - } object Provider extends EnvironmentProvider { diff --git a/src/main/scala/li/cil/oc/integration/thaumicenergistics/DriverEssentiaImportBus.scala b/src/main/scala/li/cil/oc/integration/thaumicenergistics/DriverEssentiaImportBus.scala index 322f5c00ed..e2525a2cfe 100644 --- a/src/main/scala/li/cil/oc/integration/thaumicenergistics/DriverEssentiaImportBus.scala +++ b/src/main/scala/li/cil/oc/integration/thaumicenergistics/DriverEssentiaImportBus.scala @@ -5,35 +5,43 @@ import li.cil.oc.api.driver import li.cil.oc.api.driver.{EnvironmentProvider, NamedBlock} import li.cil.oc.api.machine.{Arguments, Callback, Context} import li.cil.oc.integration.ManagedTileEntityEnvironment +import li.cil.oc.integration.appeng.AEStackFactory +import li.cil.oc.integration.appeng.internal.PartSharedItemBusBase +import li.cil.oc.util.ResultWrapper.result import net.minecraft.item.ItemStack import net.minecraft.world.World import net.minecraftforge.common.util.ForgeDirection import thaumicenergistics.api.ThEApi import thaumicenergistics.common.parts.PartEssentiaImportBus +import thaumicenergistics.common.storage.AEEssentiaStack object DriverEssentiaImportBus extends driver.SidedBlock { override def worksWith(world: World, x: Int, y: Int, z: Int, side: ForgeDirection) = world.getTileEntity(x, y, z) match { - case container: IPartHost => ForgeDirection.VALID_DIRECTIONS.map(container.getPart).filter(obj => { obj != null }).exists(_.isInstanceOf[PartEssentiaImportBus]) + case container: IPartHost => ForgeDirection.VALID_DIRECTIONS.map(container.getPart).filter(obj => { + obj != null + }).exists(_.isInstanceOf[PartEssentiaImportBus]) case _ => false } override def createEnvironment(world: World, x: Int, y: Int, z: Int, side: ForgeDirection) = new Environment(world, world.getTileEntity(x, y, z).asInstanceOf[IPartHost]) - final class Environment(val world: World, val host: IPartHost) extends ManagedTileEntityEnvironment[IPartHost](host, "essentia_importbus") with NamedBlock with PartEnvironmentBase { + final class Environment(val world: World, val host: IPartHost) extends ManagedTileEntityEnvironment[IPartHost](host, "essentia_importbus") with NamedBlock with PartSharedItemBusBase[PartEssentiaImportBus] { override def preferredName = "essentia_importbus" override def priority = 2 @Callback(doc = "function(side:number[, slot:number]):string -- Get the configuration of the import bus pointing in the specified direction.") - def getImportConfiguration(context: Context, args: Arguments): Array[AnyRef] = getPartConfig[PartEssentiaImportBus](context, args) + def getImportConfiguration(context: Context, args: Arguments): Array[AnyRef] = result(getPartConfig(context, args)) @Callback(doc = "function(side:number[, slot:number][, aspect:string]):boolean -- Configure the import bus pointing in the specified direction to import essentia matching the specified type.") - def setImportConfiguration(context: Context, args: Arguments): Array[AnyRef] = setPartConfig[PartEssentiaImportBus](context, args) + def setImportConfiguration(context: Context, args: Arguments): Array[AnyRef] = { + setPartConfig[AEEssentiaStack](context, args) + result(true) + } @Callback(doc = "function(side:number):number -- Get the number of valid slots in this import bus.") - def getImportSlotSize(context: Context, args: Arguments): Array[AnyRef] = getSlotSize[PartEssentiaImportBus](context, args) - + def getImportSlotSize(context: Context, args: Arguments): Array[AnyRef] = result(getSlotSize(context, args)) } object Provider extends EnvironmentProvider { diff --git a/src/main/scala/li/cil/oc/integration/thaumicenergistics/ModThaumicEnergistics.scala b/src/main/scala/li/cil/oc/integration/thaumicenergistics/ModThaumicEnergistics.scala index 6a4244dbfa..ede39cc164 100644 --- a/src/main/scala/li/cil/oc/integration/thaumicenergistics/ModThaumicEnergistics.scala +++ b/src/main/scala/li/cil/oc/integration/thaumicenergistics/ModThaumicEnergistics.scala @@ -1,14 +1,17 @@ package li.cil.oc.integration.thaumicenergistics import li.cil.oc.api.Driver -import li.cil.oc.integration.Mod -import li.cil.oc.integration.ModProxy -import li.cil.oc.integration.Mods +import li.cil.oc.integration.{Mod, ModProxy, Mods} +import li.cil.oc.integration.appeng.AEStackFactory +import thaumicenergistics.common.storage.{AEEssentiaStack, AEEssentiaStackType} object ModThaumicEnergistics extends ModProxy { override def getMod: Mod = Mods.ThaumicEnergistics override def initialize(): Unit = { + AEStackFactory.register[AEEssentiaStack](AEEssentiaStackType.ESSENTIA_STACK_TYPE, ConvertAEEssentiaStack.convert, ConvertAEEssentiaStack.parse) + + Driver.add(ConvertAEEssentiaStack) Driver.add(DriverController) Driver.add(DriverBlockInterface) Driver.add(DriverEssentiaExportBus) diff --git a/src/main/scala/li/cil/oc/integration/thaumicenergistics/NetworkControl.scala b/src/main/scala/li/cil/oc/integration/thaumicenergistics/NetworkControl.scala index 61a18b252f..71cb67121e 100644 --- a/src/main/scala/li/cil/oc/integration/thaumicenergistics/NetworkControl.scala +++ b/src/main/scala/li/cil/oc/integration/thaumicenergistics/NetworkControl.scala @@ -2,22 +2,34 @@ package li.cil.oc.integration.thaumicenergistics import appeng.api.networking.security.IActionHost import appeng.me.helpers.IGridProxyable -import li.cil.oc.api.machine.Arguments -import li.cil.oc.api.machine.Callback -import li.cil.oc.api.machine.Context +import li.cil.oc.api.machine.{Arguments, Callback, Context} +import li.cil.oc.api.network.Node +import li.cil.oc.integration.appeng.NetworkControl.NetworkContents import li.cil.oc.util.ResultWrapper._ import net.minecraft.tileentity.TileEntity -import thaumicenergistics.api.IThEEssentiaGas +import thaumicenergistics.common.storage.AEEssentiaStack +import thaumicenergistics.common.storage.AEEssentiaStackType.ESSENTIA_STACK_TYPE -import scala.collection.convert.WrapAsScala._ +import scala.collection.JavaConverters.iterableAsScalaIterableConverter // Note to self: this class is used by ExtraCells (and potentially others), do not rename / drastically change it. trait NetworkControl[AETile >: Null <: TileEntity with IGridProxyable with IActionHost] { def tile: AETile + def node: Node + @Callback(doc = "function():table -- Get a list of the stored essentia in the network.") def getEssentiaInNetwork(context: Context, args: Arguments): Array[AnyRef] = - result(tile.getProxy.getStorage.getFluidInventory.getStorageList.filter(stack => - stack.getFluid != null && stack.getFluid.isInstanceOf[IThEEssentiaGas]). - map(ThaumicEnergisticsUtils.getAspect).toArray) + result(tile.getProxy.getStorage.getMEMonitor(ESSENTIA_STACK_TYPE).getStorageList.asScala.toArray) + + @Callback(doc = "function([subscribe:bool]):userdata -- Get an iterator object for the list of the essentia in the network. [Event: network_essentia_changed]") + def allEssentia(context: Context, args: Arguments): Array[AnyRef] = { + result(new EssentiaNetworkContents(tile, node, args.optBoolean(0, false))) + } + + private class EssentiaNetworkContents(controller: TileEntity with IGridProxyable with IActionHost, node: Node, subscribe: Boolean) extends NetworkContents[AEEssentiaStack](controller, node, subscribe) { + def this() = this(null, null, false) + + override val event_name: String = "network_essentia_changed" + } } diff --git a/src/main/scala/li/cil/oc/integration/thaumicenergistics/PartEnvironmentBase.scala b/src/main/scala/li/cil/oc/integration/thaumicenergistics/PartEnvironmentBase.scala deleted file mode 100644 index 7733f7b267..0000000000 --- a/src/main/scala/li/cil/oc/integration/thaumicenergistics/PartEnvironmentBase.scala +++ /dev/null @@ -1,78 +0,0 @@ -package li.cil.oc.integration.thaumicenergistics - -import appeng.api.parts.IPartHost -import li.cil.oc.Settings -import li.cil.oc.api.machine.{Arguments, Context} -import li.cil.oc.api.network.ManagedEnvironment -import li.cil.oc.util.ExtendedArguments._ -import li.cil.oc.util.ResultWrapper.result -import net.minecraftforge.common.util.FakePlayerFactory -import net.minecraft.world.World -import net.minecraft.world.WorldServer -import thaumcraft.api.aspects.Aspect -import thaumicenergistics.common.network.IAspectSlotPart - -import scala.reflect.ClassTag - -trait PartEnvironmentBase extends ManagedEnvironment { - def world: World - def host: IPartHost - - private def resolveAspectSlot(part: IAspectSlotPart, slot: Int): Int = { - val available = part.getAvailableAspectSlots - - if (slot < 1 || slot > available.length) { - throw new IllegalArgumentException("invalid slot") - } - available(slot - 1) - } - - // function(side:number):number - def getSlotSize[PartType <: IAspectSlotPart : ClassTag](context: Context, args: Arguments): Array[AnyRef] = { - val side = args.checkSideAny(0) - host.getPart(side) match { - case part: PartType => - result(part.getAvailableAspectSlots.length) - case _ => result(Unit, "no matching part") - } - } - - // function(side:number[, slot:number]):string - def getPartConfig[PartType <: IAspectSlotPart : ClassTag](context: Context, args: Arguments): Array[AnyRef] = { - val side = args.checkSideAny(0) - host.getPart(side) match { - case part: PartType => - val slot = resolveAspectSlot(part, args.optInteger(1, 1)) - val stack = Option(part.getAspect(slot)) - result(stack match { - case Some(aspect) => aspect.getTag - case None => Unit - }) - case _ => result(Unit, "no matching part") - } - } - - // function(side:number[, slot:number][, aspect:string]):boolean - def setPartConfig[PartType <: IAspectSlotPart : ClassTag](context: Context, args: Arguments): Array[AnyRef] = { - val side = args.checkSideAny(0) - host.getPart(side) match { - case part: PartType => - val noSlotArg = args.isString(1) - val slot = resolveAspectSlot(part, if (noSlotArg) 1 else args.optInteger(1, 1)) - val aspect = if (noSlotArg || args.count > 2) { - Aspect.getAspect(args.checkString(if (noSlotArg) 1 else 2)) match { - case aspect: Aspect => aspect - case _ => throw new IllegalArgumentException("invalid aspect") - } - } - else null - - val fakePlayer = FakePlayerFactory.get(world.asInstanceOf[WorldServer], Settings.get.fakePlayerProfile) - - part.setAspect(slot, aspect, fakePlayer) - context.pause(0.5) - result(true) - case _ => result(Unit, "no matching part") - } - } -} diff --git a/src/main/scala/li/cil/oc/integration/util/MapUtils.scala b/src/main/scala/li/cil/oc/integration/util/MapUtils.scala new file mode 100644 index 0000000000..f4d086c6ec --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/util/MapUtils.scala @@ -0,0 +1,40 @@ +package li.cil.oc.integration.util + +object MapUtils { + implicit class MapWrapper(val map: java.util.Map[_, _]) extends AnyVal { + def getInt(key: String): Option[Int] = map.get(key) match { + case value: java.lang.Number => Some(value.intValue) + case _ => None + } + + def getDouble(key: String): Option[Double] = map.get(key) match { + case value: java.lang.Number => Some(value.doubleValue) + case _ => None + } + + def getFloat(key: String): Option[Float] = map.get(key) match { + case value: java.lang.Number => Some(value.floatValue) + case _ => None + } + + def getLong(key: String): Option[Long] = map.get(key) match { + case value: java.lang.Number => Some(value.longValue) + case _ => None + } + + def getBoolean(key: String): Option[Boolean] = map.get(key) match { + case value: java.lang.Boolean => Some(value) + case _ => None + } + + def getString(key: String): Option[String] = map.get(key) match { + case value: String => Some(value) + case _ => None + } + + def getMap(key: String): Option[java.util.Map[_,_]] = map.get(key) match { + case value: java.util.Map[_,_] => Some(value) + case _ => None + } + } +} diff --git a/src/main/scala/li/cil/oc/integration/vanilla/ConverterFluidStack.scala b/src/main/scala/li/cil/oc/integration/vanilla/ConverterFluidStack.scala index 844b832ffa..58076b4f5c 100644 --- a/src/main/scala/li/cil/oc/integration/vanilla/ConverterFluidStack.scala +++ b/src/main/scala/li/cil/oc/integration/vanilla/ConverterFluidStack.scala @@ -1,16 +1,17 @@ package li.cil.oc.integration.vanilla import java.util - import li.cil.oc.Settings import li.cil.oc.api +import net.minecraftforge.fluids.{FluidRegistry, FluidStack} import scala.collection.convert.WrapAsScala._ +import li.cil.oc.integration.util.MapUtils.MapWrapper object ConverterFluidStack extends api.driver.Converter { override def convert(value: scala.Any, output: util.Map[AnyRef, AnyRef]) = value match { - case stack: net.minecraftforge.fluids.FluidStack => + case stack: FluidStack => if (Settings.get.insertIdsInConverters) { output += "id" -> Int.box(stack.getFluid.getID) } @@ -23,4 +24,16 @@ object ConverterFluidStack extends api.driver.Converter { } case _ => } + def parse(args: util.Map[_, _]): FluidStack = { + val id = args.getInt("id") + val name = args.getString("name") + val fluid = (id, name) match { + case (Some(i), _) => FluidRegistry.getFluid(i) + case (_, Some(n)) => FluidRegistry.getFluid(n) + case _ => throw new IllegalArgumentException("fluid id or name expected") + } + if (fluid == null) throw new IllegalArgumentException("fluid not found") + val amount = args.getInt("amount").getOrElse(0) + new FluidStack(fluid, amount) + } } diff --git a/src/main/scala/li/cil/oc/integration/vanilla/ConverterItemStack.scala b/src/main/scala/li/cil/oc/integration/vanilla/ConverterItemStack.scala index 802161139e..9f67146eca 100644 --- a/src/main/scala/li/cil/oc/integration/vanilla/ConverterItemStack.scala +++ b/src/main/scala/li/cil/oc/integration/vanilla/ConverterItemStack.scala @@ -1,14 +1,16 @@ package li.cil.oc.integration.vanilla -import java.util +import cpw.mods.fml.common.registry.GameData +import java.util import li.cil.oc.Settings import li.cil.oc.api +import li.cil.oc.integration.util.MapUtils.MapWrapper import li.cil.oc.util.ExtendedNBT._ import net.minecraft.enchantment.Enchantment import net.minecraft.enchantment.EnchantmentHelper import net.minecraft.item -import net.minecraft.item.Item +import net.minecraft.item.{Item, ItemStack} import net.minecraft.nbt.CompressedStreamTools import net.minecraft.nbt.NBTTagString import net.minecraftforge.common.util.Constants.NBT @@ -20,7 +22,7 @@ import scala.collection.mutable object ConverterItemStack extends api.driver.Converter { override def convert(value: AnyRef, output: util.Map[AnyRef, AnyRef]) = value match { - case stack: item.ItemStack => + case stack: ItemStack => if (Settings.get.insertIdsInConverters) { output += "id" -> Int.box(Item.getIdFromItem(stack.getItem)) output += "oreNames" -> OreDictionary.getOreIDs(stack).map(OreDictionary.getOreName) @@ -64,4 +66,21 @@ object ConverterItemStack extends api.driver.Converter { } case _ => } + + private val ItemRegistry = GameData.getItemRegistry + def parse(args: util.Map[_, _]): ItemStack = + { + val id = args.getInt("id") + val name = args.getString("name") + val item = (id, name) match { + case (Some(i), _) => ItemRegistry.getObjectById(i) + case (_, Some(n)) => ItemRegistry.getObject(n) + case _ => throw new IllegalArgumentException("item id or name expected") + } + if (item == null) throw new IllegalArgumentException("item not found") + val amount = args.getInt("size").getOrElse(1) + val damage = args.getInt("damage").getOrElse(0) + val stack = new ItemStack(item, amount, damage) + stack + } } diff --git a/src/main/scala/li/cil/oc/util/DatabaseAccess.scala b/src/main/scala/li/cil/oc/util/DatabaseAccess.scala index 60ae20bc8c..bc97efac18 100644 --- a/src/main/scala/li/cil/oc/util/DatabaseAccess.scala +++ b/src/main/scala/li/cil/oc/util/DatabaseAccess.scala @@ -3,9 +3,15 @@ package li.cil.oc.util import li.cil.oc.api.network.Component import li.cil.oc.api.network.Node import li.cil.oc.server.component.UpgradeDatabase +import li.cil.oc.api.machine.Arguments +import net.minecraft.item.ItemStack object DatabaseAccess { def withDatabase(node: Node, address: String, f: UpgradeDatabase => Array[AnyRef]): Array[AnyRef] = { + withDatabaseGeneric[Array[AnyRef]](node, address, f) + } + + def withDatabaseGeneric[T](node: Node, address: String, f: UpgradeDatabase => T): T = { node.network.node(address) match { case component: Component => component.host match { case database: UpgradeDatabase => f(database) @@ -14,4 +20,18 @@ object DatabaseAccess { case _ => throw new IllegalArgumentException("no such component") } } + + def getStackFromDatabase(node: Node, args: Arguments, offset: Int): ItemStack = { + if (args.isString(offset)) DatabaseAccess.withDatabaseGeneric[ItemStack](node, args.checkString(offset), database => { + val entry = args.checkInteger(offset + 1) + val size = args.optInteger(offset + 2, 1) + val dbStack = database.getStackInSlot(entry - 1) + if (dbStack == null || size < 1) null + else { + dbStack.stackSize = math.min(size, dbStack.getMaxStackSize) + dbStack + } + }) + else null + } } From 6c7c52fb0fe47667b4e8f2273bd99830a1fdc24e Mon Sep 17 00:00:00 2001 From: hinyb <40139991+hinyb@users.noreply.github.com> Date: Mon, 16 Feb 2026 14:10:43 +0800 Subject: [PATCH 06/11] refactor: merge setPartConfig and Extended into single setPartConfig for API consistency. --- .../integration/appeng/DriverExportBus.scala | 12 +++------ .../integration/appeng/DriverImportBus.scala | 12 +++------ .../appeng/internal/PartEnvironmentBase.scala | 25 +++++++++++-------- 3 files changed, 20 insertions(+), 29 deletions(-) diff --git a/src/main/scala/li/cil/oc/integration/appeng/DriverExportBus.scala b/src/main/scala/li/cil/oc/integration/appeng/DriverExportBus.scala index 21865715dc..52e1ae558d 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/DriverExportBus.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/DriverExportBus.scala @@ -10,7 +10,7 @@ import li.cil.oc.api.driver import li.cil.oc.api.driver.{EnvironmentProvider, NamedBlock} import li.cil.oc.api.machine.{Arguments, Callback, Context} import li.cil.oc.integration.ManagedTileEntityEnvironment -import li.cil.oc.integration.appeng.internal.PartSharedItemBusBase +import li.cil.oc.integration.appeng.internal.PartItemBusBase import li.cil.oc.util.ExtendedArguments._ import li.cil.oc.util.ResultWrapper._ import li.cil.oc.util.{BlockPosition, InventoryUtils} @@ -31,7 +31,7 @@ object DriverExportBus extends driver.SidedBlock { override def createEnvironment(world: World, x: Int, y: Int, z: Int, side: ForgeDirection) = new Environment(world.getTileEntity(x, y, z).asInstanceOf[IPartHost]) - final class Environment(val host: IPartHost) extends ManagedTileEntityEnvironment[IPartHost](host, "me_exportbus") with NamedBlock with PartSharedItemBusBase[PartExportBus] { + final class Environment(val host: IPartHost) extends ManagedTileEntityEnvironment[IPartHost](host, "me_exportbus") with NamedBlock with PartItemBusBase[PartExportBus] { override def preferredName = "me_exportbus" override def priority = 2 @@ -39,14 +39,8 @@ object DriverExportBus extends driver.SidedBlock { @Callback(doc = "function(side:number, [ slot:number]):boolean -- Get the configuration of the export bus pointing in the specified direction.") def getExportConfiguration(context: Context, args: Arguments): Array[AnyRef] = result(getPartConfig(context, args)) - @Callback(doc = "function(side:number[, slot:number][, database:address, entry:number):boolean -- Configure the export bus pointing in the specified direction to export item stacks matching the specified descriptor.") + @Callback(doc = "function(side:number[, slot:number][, database:address, entry:number):boolean OR function(side:number[, slot:number][, detail: table):boolean -- Configure the export bus pointing in the specified direction to export item stacks matching the specified descriptor.") def setExportConfiguration(context: Context, args: Arguments): Array[AnyRef] = { - setPartConfigByDatabase(context, args) - result(true) - } - - @Callback(doc = "function(side:number[, slot:number][, detail: table):boolean -- Configure the export bus pointing in the specified direction to export stacks matching the specified descriptor.") - def setExportConfigurationExtended(context: Context, args: Arguments): Array[AnyRef] = { setPartConfig[IAEItemStack](context, args) result(true) } diff --git a/src/main/scala/li/cil/oc/integration/appeng/DriverImportBus.scala b/src/main/scala/li/cil/oc/integration/appeng/DriverImportBus.scala index 2e8c2d9f70..188ecccc9d 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/DriverImportBus.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/DriverImportBus.scala @@ -7,7 +7,7 @@ import li.cil.oc.api.driver import li.cil.oc.api.driver.{EnvironmentProvider, NamedBlock} import li.cil.oc.api.machine.{Arguments, Callback, Context} import li.cil.oc.integration.ManagedTileEntityEnvironment -import li.cil.oc.integration.appeng.internal.PartSharedItemBusBase +import li.cil.oc.integration.appeng.internal.PartItemBusBase import li.cil.oc.util.ResultWrapper.result import net.minecraft.item.ItemStack import net.minecraft.world.World @@ -24,7 +24,7 @@ object DriverImportBus extends driver.SidedBlock { override def createEnvironment(world: World, x: Int, y: Int, z: Int, side: ForgeDirection) = new Environment(world.getTileEntity(x, y, z).asInstanceOf[IPartHost]) - final class Environment(val host: IPartHost) extends ManagedTileEntityEnvironment[IPartHost](host, "me_importbus") with NamedBlock with PartSharedItemBusBase[PartImportBus] { + final class Environment(val host: IPartHost) extends ManagedTileEntityEnvironment[IPartHost](host, "me_importbus") with NamedBlock with PartItemBusBase[PartImportBus] { override def preferredName = "me_importbus" override def priority = 1 @@ -32,14 +32,8 @@ object DriverImportBus extends driver.SidedBlock { @Callback(doc = "function(side:number[, slot:number]):boolean -- Get the configuration of the import bus pointing in the specified direction.") def getImportConfiguration(context: Context, args: Arguments): Array[AnyRef] = result(getPartConfig(context, args)) - @Callback(doc = "function(side:number[, slot:number][, database:address, entry:number]):boolean -- Configure the import bus pointing in the specified direction to import item stacks matching the specified descriptor.") + @Callback(doc = "function(side:number[, slot:number][, database:address, entry:number]):boolean OR function(side:number[, slot:number][, detail:table]):boolean -- Configure the import bus pointing in the specified direction to import item stacks matching the specified descriptor.") def setImportConfiguration(context: Context, args: Arguments): Array[AnyRef] = { - setPartConfigByDatabase(context, args) - result(true) - } - - @Callback(doc = "function(side:number[, slot:number][, detail:table]):boolean -- Configure the import bus pointing in the specified direction to import stacks matching the specified descriptor.") - def setImportConfigurationExtended(context: Context, args: Arguments): Array[AnyRef] = { setPartConfig[IAEItemStack](context, args) result(true) } diff --git a/src/main/scala/li/cil/oc/integration/appeng/internal/PartEnvironmentBase.scala b/src/main/scala/li/cil/oc/integration/appeng/internal/PartEnvironmentBase.scala index 0937bb9f04..607ea67abf 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/internal/PartEnvironmentBase.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/internal/PartEnvironmentBase.scala @@ -3,7 +3,7 @@ package li.cil.oc.integration.appeng.internal import appeng.api.config.Upgrades import appeng.api.parts.IPartHost import appeng.api.storage.StorageName -import appeng.api.storage.data.IAEStack +import appeng.api.storage.data.{IAEItemStack, IAEStack} import appeng.parts.automation.PartSharedItemBus import appeng.tile.inventory.IIAEStackInventory import appeng.util.item.AEItemStack @@ -58,16 +58,6 @@ trait PartEnvironmentBase[PartType <: IIAEStackInventory] extends ManagedEnviron val stack = AEStackFactory.parse[T](args.checkTable(offset)) setPartConfig(part, slot, stack) } - - // function(side:number[, slot:number][, database:address, entry:number[, size:number]]):boolean - def setPartConfigByDatabase(context: Context, args: Arguments): Unit = { - val stack: AEItemStack = if (!args.isString(2)) null - else AEItemStack.create(DatabaseAccess.getStackFromDatabase(node, args, 2)) - val side = args.checkSideAny(0) - val part = getPart(side) - val slot = args.optInteger(1, 0) - setPartConfig(part, slot, stack) - } } trait PartSharedItemBusBase[PartType <: PartSharedItemBus[_]] extends PartEnvironmentBase[PartType] { @@ -79,4 +69,17 @@ trait PartSharedItemBusBase[PartType <: PartSharedItemBus[_]] extends PartEnviro val part = getPart(side) getSlotSize(part) } +} + +trait PartItemBusBase[PartType <: PartSharedItemBus[_]] extends PartSharedItemBusBase[PartType]{ + // function(side:number[, slot:number][, database:address, entry:number[, size:number]]):boolean + override def setPartConfig[T <: IAEStack[T]: ClassTag](context: Context, args: Arguments): Unit = { + val side = args.checkSideAny(0) + val part = getPart(side) + val (slot, offset) = if (args.isInteger(1)) (args.checkInteger(1), 2) else (0, 1) + val stack = + if (args.isTable(offset)) AEStackFactory.parse[IAEItemStack](args.checkTable(offset)) + else AEItemStack.create(DatabaseAccess.getStackFromDatabase(node, args, offset)) + setPartConfig(part, slot, stack) + } } \ No newline at end of file From 54fb6900d84dc89d974f43bc2f0402dca666bbf3 Mon Sep 17 00:00:00 2001 From: hinyb <40139991+hinyb@users.noreply.github.com> Date: Mon, 16 Feb 2026 14:36:19 +0800 Subject: [PATCH 07/11] build: remove unnecessary runtimeOnly dependency --- dependencies.gradle | 2 -- 1 file changed, 2 deletions(-) diff --git a/dependencies.gradle b/dependencies.gradle index 58d4ee85c6..54b30a899e 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -28,8 +28,6 @@ dependencies { compileOnly("com.github.GTNewHorizons:Railcraft:9.17.12:dev") {transitive = false} compileOnly("com.github.GTNewHorizons:StructureLib:1.4.25:dev") {transitive = false} compileOnly("com.github.GTNewHorizons:ThaumicEnergistics:1.7.27-GTNH:dev") {transitive = false} - runtimeOnly("thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev") {transitive = false} - runtimeOnly("com.github.GTNewHorizons:ThaumicEnergistics:1.7.27-GTNH:dev") {transitive = false} compileOnly("com.github.GTNewHorizons:TinkersMechworks:0.4.1:dev") {transitive = false} compileOnly("com.github.GTNewHorizons:waila:1.9.15:dev") {transitive = false} compileOnly("com.github.GTNewHorizons:WirelessRedstone-CBE:1.7.1:dev") {transitive = false} From 28c840205bd96c2a66864288187b45b3297f6c59 Mon Sep 17 00:00:00 2001 From: hinyb <40139991+hinyb@users.noreply.github.com> Date: Tue, 17 Feb 2026 05:40:11 +0800 Subject: [PATCH 08/11] fix: add missing wildcard case to prevent MatchError. --- .../scala/li/cil/oc/integration/appeng/ConverterAEStack.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/scala/li/cil/oc/integration/appeng/ConverterAEStack.scala b/src/main/scala/li/cil/oc/integration/appeng/ConverterAEStack.scala index 0784231de3..131174997d 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/ConverterAEStack.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/ConverterAEStack.scala @@ -10,6 +10,7 @@ object ConverterAEStack extends Converter { value match { case stack: IAEStack[_] => AEStackFactory.convert(stack, output) + case _ => } } } \ No newline at end of file From 60e45bdaf9ec144b16c25886121f343668a46b88 Mon Sep 17 00:00:00 2001 From: hinyb <40139991+hinyb@users.noreply.github.com> Date: Tue, 17 Feb 2026 06:06:16 +0800 Subject: [PATCH 09/11] refactor: clean up AE2 traits and fix type matching. --- .../appeng/DriverBlockInterface.scala | 6 +- .../integration/appeng/DriverExportBus.scala | 3 +- .../integration/appeng/DriverImportBus.scala | 4 +- .../appeng/DriverPartInterface.scala | 10 +- .../internal/InterfaceEnvironmentBase.scala | 101 +++++------------- .../appeng/internal/PartEnvironmentBase.scala | 27 +++-- .../appeng/internal/PatternEnvironment.scala | 16 +-- .../DriverEssentiaExportBus.scala | 4 +- .../DriverEssentiaImportBus.scala | 4 +- 9 files changed, 72 insertions(+), 103 deletions(-) diff --git a/src/main/scala/li/cil/oc/integration/appeng/DriverBlockInterface.scala b/src/main/scala/li/cil/oc/integration/appeng/DriverBlockInterface.scala index a37785ade4..a45844e656 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/DriverBlockInterface.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/DriverBlockInterface.scala @@ -6,7 +6,7 @@ import li.cil.oc.api.machine.{Arguments, Callback, Context} import li.cil.oc.api.network.ManagedEnvironment import li.cil.oc.api.prefab.DriverSidedTileEntity import li.cil.oc.integration.ManagedTileEntityEnvironment -import li.cil.oc.integration.appeng.internal.{BlockInterfaceEnvironmentAE2, BlockPatternEnvironment} +import li.cil.oc.integration.appeng.internal.{BlockInterfaceEnvironment, BlockPatternEnvironment} import li.cil.oc.util.ExtendedArguments._ import li.cil.oc.util.ResultWrapper.result import net.minecraft.item.ItemStack @@ -19,7 +19,7 @@ object DriverBlockInterface extends DriverSidedTileEntity { def createEnvironment(world: World, x: Int, y: Int, z: Int, side: ForgeDirection): ManagedEnvironment = new Environment(world.getTileEntity(x, y, z).asInstanceOf[TileInterface]) - final class Environment(val tile: TileInterface) extends ManagedTileEntityEnvironment[TileInterface](tile, "me_interface") with NamedBlock with NetworkControl[TileInterface] with BlockInterfaceEnvironmentAE2 with BlockPatternEnvironment { + final class Environment(val tile: TileInterface) extends ManagedTileEntityEnvironment[TileInterface](tile, "me_interface") with NamedBlock with NetworkControl[TileInterface] with BlockInterfaceEnvironment with BlockPatternEnvironment { override def preferredName = "me_interface" override def priority = 5 @@ -64,6 +64,8 @@ object DriverBlockInterface extends DriverSidedTileEntity { @Callback(doc = "function(slot:number, index:number):boolean -- Clear pattern output at the given index.") def clearInterfacePatternOutput(context: Context, args: Arguments): Array[AnyRef] = setPatternSlot(context, args, "out") + + override def offset: Int = 0 } object Provider extends EnvironmentProvider { diff --git a/src/main/scala/li/cil/oc/integration/appeng/DriverExportBus.scala b/src/main/scala/li/cil/oc/integration/appeng/DriverExportBus.scala index 52e1ae558d..12a430e6d6 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/DriverExportBus.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/DriverExportBus.scala @@ -19,6 +19,7 @@ import net.minecraft.world.World import net.minecraftforge.common.util.ForgeDirection import scala.collection.convert.WrapAsScala._ +import scala.reflect.ClassTag object DriverExportBus extends driver.SidedBlock { override def worksWith(world: World, x: Int, y: Int, z: Int, side: ForgeDirection) = @@ -31,7 +32,7 @@ object DriverExportBus extends driver.SidedBlock { override def createEnvironment(world: World, x: Int, y: Int, z: Int, side: ForgeDirection) = new Environment(world.getTileEntity(x, y, z).asInstanceOf[IPartHost]) - final class Environment(val host: IPartHost) extends ManagedTileEntityEnvironment[IPartHost](host, "me_exportbus") with NamedBlock with PartItemBusBase[PartExportBus] { + final class Environment(val host: IPartHost)(implicit val tag: ClassTag[PartExportBus]) extends ManagedTileEntityEnvironment[IPartHost](host, "me_exportbus") with NamedBlock with PartItemBusBase[PartExportBus] { override def preferredName = "me_exportbus" override def priority = 2 diff --git a/src/main/scala/li/cil/oc/integration/appeng/DriverImportBus.scala b/src/main/scala/li/cil/oc/integration/appeng/DriverImportBus.scala index 188ecccc9d..c64daf780a 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/DriverImportBus.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/DriverImportBus.scala @@ -13,6 +13,8 @@ import net.minecraft.item.ItemStack import net.minecraft.world.World import net.minecraftforge.common.util.ForgeDirection +import scala.reflect.ClassTag + object DriverImportBus extends driver.SidedBlock { override def worksWith(world: World, x: Int, y: Int, z: Int, side: ForgeDirection) = world.getTileEntity(x, y, z) match { @@ -24,7 +26,7 @@ object DriverImportBus extends driver.SidedBlock { override def createEnvironment(world: World, x: Int, y: Int, z: Int, side: ForgeDirection) = new Environment(world.getTileEntity(x, y, z).asInstanceOf[IPartHost]) - final class Environment(val host: IPartHost) extends ManagedTileEntityEnvironment[IPartHost](host, "me_importbus") with NamedBlock with PartItemBusBase[PartImportBus] { + final class Environment(val host: IPartHost)(implicit val tag: ClassTag[PartImportBus]) extends ManagedTileEntityEnvironment[IPartHost](host, "me_importbus") with NamedBlock with PartItemBusBase[PartImportBus] { override def preferredName = "me_importbus" override def priority = 1 diff --git a/src/main/scala/li/cil/oc/integration/appeng/DriverPartInterface.scala b/src/main/scala/li/cil/oc/integration/appeng/DriverPartInterface.scala index 3c65f94d96..550767e81d 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/DriverPartInterface.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/DriverPartInterface.scala @@ -6,13 +6,15 @@ import li.cil.oc.api.driver import li.cil.oc.api.driver.{EnvironmentProvider, NamedBlock} import li.cil.oc.api.machine.{Arguments, Callback, Context} import li.cil.oc.integration.ManagedTileEntityEnvironment -import li.cil.oc.integration.appeng.internal.{PartInterfaceEnvironmentAE2, PartPatternEnvironment} +import li.cil.oc.integration.appeng.internal.{PartInterfaceEnvironment, PartPatternEnvironment} 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.reflect.ClassTag + object DriverPartInterface extends driver.SidedBlock { override def worksWith(world: World, x: Int, y: Int, z: Int, side: ForgeDirection) = world.getTileEntity(x, y, z) match { @@ -24,7 +26,7 @@ object DriverPartInterface extends driver.SidedBlock { override def createEnvironment(world: World, x: Int, y: Int, z: Int, side: ForgeDirection) = new Environment(world.getTileEntity(x, y, z).asInstanceOf[IPartHost]) - final class Environment(val host: IPartHost) extends ManagedTileEntityEnvironment[IPartHost](host, "me_interface") with NamedBlock with PartInterfaceEnvironmentAE2 with PartPatternEnvironment[PartInterface] { + final class Environment(val host: IPartHost)(implicit val tag: ClassTag[PartInterface]) extends ManagedTileEntityEnvironment[IPartHost](host, "me_interface") with NamedBlock with PartInterfaceEnvironment[PartInterface] with PartPatternEnvironment[PartInterface] { override def preferredName = "me_interface" override def priority = 0 @@ -38,7 +40,7 @@ object DriverPartInterface extends driver.SidedBlock { @Callback(doc = "function([slot:number]):table -- Get the given pattern in the interface.") def getInterfacePattern(context: Context, args: Arguments): Array[AnyRef] = { val inv = getPatternInventory(context, args) - val slot = args.optSlot(inv, 0, 0) + val slot = args.optSlot(inv, 1, 0) val stack = inv.getStackInSlot(slot) result(stack) } @@ -59,6 +61,8 @@ object DriverPartInterface extends driver.SidedBlock { @Callback(doc = "function(slot:number, index:number, database:address, entry:number):boolean -- Store pattern output at the given index to the database entry.") def storeInterfacePatternOutput(context: Context, args: Arguments): Array[AnyRef] = storeInterfacePattern(context, args, "out") + + override def offset: Int = 1 } object Provider extends EnvironmentProvider { diff --git a/src/main/scala/li/cil/oc/integration/appeng/internal/InterfaceEnvironmentBase.scala b/src/main/scala/li/cil/oc/integration/appeng/internal/InterfaceEnvironmentBase.scala index 0ad1f5226a..e8ecd3471d 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/internal/InterfaceEnvironmentBase.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/internal/InterfaceEnvironmentBase.scala @@ -1,7 +1,7 @@ package li.cil.oc.integration.appeng.internal import appeng.api.implementations.tiles.ISegmentedInventory -import appeng.api.parts.{IPart, IPartHost} +import appeng.api.parts.IPart import li.cil.oc.api.machine.{Arguments, Context} import li.cil.oc.api.network.{ManagedEnvironment, Node} import li.cil.oc.integration.vanilla.ConverterItemStack @@ -11,96 +11,49 @@ import li.cil.oc.util.ResultWrapper.result import net.minecraft.inventory.IInventory import net.minecraft.item.ItemStack -trait InterfaceEnvironmentBase[V] extends ManagedEnvironment { - def getConfig(context: Context, args: Arguments): Array[AnyRef] - - def setConfig(context: Context, args: Arguments): Array[AnyRef] - - protected def getConfigValue(args: Arguments, offset: Int): V -} - -trait PartInterfaceEnvironmentBase[V] extends InterfaceEnvironmentBase[V] { - def host: IPartHost - - override def getConfig(context: Context, args: Arguments): Array[AnyRef] = { - val side = args.checkSideAny(0) - val slot = args.optInteger(1, 0) - result(readFromPart(host.getPart(side), slot)) - } - - protected def readFromPart(part: IPart, slot: Int): AnyRef - - override def setConfig(context: Context, args: Arguments): Array[AnyRef] = { - val side = args.checkSideAny(0) - val (slot, offset) = if (args.isInteger(1)) (args.checkInteger(1), 2) else (0, 1) - val stack = getConfigValue(args, offset) - result(setToPart(host.getPart(side), slot, stack)) - } - - protected def setToPart(part: IPart, slot: Int, stack: V): AnyRef -} - -trait BlockInterfaceEnvironmentBase[V] extends InterfaceEnvironmentBase[V] { - def tile: AnyRef - - override def getConfig(context: Context, args: Arguments): Array[AnyRef] = { - val slot = args.optInteger(0, 0) - result(readFromTile(slot)) - } - - protected def readFromTile(slot: Int): AnyRef - - override def setConfig(context: Context, args: Arguments): Array[AnyRef] = { - val (slot, offset) = if (args.isInteger(0)) (args.checkInteger(0), 1) else (0, 0) - val stack = getConfigValue(args, offset) - result(setToTile(slot, stack)) - } - - protected def setToTile(slot: Int, stack: V): AnyRef -} - -trait AE2InterfaceUtils { +trait InterfaceEnvironmentBase extends ManagedEnvironment { protected def getConfigInventory(inv: ISegmentedInventory): IInventory = inv.getInventoryByName("config") - protected def getConfigValue(node: Node, args: Arguments, offset: Int): ItemStack = + private def getConfigValue(node: Node, args: Arguments, offset: Int): ItemStack = if (args.isTable(offset)) ConverterItemStack.parse(args.checkTable(offset)) else DatabaseAccess.getStackFromDatabase(node, args, offset) -} -trait PartInterfaceEnvironmentAE2 extends PartInterfaceEnvironmentBase[ItemStack] with AE2InterfaceUtils { - private def getConfigInventory(part: IPart): IInventory = part match { - case inv: ISegmentedInventory => getConfigInventory(inv: ISegmentedInventory) - case _ => throw new IllegalArgumentException("no matching part") - } + protected def getConfigInventory(context: Context, args: Arguments): IInventory - override def readFromPart(part: IPart, slot: Int): AnyRef = { - getConfigInventory(part).getStackInSlot(slot) - } + protected def offset: Int = 0 - override def getConfigValue(args: Arguments, offset: Int): ItemStack = getConfigValue(node, args, offset) + def getConfig(context: Context, args: Arguments): Array[AnyRef] = { + val inv = getConfigInventory(context, args) + val slot = args.optInteger(offset, 0) + result(inv.getStackInSlot(slot)) + } - override def setToPart(part: IPart, slot: Int, stack: ItemStack): AnyRef = { - getConfigInventory(part).setInventorySlotContents(slot, stack) + def setConfig(context: Context, args: Arguments): Array[AnyRef] = { + val inv = getConfigInventory(context, args) + val (slot, valOffset) = if (args.isInteger(offset)) (args.checkInteger(offset), offset + 1) else (0, offset) + val stack = getConfigValue(node, args, valOffset) + inv.setInventorySlotContents(slot, stack) result(true) } } -trait BlockInterfaceEnvironmentAE2 extends BlockInterfaceEnvironmentBase[ItemStack] with AE2InterfaceUtils { - override def tile: ISegmentedInventory +trait PartInterfaceEnvironment[PartType <: IPart] extends InterfaceEnvironmentBase with PartEnvironmentBase[PartType] { + override def offset: Int = 1 - private def getConfigInventory: IInventory = getConfigInventory(tile) - - override def readFromTile(slot: Int): AnyRef = { - getConfigInventory.getStackInSlot(slot) + override protected def getConfigInventory(context: Context, args: Arguments): IInventory = { + val side = args.checkSideAny(0) + getPart(side) match { + case inv: ISegmentedInventory => getConfigInventory(inv) + case _ => throw new IllegalArgumentException("no matching part") + } } +} - override def getConfigValue(args: Arguments, offset: Int): ItemStack = getConfigValue(node, args, offset) +trait BlockInterfaceEnvironment extends InterfaceEnvironmentBase { + def tile: ISegmentedInventory - override def setToTile(slot: Int, stack: ItemStack): AnyRef = { - getConfigInventory.setInventorySlotContents(slot, stack) - result(true) - } + override protected def getConfigInventory(context: Context, args: Arguments): IInventory = getConfigInventory(tile) } \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/integration/appeng/internal/PartEnvironmentBase.scala b/src/main/scala/li/cil/oc/integration/appeng/internal/PartEnvironmentBase.scala index 607ea67abf..cc0b7be9dc 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/internal/PartEnvironmentBase.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/internal/PartEnvironmentBase.scala @@ -1,7 +1,7 @@ package li.cil.oc.integration.appeng.internal import appeng.api.config.Upgrades -import appeng.api.parts.IPartHost +import appeng.api.parts.{IPart, IPartHost} import appeng.api.storage.StorageName import appeng.api.storage.data.{IAEItemStack, IAEStack} import appeng.parts.automation.PartSharedItemBus @@ -16,7 +16,9 @@ import net.minecraftforge.common.util.ForgeDirection import scala.reflect.ClassTag -trait PartEnvironmentBase[PartType <: IIAEStackInventory] extends ManagedEnvironment { +trait PartEnvironmentBase[PartType <: IPart] extends ManagedEnvironment { + implicit def tag: ClassTag[PartType] + def host: IPartHost def getPart(side: ForgeDirection): PartType = { @@ -25,6 +27,10 @@ trait PartEnvironmentBase[PartType <: IIAEStackInventory] extends ManagedEnviron case _ => throw new IllegalArgumentException("no matching part") } } +} + +trait PartConfigurable[PartType <: IIAEStackInventory with IPart] extends PartEnvironmentBase[PartType] { + implicit def tag: ClassTag[PartType] private def getConfigAndValidate(part: PartType, slot: Int) = { val config = part.getAEInventoryByName(StorageName.CONFIG) @@ -46,21 +52,26 @@ trait PartEnvironmentBase[PartType <: IIAEStackInventory] extends ManagedEnviron getPartConfig(part, args.optInteger(1, 0)) } + // NOTE: Setting a config to null won't sync to the client properly. + // Because updateVirtualSlots currently skips null entries during init, so the client might still show an item that was actually cleared on the server. def setPartConfig(part: PartType, slot: Int, stack: IAEStack[_]): Unit = { val config = getConfigAndValidate(part, slot) config.putAEStackInSlot(slot, stack) } - def setPartConfig[T <: IAEStack[T]: ClassTag](context: Context, args: Arguments): Unit = { + def setPartConfig[T <: IAEStack[T] : ClassTag](context: Context, args: Arguments): Unit = { val side = args.checkSideAny(0) val part = getPart(side) val (slot, offset) = if (args.isInteger(1)) (args.checkInteger(1), 2) else (0, 1) - val stack = AEStackFactory.parse[T](args.checkTable(offset)) + val stack: T = if (args.count() <= offset) null.asInstanceOf[T] + else AEStackFactory.parse[T](args.checkTable(offset)) setPartConfig(part, slot, stack) } } -trait PartSharedItemBusBase[PartType <: PartSharedItemBus[_]] extends PartEnvironmentBase[PartType] { +trait PartSharedItemBusBase[PartType <: PartSharedItemBus[_]] extends PartConfigurable[PartType] { + implicit def tag: ClassTag[PartType] + def getSlotSize(part: PartType): Int = Math.min(1 + part.getInstalledUpgrades(Upgrades.CAPACITY) * 4, part.getAEInventoryByName(StorageName.CONFIG).getSizeInventory) @@ -71,9 +82,11 @@ trait PartSharedItemBusBase[PartType <: PartSharedItemBus[_]] extends PartEnviro } } -trait PartItemBusBase[PartType <: PartSharedItemBus[_]] extends PartSharedItemBusBase[PartType]{ +trait PartItemBusBase[PartType <: PartSharedItemBus[_]] extends PartSharedItemBusBase[PartType] { + implicit def tag: ClassTag[PartType] + // function(side:number[, slot:number][, database:address, entry:number[, size:number]]):boolean - override def setPartConfig[T <: IAEStack[T]: ClassTag](context: Context, args: Arguments): Unit = { + override def setPartConfig[T <: IAEStack[T] : ClassTag](context: Context, args: Arguments): Unit = { val side = args.checkSideAny(0) val part = getPart(side) val (slot, offset) = if (args.isInteger(1)) (args.checkInteger(1), 2) else (0, 1) 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 37bc2f4aca..704ba66b90 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 @@ -1,7 +1,7 @@ package li.cil.oc.integration.appeng.internal import appeng.api.implementations.tiles.ISegmentedInventory -import appeng.api.parts.IPartHost +import appeng.api.parts.IPart import appeng.api.storage.data.IAEStack import appeng.tile.misc.TileInterface import appeng.util.Platform @@ -16,12 +16,11 @@ import net.minecraft.inventory.IInventory import net.minecraft.item.ItemStack import net.minecraft.nbt.NBTTagCompound import net.minecraftforge.common.util.Constants.NBT -import net.minecraftforge.common.util.ForgeDirection trait PatternEnvironment extends ManagedEnvironment { def getPatternInventory(context: Context, args: Arguments): IInventory - def offset: Int = 0 + protected def offset: Int = 0 private def getPatternNBT(context: Context, args: Arguments, tag: String) = { val inv = getPatternInventory(context, args) @@ -89,18 +88,9 @@ trait BlockPatternEnvironment extends PatternEnvironment { } } -trait PartPatternEnvironment[PartType <: ISegmentedInventory] extends PatternEnvironment { - def host: IPartHost - +trait PartPatternEnvironment[PartType <: ISegmentedInventory with IPart] extends PatternEnvironment with PartEnvironmentBase[PartType] { override def offset: Int = 1 - def getPart(side: ForgeDirection): PartType = { - host.getPart(side) match { - case part: PartType => part - case _ => throw new IllegalArgumentException("no matching part") - } - } - override def getPatternInventory(context: Context, args: Arguments): IInventory = { val side = args.checkSideAny(0) getPart(side).getInventoryByName("patterns") diff --git a/src/main/scala/li/cil/oc/integration/thaumicenergistics/DriverEssentiaExportBus.scala b/src/main/scala/li/cil/oc/integration/thaumicenergistics/DriverEssentiaExportBus.scala index f1878c0bbe..c2e62e25e8 100644 --- a/src/main/scala/li/cil/oc/integration/thaumicenergistics/DriverEssentiaExportBus.scala +++ b/src/main/scala/li/cil/oc/integration/thaumicenergistics/DriverEssentiaExportBus.scala @@ -16,6 +16,8 @@ import thaumicenergistics.api.ThEApi import thaumicenergistics.common.parts.PartEssentiaExportBus import thaumicenergistics.common.storage.AEEssentiaStack +import scala.reflect.{ClassTag, classTag} + object DriverEssentiaExportBus extends driver.SidedBlock { override def worksWith(world: World, x: Int, y: Int, z: Int, side: ForgeDirection) = world.getTileEntity(x, y, z) match { @@ -27,7 +29,7 @@ object DriverEssentiaExportBus extends driver.SidedBlock { override def createEnvironment(world: World, x: Int, y: Int, z: Int, side: ForgeDirection) = new Environment(world, world.getTileEntity(x, y, z).asInstanceOf[IPartHost]) - final class Environment(val world: World, val host: IPartHost) extends ManagedTileEntityEnvironment[IPartHost](host, "essentia_exportbus") with NamedBlock with PartSharedItemBusBase[PartEssentiaExportBus] { + final class Environment(val world: World, val host: IPartHost)(implicit val tag: ClassTag[PartEssentiaExportBus]) extends ManagedTileEntityEnvironment[IPartHost](host, "essentia_exportbus") with NamedBlock with PartSharedItemBusBase[PartEssentiaExportBus] { override def preferredName = "essentia_exportbus" override def priority = 2 diff --git a/src/main/scala/li/cil/oc/integration/thaumicenergistics/DriverEssentiaImportBus.scala b/src/main/scala/li/cil/oc/integration/thaumicenergistics/DriverEssentiaImportBus.scala index e2525a2cfe..a662917675 100644 --- a/src/main/scala/li/cil/oc/integration/thaumicenergistics/DriverEssentiaImportBus.scala +++ b/src/main/scala/li/cil/oc/integration/thaumicenergistics/DriverEssentiaImportBus.scala @@ -15,6 +15,8 @@ import thaumicenergistics.api.ThEApi import thaumicenergistics.common.parts.PartEssentiaImportBus import thaumicenergistics.common.storage.AEEssentiaStack +import scala.reflect.{ClassTag, classTag} + object DriverEssentiaImportBus extends driver.SidedBlock { override def worksWith(world: World, x: Int, y: Int, z: Int, side: ForgeDirection) = world.getTileEntity(x, y, z) match { @@ -26,7 +28,7 @@ object DriverEssentiaImportBus extends driver.SidedBlock { override def createEnvironment(world: World, x: Int, y: Int, z: Int, side: ForgeDirection) = new Environment(world, world.getTileEntity(x, y, z).asInstanceOf[IPartHost]) - final class Environment(val world: World, val host: IPartHost) extends ManagedTileEntityEnvironment[IPartHost](host, "essentia_importbus") with NamedBlock with PartSharedItemBusBase[PartEssentiaImportBus] { + final class Environment(val world: World, val host: IPartHost)(implicit val tag: ClassTag[PartEssentiaImportBus]) extends ManagedTileEntityEnvironment[IPartHost](host, "essentia_importbus") with NamedBlock with PartSharedItemBusBase[PartEssentiaImportBus] { override def preferredName = "essentia_importbus" override def priority = 2 From 5e48c6f4ad563c2a4e975abc401750834718f196 Mon Sep 17 00:00:00 2001 From: hinyb <40139991+hinyb@users.noreply.github.com> Date: Tue, 17 Feb 2026 06:06:31 +0800 Subject: [PATCH 10/11] feat: add support for AE2FC blocks. --- .../cil/oc/integration/ae2fc/Ae2FcUtil.scala | 15 +++++ .../ae2fc/ConverterFluidPacket.scala | 17 ++++++ .../ae2fc/DriverBlockFluidInterface.scala | 50 ++++++++++++++++ .../ae2fc/DriverFluidExportBus.scala | 54 +++++++++++++++++ .../ae2fc/DriverFluidImportBus.scala | 54 +++++++++++++++++ .../ae2fc/DriverPartFluidInterface.scala | 60 +++++++++++++++++++ .../cil/oc/integration/ae2fc/ModAe2fc.scala | 13 +++- 7 files changed, 262 insertions(+), 1 deletion(-) create mode 100644 src/main/scala/li/cil/oc/integration/ae2fc/ConverterFluidPacket.scala create mode 100644 src/main/scala/li/cil/oc/integration/ae2fc/DriverBlockFluidInterface.scala create mode 100644 src/main/scala/li/cil/oc/integration/ae2fc/DriverFluidExportBus.scala create mode 100644 src/main/scala/li/cil/oc/integration/ae2fc/DriverFluidImportBus.scala create mode 100644 src/main/scala/li/cil/oc/integration/ae2fc/DriverPartFluidInterface.scala diff --git a/src/main/scala/li/cil/oc/integration/ae2fc/Ae2FcUtil.scala b/src/main/scala/li/cil/oc/integration/ae2fc/Ae2FcUtil.scala index 1b54b0250a..2299ff20b2 100644 --- a/src/main/scala/li/cil/oc/integration/ae2fc/Ae2FcUtil.scala +++ b/src/main/scala/li/cil/oc/integration/ae2fc/Ae2FcUtil.scala @@ -2,7 +2,22 @@ package li.cil.oc.integration.ae2fc import appeng.api.storage.data.IAEFluidStack import com.glodblock.github.api.FluidCraftAPI +import com.glodblock.github.loader.ItemAndBlockHolder +import net.minecraft.block.Block +import net.minecraft.item.ItemStack object Ae2FcUtil { def canSeeFluidInNetwork(fluid: IAEFluidStack) = fluid != null && fluid.getFluid != null && !FluidCraftAPI.instance().isBlacklistedInDisplay(fluid.getFluid.getClass) + + def isFluidExportBus(stack: ItemStack): Boolean = + stack != null && stack.getItem == ItemAndBlockHolder.FLUID_EXPORT_BUS + + def isFluidImportBus(stack: ItemStack): Boolean = + stack != null && stack.getItem == ItemAndBlockHolder.FLUID_IMPORT_BUS + + def isFluidInterface(stack: ItemStack): Boolean = + stack != null && Block.getBlockFromItem(stack.getItem) == ItemAndBlockHolder.INTERFACE + + def isPartFluidInterface(stack: ItemStack): Boolean = + stack != null && stack.getItem == ItemAndBlockHolder.FLUID_INTERFACE } diff --git a/src/main/scala/li/cil/oc/integration/ae2fc/ConverterFluidPacket.scala b/src/main/scala/li/cil/oc/integration/ae2fc/ConverterFluidPacket.scala new file mode 100644 index 0000000000..67c86fe05d --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/ae2fc/ConverterFluidPacket.scala @@ -0,0 +1,17 @@ +package li.cil.oc.integration.ae2fc + +import com.glodblock.github.common.item.ItemFluidPacket +import li.cil.oc.api.driver.Converter +import net.minecraft.item.ItemStack + +import java.util +import scala.collection.convert.WrapAsScala._ + +object ConverterFluidPacket extends Converter { + + override def convert(value: Any, output: util.Map[AnyRef, AnyRef]): Unit = value match { + case stack: ItemStack if stack.getItem.isInstanceOf[ItemFluidPacket] => + output += "fluidPacket" -> ItemFluidPacket.getFluidStack(stack) + case _ => + } +} diff --git a/src/main/scala/li/cil/oc/integration/ae2fc/DriverBlockFluidInterface.scala b/src/main/scala/li/cil/oc/integration/ae2fc/DriverBlockFluidInterface.scala new file mode 100644 index 0000000000..fc8fe8d10e --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/ae2fc/DriverBlockFluidInterface.scala @@ -0,0 +1,50 @@ +package li.cil.oc.integration.ae2fc + +import appeng.api.storage.data.IAEFluidStack +import com.glodblock.github.common.tile.TileFluidInterface +import li.cil.oc.api.driver.{EnvironmentProvider, NamedBlock} +import li.cil.oc.api.machine.{Arguments, Callback, Context} +import li.cil.oc.api.network.ManagedEnvironment +import li.cil.oc.api.prefab.DriverSidedTileEntity +import li.cil.oc.integration.ManagedTileEntityEnvironment +import li.cil.oc.integration.appeng.AEStackFactory +import li.cil.oc.util.ResultWrapper._ +import net.minecraft.item.ItemStack +import net.minecraft.world.World +import net.minecraftforge.common.util.ForgeDirection + +object DriverBlockFluidInterface extends DriverSidedTileEntity { + def getTileEntityClass: Class[_] = classOf[TileFluidInterface] + + def createEnvironment(world: World, x: Int, y: Int, z: Int, side: ForgeDirection): ManagedEnvironment = + new Environment(world.getTileEntity(x, y, z).asInstanceOf[TileFluidInterface]) + + final class Environment(val tile: TileFluidInterface) extends ManagedTileEntityEnvironment[TileFluidInterface](tile, "fluid_interface") with NamedBlock { + + override def preferredName = "fluid_interface" + + override def priority = 6 + + @Callback(doc = "function([slot:number]):table -- Get the configuration of the fluid interface.") + def getFluidInterfaceConfiguration(context: Context, args: Arguments): Array[AnyRef] = { + val slot = args.optInteger(0, 0) + result(tile.getConfig.getStackInSlot(slot)) + } + + @Callback(doc = "function([slot:number][, detail:table]):boolean -- Configure the fluid interface.") + def setFluidInterfaceConfiguration(context: Context, args: Arguments): Array[AnyRef] = { + val (slot, offset) = if (args.isInteger(0)) (args.checkInteger(0), 1) else (0, 0) + val stack = if (args.count() <= offset) null.asInstanceOf[IAEFluidStack] + else AEStackFactory.parse[IAEFluidStack](args.checkTable(offset)) + tile.setConfig(slot, stack) + result(true) + } + } + + object Provider extends EnvironmentProvider { + override def getEnvironment(stack: ItemStack): Class[_] = + if (Ae2FcUtil.isFluidInterface(stack)) + classOf[Environment] + else null + } +} diff --git a/src/main/scala/li/cil/oc/integration/ae2fc/DriverFluidExportBus.scala b/src/main/scala/li/cil/oc/integration/ae2fc/DriverFluidExportBus.scala new file mode 100644 index 0000000000..9a0b92475b --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/ae2fc/DriverFluidExportBus.scala @@ -0,0 +1,54 @@ +package li.cil.oc.integration.ae2fc + +import appeng.api.parts.IPartHost +import appeng.api.storage.data.IAEItemStack +import com.glodblock.github.common.parts.PartFluidExportBus +import li.cil.oc.api.driver +import li.cil.oc.api.driver.{EnvironmentProvider, NamedBlock} +import li.cil.oc.api.machine.{Arguments, Callback, Context} +import li.cil.oc.integration.ManagedTileEntityEnvironment +import li.cil.oc.integration.appeng.internal.PartSharedItemBusBase +import li.cil.oc.util.ResultWrapper._ +import net.minecraft.item.ItemStack +import net.minecraft.world.World +import net.minecraftforge.common.util.ForgeDirection + +import scala.reflect.ClassTag + +object DriverFluidExportBus 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).filter(obj => { + obj != null + }).exists(_.isInstanceOf[PartFluidExportBus]) + case _ => false + } + + override def createEnvironment(world: World, x: Int, y: Int, z: Int, side: ForgeDirection) = new Environment(world.getTileEntity(x, y, z).asInstanceOf[IPartHost]) + + final class Environment(val host: IPartHost)(implicit val tag: ClassTag[PartFluidExportBus]) extends ManagedTileEntityEnvironment[IPartHost](host, "fluid_exportbus") with NamedBlock with PartSharedItemBusBase[PartFluidExportBus] { + + override def preferredName = "fluid_exportbus" + + override def priority = 2 + + @Callback(doc = "function(side:number, [ slot:number]):boolean -- Get the configuration of the export bus pointing in the specified direction.") + def getExportConfiguration(context: Context, args: Arguments): Array[AnyRef] = result(getPartConfig(context, args)) + + @Callback(doc = "function(side:number[, slot:number][, database:address, entry:number]):boolean OR function(side:number[, slot:number][, detail: table):boolean -- Configure the export bus pointing in the specified direction to export item stacks matching the specified descriptor.") + def setExportConfiguration(context: Context, args: Arguments): Array[AnyRef] = { + setPartConfig[IAEItemStack](context, args) + result(true) + } + + @Callback(doc = "function(side:number):number -- Get the number of valid slots in this export bus.") + def getExportSlotSize(context: Context, args: Arguments): Array[AnyRef] = result(getSlotSize(context, args)) + } + + object Provider extends EnvironmentProvider { + override def getEnvironment(stack: ItemStack): Class[_] = + if (Ae2FcUtil.isFluidExportBus(stack)) + classOf[Environment] + else null + } +} diff --git a/src/main/scala/li/cil/oc/integration/ae2fc/DriverFluidImportBus.scala b/src/main/scala/li/cil/oc/integration/ae2fc/DriverFluidImportBus.scala new file mode 100644 index 0000000000..edb971ca05 --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/ae2fc/DriverFluidImportBus.scala @@ -0,0 +1,54 @@ +package li.cil.oc.integration.ae2fc + +import appeng.api.parts.IPartHost +import appeng.api.storage.data.IAEFluidStack +import com.glodblock.github.common.parts.PartFluidImportBus +import li.cil.oc.api.driver +import li.cil.oc.api.driver.{EnvironmentProvider, NamedBlock} +import li.cil.oc.api.machine.{Arguments, Callback, Context} +import li.cil.oc.integration.ManagedTileEntityEnvironment +import li.cil.oc.integration.appeng.internal.PartSharedItemBusBase +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.reflect.ClassTag + +object DriverFluidImportBus extends driver.SidedBlock { + override def worksWith(world: World, x: Int, y: Int, z: Int, side: ForgeDirection) = + world.getTileEntity(x, y, z) match { + case container: IPartHost => ForgeDirection.VALID_DIRECTIONS.map(container.getPart).filter(obj => { + obj != null + }).exists(_.isInstanceOf[PartFluidImportBus]) + case _ => false + } + + override def createEnvironment(world: World, x: Int, y: Int, z: Int, side: ForgeDirection) = new Environment(world.getTileEntity(x, y, z).asInstanceOf[IPartHost]) + + final class Environment(val host: IPartHost)(implicit val tag: ClassTag[PartFluidImportBus]) extends ManagedTileEntityEnvironment[IPartHost](host, "fluid_importbus") with NamedBlock with PartSharedItemBusBase[PartFluidImportBus] { + override def preferredName = "fluid_importbus" + + override def priority = 1 + + @Callback(doc = "function(side:number[, slot:number]):boolean -- Get the configuration of the import bus pointing in the specified direction.") + def getImportConfiguration(context: Context, args: Arguments): Array[AnyRef] = result(getPartConfig(context, args)) + + @Callback(doc = "function(side:number[, slot:number][, database:address, entry:number]):boolean OR function(side:number[, slot:number][, detail:table]):boolean -- Configure the import bus pointing in the specified direction to import item stacks matching the specified descriptor.") + def setImportConfiguration(context: Context, args: Arguments): Array[AnyRef] = { + setPartConfig[IAEFluidStack](context, args) + result(true) + } + + @Callback(doc = "function(side:number):number -- Get the number of valid slots in this import bus.") + def getImportSlotSize(context: Context, args: Arguments): Array[AnyRef] = result(getSlotSize(context, args)) + } + + object Provider extends EnvironmentProvider { + override def getEnvironment(stack: ItemStack): Class[_] = + if (Ae2FcUtil.isFluidImportBus(stack)) + classOf[Environment] + else null + } + +} diff --git a/src/main/scala/li/cil/oc/integration/ae2fc/DriverPartFluidInterface.scala b/src/main/scala/li/cil/oc/integration/ae2fc/DriverPartFluidInterface.scala new file mode 100644 index 0000000000..170fc8ea6c --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/ae2fc/DriverPartFluidInterface.scala @@ -0,0 +1,60 @@ +package li.cil.oc.integration.ae2fc + +import appeng.api.parts.IPartHost +import appeng.api.storage.data.IAEFluidStack +import com.glodblock.github.common.parts.PartFluidInterface +import li.cil.oc.api.driver +import li.cil.oc.api.driver.{EnvironmentProvider, NamedBlock} +import li.cil.oc.api.machine.{Arguments, Callback, Context} +import li.cil.oc.integration.ManagedTileEntityEnvironment +import li.cil.oc.integration.appeng.AEStackFactory +import li.cil.oc.integration.appeng.internal.PartInterfaceEnvironment +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.reflect.ClassTag + +object DriverPartFluidInterface extends driver.SidedBlock { + override def worksWith(world: World, x: Int, y: Int, z: Int, side: ForgeDirection) = + world.getTileEntity(x, y, z) match { + case container: IPartHost => ForgeDirection.VALID_DIRECTIONS.map(container.getPart).filter(obj => { + obj != null + }).exists(_.isInstanceOf[PartFluidInterface]) + case _ => false + } + + override def createEnvironment(world: World, x: Int, y: Int, z: Int, side: ForgeDirection) = new Environment(world.getTileEntity(x, y, z).asInstanceOf[IPartHost]) + + final class Environment(val host: IPartHost)(implicit val tag: ClassTag[PartFluidInterface]) extends ManagedTileEntityEnvironment[IPartHost](host, "fluid_interface") with NamedBlock with PartInterfaceEnvironment[PartFluidInterface] { + override def preferredName = "fluid_interface" + + override def priority = 6 + + @Callback(doc = "function(side:number[, slot:number]):table -- Get the configuration of the fluid interface.") + def getFluidInterfaceConfiguration(context: Context, args: Arguments): Array[AnyRef] = { + val side = args.checkSideAny(0) + val slot = args.optInteger(1, 0) + result(getPart(side).getConfig.getStackInSlot(slot)) + } + + @Callback(doc = "function(side:number[, slot:number][, detail:table]):boolean -- Configure the fluid interface.") + def setFluidInterfaceConfiguration(context: Context, args: Arguments): Array[AnyRef] = { + val side = args.checkSideAny(0) + val (slot, offset) = if (args.isInteger(1)) (args.checkInteger(1), 2) else (0, 1) + val stack = if (args.count() <= offset) null.asInstanceOf[IAEFluidStack] + else AEStackFactory.parse[IAEFluidStack](args.checkTable(offset)) + getPart(side).setConfig(slot, stack) + result(true) + } + } + + object Provider extends EnvironmentProvider { + override def getEnvironment(stack: ItemStack): Class[_] = + if (Ae2FcUtil.isPartFluidInterface(stack)) + classOf[Environment] + else null + } +} \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/integration/ae2fc/ModAe2fc.scala b/src/main/scala/li/cil/oc/integration/ae2fc/ModAe2fc.scala index 4f627850af..4d1a3ee0bd 100644 --- a/src/main/scala/li/cil/oc/integration/ae2fc/ModAe2fc.scala +++ b/src/main/scala/li/cil/oc/integration/ae2fc/ModAe2fc.scala @@ -3,10 +3,21 @@ package li.cil.oc.integration.ae2fc import li.cil.oc.api.Driver import li.cil.oc.integration.{Mod, ModProxy, Mods} -object ModAe2fc extends ModProxy { +object ModAe2fc extends ModProxy { override def getMod: Mod = Mods.Ae2Fc + override def initialize(): Unit = { Driver.add(ConverterFluidDrop) + Driver.add(ConverterFluidPacket) Driver.add(new ConverterFluidCellInventory) + + Driver.add(DriverBlockFluidInterface) + Driver.add(DriverBlockFluidInterface.Provider) + Driver.add(DriverPartFluidInterface) + Driver.add(DriverPartFluidInterface.Provider) + Driver.add(DriverFluidExportBus) + Driver.add(DriverFluidExportBus.Provider) + Driver.add(DriverFluidImportBus) + Driver.add(DriverFluidImportBus.Provider) } } From edc9daef0a4aaca8645e1f4375a30488ba3771ea Mon Sep 17 00:00:00 2001 From: hinyb <40139991+hinyb@users.noreply.github.com> Date: Wed, 18 Feb 2026 04:43:55 +0800 Subject: [PATCH 11/11] feat: add support for ore filter and storage bus. --- .../cil/oc/integration/ae2fc/Ae2FcUtil.scala | 3 + .../ae2fc/DriverFluidExportBus.scala | 16 ++- .../ae2fc/DriverFluidImportBus.scala | 16 ++- .../ae2fc/DriverFluidStorageBus.scala | 55 ++++++++ .../cil/oc/integration/ae2fc/ModAe2fc.scala | 2 + .../li/cil/oc/integration/appeng/AEUtil.scala | 4 + .../integration/appeng/DriverExportBus.scala | 15 +- .../integration/appeng/DriverImportBus.scala | 16 ++- .../integration/appeng/DriverStorageBus.scala | 56 ++++++++ .../cil/oc/integration/appeng/ModAppEng.scala | 2 + .../appeng/internal/PartEnvironmentBase.scala | 130 ++++++++++++------ .../DriverEssentiaExportBus.scala | 18 +-- .../DriverEssentiaImportBus.scala | 19 +-- .../DriverEssentiaStorageBus.scala | 56 ++++++++ .../ModThaumicEnergistics.scala | 4 +- .../ThaumicEnergisticsUtils.scala | 7 - 16 files changed, 327 insertions(+), 92 deletions(-) create mode 100644 src/main/scala/li/cil/oc/integration/ae2fc/DriverFluidStorageBus.scala create mode 100644 src/main/scala/li/cil/oc/integration/appeng/DriverStorageBus.scala create mode 100644 src/main/scala/li/cil/oc/integration/thaumicenergistics/DriverEssentiaStorageBus.scala diff --git a/src/main/scala/li/cil/oc/integration/ae2fc/Ae2FcUtil.scala b/src/main/scala/li/cil/oc/integration/ae2fc/Ae2FcUtil.scala index 2299ff20b2..5514cd933b 100644 --- a/src/main/scala/li/cil/oc/integration/ae2fc/Ae2FcUtil.scala +++ b/src/main/scala/li/cil/oc/integration/ae2fc/Ae2FcUtil.scala @@ -20,4 +20,7 @@ object Ae2FcUtil { def isPartFluidInterface(stack: ItemStack): Boolean = stack != null && stack.getItem == ItemAndBlockHolder.FLUID_INTERFACE + + def isFluidStorageBus(stack: ItemStack): Boolean = + stack != null && stack.getItem == ItemAndBlockHolder.FLUID_STORAGE_BUS } diff --git a/src/main/scala/li/cil/oc/integration/ae2fc/DriverFluidExportBus.scala b/src/main/scala/li/cil/oc/integration/ae2fc/DriverFluidExportBus.scala index 9a0b92475b..80fef1ef3a 100644 --- a/src/main/scala/li/cil/oc/integration/ae2fc/DriverFluidExportBus.scala +++ b/src/main/scala/li/cil/oc/integration/ae2fc/DriverFluidExportBus.scala @@ -8,7 +8,6 @@ import li.cil.oc.api.driver.{EnvironmentProvider, NamedBlock} import li.cil.oc.api.machine.{Arguments, Callback, Context} import li.cil.oc.integration.ManagedTileEntityEnvironment import li.cil.oc.integration.appeng.internal.PartSharedItemBusBase -import li.cil.oc.util.ResultWrapper._ import net.minecraft.item.ItemStack import net.minecraft.world.World import net.minecraftforge.common.util.ForgeDirection @@ -33,16 +32,19 @@ object DriverFluidExportBus extends driver.SidedBlock { override def priority = 2 @Callback(doc = "function(side:number, [ slot:number]):boolean -- Get the configuration of the export bus pointing in the specified direction.") - def getExportConfiguration(context: Context, args: Arguments): Array[AnyRef] = result(getPartConfig(context, args)) + def getExportConfiguration(context: Context, args: Arguments): Array[AnyRef] = this.getPartConfig(context, args) @Callback(doc = "function(side:number[, slot:number][, database:address, entry:number]):boolean OR function(side:number[, slot:number][, detail: table):boolean -- Configure the export bus pointing in the specified direction to export item stacks matching the specified descriptor.") - def setExportConfiguration(context: Context, args: Arguments): Array[AnyRef] = { - setPartConfig[IAEItemStack](context, args) - result(true) - } + def setExportConfiguration(context: Context, args: Arguments): Array[AnyRef] = this.setPartConfig[IAEItemStack](context, args) @Callback(doc = "function(side:number):number -- Get the number of valid slots in this export bus.") - def getExportSlotSize(context: Context, args: Arguments): Array[AnyRef] = result(getSlotSize(context, args)) + def getExportSlotSize(context: Context, args: Arguments): Array[AnyRef] = getSlotSize(context, args) + + @Callback(doc = "function(side:number):boolean -- Get the ore filter of the export bus pointing in the specified direction.") + def getExportOreFilter(context: Context, args: Arguments): Array[AnyRef] = this.getPartOreFilter(context, args) + + @Callback(doc = "function(side:number, filter: String):boolean -- Set the ore filter of the export bus pointing in the specified direction.") + def setExportOreFilter(context: Context, args: Arguments): Array[AnyRef] = this.setPartOreFilter(context, args) } object Provider extends EnvironmentProvider { diff --git a/src/main/scala/li/cil/oc/integration/ae2fc/DriverFluidImportBus.scala b/src/main/scala/li/cil/oc/integration/ae2fc/DriverFluidImportBus.scala index edb971ca05..35eda439f0 100644 --- a/src/main/scala/li/cil/oc/integration/ae2fc/DriverFluidImportBus.scala +++ b/src/main/scala/li/cil/oc/integration/ae2fc/DriverFluidImportBus.scala @@ -8,7 +8,6 @@ import li.cil.oc.api.driver.{EnvironmentProvider, NamedBlock} import li.cil.oc.api.machine.{Arguments, Callback, Context} import li.cil.oc.integration.ManagedTileEntityEnvironment import li.cil.oc.integration.appeng.internal.PartSharedItemBusBase -import li.cil.oc.util.ResultWrapper.result import net.minecraft.item.ItemStack import net.minecraft.world.World import net.minecraftforge.common.util.ForgeDirection @@ -32,16 +31,19 @@ object DriverFluidImportBus extends driver.SidedBlock { override def priority = 1 @Callback(doc = "function(side:number[, slot:number]):boolean -- Get the configuration of the import bus pointing in the specified direction.") - def getImportConfiguration(context: Context, args: Arguments): Array[AnyRef] = result(getPartConfig(context, args)) + def getImportConfiguration(context: Context, args: Arguments): Array[AnyRef] = this.getPartConfig(context, args) @Callback(doc = "function(side:number[, slot:number][, database:address, entry:number]):boolean OR function(side:number[, slot:number][, detail:table]):boolean -- Configure the import bus pointing in the specified direction to import item stacks matching the specified descriptor.") - def setImportConfiguration(context: Context, args: Arguments): Array[AnyRef] = { - setPartConfig[IAEFluidStack](context, args) - result(true) - } + def setImportConfiguration(context: Context, args: Arguments): Array[AnyRef] = this.setPartConfig[IAEFluidStack](context, args) @Callback(doc = "function(side:number):number -- Get the number of valid slots in this import bus.") - def getImportSlotSize(context: Context, args: Arguments): Array[AnyRef] = result(getSlotSize(context, args)) + def getImportSlotSize(context: Context, args: Arguments): Array[AnyRef] = getSlotSize(context, args) + + @Callback(doc = "function(side:number):boolean -- Get the ore filter of the import bus pointing in the specified direction.") + def getImportOreFilter(context: Context, args: Arguments): Array[AnyRef] = this.getPartOreFilter(context, args) + + @Callback(doc = "function(side:number, filter: String):boolean -- Set the ore filter of the import bus pointing in the specified direction.") + def setImportOreFilter(context: Context, args: Arguments): Array[AnyRef] = this.setPartOreFilter(context, args) } object Provider extends EnvironmentProvider { diff --git a/src/main/scala/li/cil/oc/integration/ae2fc/DriverFluidStorageBus.scala b/src/main/scala/li/cil/oc/integration/ae2fc/DriverFluidStorageBus.scala new file mode 100644 index 0000000000..62d34f9446 --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/ae2fc/DriverFluidStorageBus.scala @@ -0,0 +1,55 @@ +package li.cil.oc.integration.ae2fc + +import appeng.api.parts.IPartHost +import appeng.api.storage.data.IAEFluidStack +import com.glodblock.github.common.parts.PartFluidStorageBus +import li.cil.oc.api.driver +import li.cil.oc.api.driver.{EnvironmentProvider, NamedBlock} +import li.cil.oc.api.machine.{Arguments, Callback, Context} +import li.cil.oc.integration.ManagedTileEntityEnvironment +import li.cil.oc.integration.appeng.internal.PartStorageBusBase +import net.minecraft.item.ItemStack +import net.minecraft.world.World +import net.minecraftforge.common.util.ForgeDirection + +import scala.reflect.ClassTag + +object DriverFluidStorageBus extends driver.SidedBlock { + override def worksWith(world: World, x: Int, y: Int, z: Int, side: ForgeDirection) = + world.getTileEntity(x, y, z) match { + case container: IPartHost => ForgeDirection.VALID_DIRECTIONS.map(container.getPart).filter(obj => { + obj != null + }).exists(_.isInstanceOf[PartFluidStorageBus]) + case _ => false + } + + override def createEnvironment(world: World, x: Int, y: Int, z: Int, side: ForgeDirection) = new Environment(world.getTileEntity(x, y, z).asInstanceOf[IPartHost]) + + final class Environment(val host: IPartHost)(implicit val tag: ClassTag[PartFluidStorageBus]) extends ManagedTileEntityEnvironment[IPartHost](host, "fluid_storagebus") with NamedBlock with PartStorageBusBase[PartFluidStorageBus] { + override def preferredName = "fluid_storagebus" + + override def priority = 2 + + @Callback(doc = "function(side:number[, slot:number]):boolean -- Get the configuration of the storage bus pointing in the specified direction.") + def getStorageConfiguration(context: Context, args: Arguments): Array[AnyRef] = this.getPartConfig(context, args) + + @Callback(doc = "function(side:number[, slot:number][, database:address, entry:number]):boolean OR function(side:number[, slot:number][, detail:table]):boolean -- Configure the storage bus pointing in the specified direction to storage item stacks matching the specified descriptor.") + def setStorageConfiguration(context: Context, args: Arguments): Array[AnyRef] = this.setPartConfig[IAEFluidStack](context, args) + + @Callback(doc = "function(side:number):boolean -- Get the ore filter of the storage bus pointing in the specified direction.") + def getStorageOreFilter(context: Context, args: Arguments): Array[AnyRef] = this.getPartOreFilter(context, args) + + @Callback(doc = "function(side:number, filter: String):boolean -- Set the ore filter of the storage bus pointing in the specified direction.") + def setStorageOreFilter(context: Context, args: Arguments): Array[AnyRef] = this.setPartOreFilter(context, args) + + @Callback(doc = "function(side:number):number -- Get the number of valid slots in this storage bus.") + def getStorageSlotSize(context: Context, args: Arguments): Array[AnyRef] = this.getSlotSize(context, args) + } + + object Provider extends EnvironmentProvider { + override def getEnvironment(stack: ItemStack): Class[_] = + if (Ae2FcUtil.isFluidStorageBus(stack)) + classOf[Environment] + else null + } +} diff --git a/src/main/scala/li/cil/oc/integration/ae2fc/ModAe2fc.scala b/src/main/scala/li/cil/oc/integration/ae2fc/ModAe2fc.scala index 4d1a3ee0bd..b31e3bf554 100644 --- a/src/main/scala/li/cil/oc/integration/ae2fc/ModAe2fc.scala +++ b/src/main/scala/li/cil/oc/integration/ae2fc/ModAe2fc.scala @@ -19,5 +19,7 @@ object ModAe2fc extends ModProxy { Driver.add(DriverFluidExportBus.Provider) Driver.add(DriverFluidImportBus) Driver.add(DriverFluidImportBus.Provider) + Driver.add(DriverFluidStorageBus) + Driver.add(DriverFluidStorageBus.Provider) } } diff --git a/src/main/scala/li/cil/oc/integration/appeng/AEUtil.scala b/src/main/scala/li/cil/oc/integration/appeng/AEUtil.scala index 9a9298c8e4..5971c0e548 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/AEUtil.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/AEUtil.scala @@ -122,6 +122,10 @@ object AEUtil { AEApi.instance.parts.partInterface != null && AEApi.instance.parts.partInterface.sameAsStack(stack) + def isPartStorageBus(stack: ItemStack): Boolean = { + AEApi.instance.definitions.parts.storageBus.isSameAs(stack) + } + def isRobot(stack: ItemStack): Boolean = api.Items.get(stack) == api.Items.get("robot") diff --git a/src/main/scala/li/cil/oc/integration/appeng/DriverExportBus.scala b/src/main/scala/li/cil/oc/integration/appeng/DriverExportBus.scala index 12a430e6d6..b91fe8d54f 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/DriverExportBus.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/DriverExportBus.scala @@ -38,16 +38,19 @@ object DriverExportBus extends driver.SidedBlock { override def priority = 2 @Callback(doc = "function(side:number, [ slot:number]):boolean -- Get the configuration of the export bus pointing in the specified direction.") - def getExportConfiguration(context: Context, args: Arguments): Array[AnyRef] = result(getPartConfig(context, args)) + def getExportConfiguration(context: Context, args: Arguments): Array[AnyRef] = this.getPartConfig(context, args) @Callback(doc = "function(side:number[, slot:number][, database:address, entry:number):boolean OR function(side:number[, slot:number][, detail: table):boolean -- Configure the export bus pointing in the specified direction to export item stacks matching the specified descriptor.") - def setExportConfiguration(context: Context, args: Arguments): Array[AnyRef] = { - setPartConfig[IAEItemStack](context, args) - result(true) - } + def setExportConfiguration(context: Context, args: Arguments): Array[AnyRef] = this.setPartConfig[IAEItemStack](context, args) @Callback(doc = "function(side:number):number -- Get the number of valid slots in this export bus.") - def getExportSlotSize(context: Context, args: Arguments): Array[AnyRef] = result(getSlotSize(context, args)) + def getExportSlotSize(context: Context, args: Arguments): Array[AnyRef] = getSlotSize(context, args) + + @Callback(doc = "function(side:number):boolean -- Get the ore filter of the export bus pointing in the specified direction.") + def getExportOreFilter(context: Context, args: Arguments): Array[AnyRef] = this.getPartOreFilter(context, args) + + @Callback(doc = "function(side:number, filter: String):boolean -- Set the ore filter of the export bus pointing in the specified direction.") + def setExportOreFilter(context: Context, args: Arguments): Array[AnyRef] = this.setPartOreFilter(context, args) @Callback(doc = "function(side:number, slot:number):boolean -- Make the export bus facing the specified direction perform a single export operation into the specified slot.") def exportIntoSlot(context: Context, args: Arguments): Array[AnyRef] = { diff --git a/src/main/scala/li/cil/oc/integration/appeng/DriverImportBus.scala b/src/main/scala/li/cil/oc/integration/appeng/DriverImportBus.scala index c64daf780a..58ae8bd34c 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/DriverImportBus.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/DriverImportBus.scala @@ -8,7 +8,6 @@ import li.cil.oc.api.driver.{EnvironmentProvider, NamedBlock} import li.cil.oc.api.machine.{Arguments, Callback, Context} import li.cil.oc.integration.ManagedTileEntityEnvironment import li.cil.oc.integration.appeng.internal.PartItemBusBase -import li.cil.oc.util.ResultWrapper.result import net.minecraft.item.ItemStack import net.minecraft.world.World import net.minecraftforge.common.util.ForgeDirection @@ -32,16 +31,19 @@ object DriverImportBus extends driver.SidedBlock { override def priority = 1 @Callback(doc = "function(side:number[, slot:number]):boolean -- Get the configuration of the import bus pointing in the specified direction.") - def getImportConfiguration(context: Context, args: Arguments): Array[AnyRef] = result(getPartConfig(context, args)) + def getImportConfiguration(context: Context, args: Arguments): Array[AnyRef] = this.getPartConfig(context, args) @Callback(doc = "function(side:number[, slot:number][, database:address, entry:number]):boolean OR function(side:number[, slot:number][, detail:table]):boolean -- Configure the import bus pointing in the specified direction to import item stacks matching the specified descriptor.") - def setImportConfiguration(context: Context, args: Arguments): Array[AnyRef] = { - setPartConfig[IAEItemStack](context, args) - result(true) - } + def setImportConfiguration(context: Context, args: Arguments): Array[AnyRef] = this.setPartConfig[IAEItemStack](context, args) @Callback(doc = "function(side:number):number -- Get the number of valid slots in this import bus.") - def getImportSlotSize(context: Context, args: Arguments): Array[AnyRef] = result(getSlotSize(context, args)) + def getImportSlotSize(context: Context, args: Arguments): Array[AnyRef] = getSlotSize(context, args) + + @Callback(doc = "function(side:number):boolean -- Get the ore filter of the import bus pointing in the specified direction.") + def getImportOreFilter(context: Context, args: Arguments): Array[AnyRef] = this.getPartOreFilter(context, args) + + @Callback(doc = "function(side:number, filter: String):boolean -- Set the ore filter of the import bus pointing in the specified direction.") + def setImportOreFilter(context: Context, args: Arguments): Array[AnyRef] = this.setPartOreFilter(context, args) } object Provider extends EnvironmentProvider { diff --git a/src/main/scala/li/cil/oc/integration/appeng/DriverStorageBus.scala b/src/main/scala/li/cil/oc/integration/appeng/DriverStorageBus.scala new file mode 100644 index 0000000000..eb54429484 --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/appeng/DriverStorageBus.scala @@ -0,0 +1,56 @@ +package li.cil.oc.integration.appeng + +import appeng.api.parts.IPartHost +import appeng.api.storage.data.IAEItemStack +import appeng.parts.misc.PartStorageBus +import li.cil.oc.api.driver +import li.cil.oc.api.driver.{EnvironmentProvider, NamedBlock} +import li.cil.oc.api.machine.{Arguments, Callback, Context} +import li.cil.oc.integration.ManagedTileEntityEnvironment +import li.cil.oc.integration.appeng.internal.PartItemStorageBusBusBase +import net.minecraft.item.ItemStack +import net.minecraft.world.World +import net.minecraftforge.common.util.ForgeDirection + +import scala.reflect.ClassTag + +object DriverStorageBus extends driver.SidedBlock { + override def worksWith(world: World, x: Int, y: Int, z: Int, side: ForgeDirection) = + world.getTileEntity(x, y, z) match { + case container: IPartHost => ForgeDirection.VALID_DIRECTIONS.map(container.getPart).filter(obj => { + obj != null + }).exists(_.isInstanceOf[PartStorageBus]) + case _ => false + } + + override def createEnvironment(world: World, x: Int, y: Int, z: Int, side: ForgeDirection) = new Environment(world.getTileEntity(x, y, z).asInstanceOf[IPartHost]) + + final class Environment(val host: IPartHost)(implicit val tag: ClassTag[PartStorageBus]) extends ManagedTileEntityEnvironment[IPartHost](host, "me_storagebus") with NamedBlock with PartItemStorageBusBusBase[PartStorageBus] { + override def preferredName = "me_storagebus" + + override def priority = 1 + + @Callback(doc = "function(side:number[, slot:number]):boolean -- Get the configuration of the storage bus pointing in the specified direction.") + def getStorageConfiguration(context: Context, args: Arguments): Array[AnyRef] = this.getPartConfig(context, args) + + @Callback(doc = "function(side:number[, slot:number][, database:address, entry:number]):boolean OR function(side:number[, slot:number][, detail:table]):boolean -- Configure the storage bus pointing in the specified direction to storage item stacks matching the specified descriptor.") + def setStorageConfiguration(context: Context, args: Arguments): Array[AnyRef] = this.setPartConfig[IAEItemStack](context, args) + + @Callback(doc = "function(side:number):boolean -- Get the ore filter of the storage bus pointing in the specified direction.") + def getStorageOreFilter(context: Context, args: Arguments): Array[AnyRef] = this.getPartOreFilter(context, args) + + @Callback(doc = "function(side:number, filter: String):boolean -- Set the ore filter of the storage bus pointing in the specified direction.") + def setStorageOreFilter(context: Context, args: Arguments): Array[AnyRef] = this.setPartOreFilter(context, args) + + @Callback(doc = "function(side:number):number -- Get the number of valid slots in this storage bus.") + def getStorageSlotSize(context: Context, args: Arguments): Array[AnyRef] = this.getSlotSize(context, args) + } + + object Provider extends EnvironmentProvider { + override def getEnvironment(stack: ItemStack): Class[_] = + if (AEUtil.isPartStorageBus(stack)) + classOf[Environment] + else null + } + +} 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 8e5201df95..8132bce148 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/ModAppEng.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/ModAppEng.scala @@ -26,6 +26,7 @@ object ModAppEng extends ModProxy { Driver.add(DriverController) Driver.add(DriverExportBus) Driver.add(DriverImportBus) + Driver.add(DriverStorageBus) Driver.add(DriverPartInterface) Driver.add(DriverBlockInterface) Driver.add(DriverUpgradeAE) @@ -38,6 +39,7 @@ object ModAppEng extends ModProxy { Driver.add(DriverController.Provider) Driver.add(DriverExportBus.Provider) Driver.add(DriverImportBus.Provider) + Driver.add(DriverStorageBus.Provider) Driver.add(DriverPartInterface.Provider) Driver.add(DriverBlockInterface.Provider) Driver.add(DriverUpgradeAE.Provider) diff --git a/src/main/scala/li/cil/oc/integration/appeng/internal/PartEnvironmentBase.scala b/src/main/scala/li/cil/oc/integration/appeng/internal/PartEnvironmentBase.scala index cc0b7be9dc..55a54d5a0b 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/internal/PartEnvironmentBase.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/internal/PartEnvironmentBase.scala @@ -4,7 +4,9 @@ import appeng.api.config.Upgrades import appeng.api.parts.{IPart, IPartHost} import appeng.api.storage.StorageName import appeng.api.storage.data.{IAEItemStack, IAEStack} +import appeng.helpers.IOreFilterable import appeng.parts.automation.PartSharedItemBus +import appeng.parts.misc.PartStorageBus import appeng.tile.inventory.IIAEStackInventory import appeng.util.item.AEItemStack import li.cil.oc.api.machine.{Arguments, Context} @@ -12,8 +14,10 @@ import li.cil.oc.api.network.ManagedEnvironment import li.cil.oc.integration.appeng.AEStackFactory import li.cil.oc.util.DatabaseAccess import li.cil.oc.util.ExtendedArguments._ +import li.cil.oc.util.ResultWrapper.result import net.minecraftforge.common.util.ForgeDirection +import scala.language.implicitConversions import scala.reflect.ClassTag trait PartEnvironmentBase[PartType <: IPart] extends ManagedEnvironment { @@ -29,70 +33,116 @@ trait PartEnvironmentBase[PartType <: IPart] extends ManagedEnvironment { } } -trait PartConfigurable[PartType <: IIAEStackInventory with IPart] extends PartEnvironmentBase[PartType] { - implicit def tag: ClassTag[PartType] +object PartEnvironmentBase { + implicit class OreFilterOps[PartType <: IPart](val env: PartEnvironmentBase[PartType]) extends AnyVal { + def getPartOreFilter(context: Context, args: Arguments)(implicit ev: PartType <:< IOreFilterable): Array[AnyRef] = { + val side = args.checkSideAny(0) + val part = env.getPart(side) + result(part.getFilter) + } - private def getConfigAndValidate(part: PartType, slot: Int) = { - val config = part.getAEInventoryByName(StorageName.CONFIG) - if (slot < 0 || slot >= config.getSizeInventory) { - throw new IllegalArgumentException("invalid slot") + def setPartOreFilter(context: Context, args: Arguments)(implicit ev: PartType <:< IOreFilterable): Array[AnyRef] = { + val side = args.checkSideAny(0) + val filter = args.checkString(1) + val part = env.getPart(side) + part.setFilter(filter) + result(true) } - config } - def getPartConfig(part: PartType, slot: Int): IAEStack[_] = { - val config = getConfigAndValidate(part, slot) - config.getAEStackInSlot(slot) - } + implicit class ConfigOps[PartType <: IPart](val env: PartEnvironmentBase[PartType]) extends AnyVal { + private def getConfigAndValidate(part: PartType, slot: Int)(implicit ev: PartType <:< IIAEStackInventory) = { + val config = part.getAEInventoryByName(StorageName.CONFIG) + if (slot < 0 || slot >= config.getSizeInventory) { + throw new IllegalArgumentException("invalid slot") + } + config + } - // function(side:number[, slot:number]):table - def getPartConfig(context: Context, args: Arguments): IAEStack[_] = { - val side = args.checkSideAny(0) - val part = getPart(side) - getPartConfig(part, args.optInteger(1, 0)) - } + def getPartConfigInternal(part: PartType, slot: Int)(implicit ev: PartType <:< IIAEStackInventory): IAEStack[_] = { + val config = getConfigAndValidate(part, slot) + config.getAEStackInSlot(slot) + } - // NOTE: Setting a config to null won't sync to the client properly. - // Because updateVirtualSlots currently skips null entries during init, so the client might still show an item that was actually cleared on the server. - def setPartConfig(part: PartType, slot: Int, stack: IAEStack[_]): Unit = { - val config = getConfigAndValidate(part, slot) - config.putAEStackInSlot(slot, stack) - } + // function(side:number[, slot:number]):table + def getPartConfig(context: Context, args: Arguments)(implicit ev: PartType <:< IIAEStackInventory): Array[AnyRef] = { + val side = args.checkSideAny(0) + val part = env.getPart(side) + result(getPartConfigInternal(part, args.optInteger(1, 0))) + } - def setPartConfig[T <: IAEStack[T] : ClassTag](context: Context, args: Arguments): Unit = { - val side = args.checkSideAny(0) - val part = getPart(side) - val (slot, offset) = if (args.isInteger(1)) (args.checkInteger(1), 2) else (0, 1) - val stack: T = if (args.count() <= offset) null.asInstanceOf[T] - else AEStackFactory.parse[T](args.checkTable(offset)) - setPartConfig(part, slot, stack) + // NOTE: Setting a config to null won't sync to the client properly. + // Because updateVirtualSlots currently skips null entries during init, so the client might still show an item that was actually cleared on the server. + def setPartConfigInternal(part: PartType, slot: Int, stack: IAEStack[_])(implicit ev: PartType <:< IIAEStackInventory): Unit = { + val config = getConfigAndValidate(part, slot) + config.putAEStackInSlot(slot, stack) + } + + def setPartConfig[T <: IAEStack[T] : ClassTag](context: Context, args: Arguments)(implicit ev: PartType <:< IIAEStackInventory): Array[AnyRef] = { + val side = args.checkSideAny(0) + val part = env.getPart(side) + val (slot, offset) = if (args.isInteger(1)) (args.checkInteger(1), 2) else (0, 1) + val stack: T = if (args.count() <= offset) null.asInstanceOf[T] + else AEStackFactory.parse[T](args.checkTable(offset)) + setPartConfigInternal(part, slot, stack) + result(true) + } } } -trait PartSharedItemBusBase[PartType <: PartSharedItemBus[_]] extends PartConfigurable[PartType] { +trait PartSharedItemBusBase[PartType <: PartSharedItemBus[_]] extends PartEnvironmentBase[PartType] { implicit def tag: ClassTag[PartType] def getSlotSize(part: PartType): Int = Math.min(1 + part.getInstalledUpgrades(Upgrades.CAPACITY) * 4, part.getAEInventoryByName(StorageName.CONFIG).getSizeInventory) - def getSlotSize(context: Context, args: Arguments): Int = { + def getSlotSize(context: Context, args: Arguments): Array[AnyRef] = { val side = args.checkSideAny(0) val part = getPart(side) - getSlotSize(part) + result(getSlotSize(part)) + } +} + +object PartItemConfigurablePart { + implicit class ConfigOps[PartType <: IPart](val env: PartEnvironmentBase[PartType]) { + def setPartConfig[T <: IAEStack[T] : ClassTag](context: Context, args: Arguments)(implicit ev: PartType <:< IIAEStackInventory): Array[AnyRef] = { + val side = args.checkSideAny(0) + val part = env.getPart(side) + val (slot, offset) = if (args.isInteger(1)) (args.checkInteger(1), 2) else (0, 1) + val stack = + if (args.isTable(offset)) AEStackFactory.parse[IAEItemStack](args.checkTable(offset)) + else AEItemStack.create(DatabaseAccess.getStackFromDatabase(env.node, args, offset)) + env.setPartConfigInternal(part, slot, stack) + result(true) + } } } trait PartItemBusBase[PartType <: PartSharedItemBus[_]] extends PartSharedItemBusBase[PartType] { implicit def tag: ClassTag[PartType] +} + +object PartItemBusBase { + implicit def ConfigOps[PartType <: PartSharedItemBus[_]](env: PartItemBusBase[PartType]): PartItemConfigurablePart.ConfigOps[PartType] = new PartItemConfigurablePart.ConfigOps[PartType](env) +} + +trait PartStorageBusBase[PartType <: PartStorageBus] extends PartEnvironmentBase[PartType] { + implicit def tag: ClassTag[PartType] - // function(side:number[, slot:number][, database:address, entry:number[, size:number]]):boolean - override def setPartConfig[T <: IAEStack[T] : ClassTag](context: Context, args: Arguments): Unit = { + def getSlotSize(part: PartType): Int = + Math.min(18 + part.getInstalledUpgrades(Upgrades.CAPACITY) * 9, part.getAEInventoryByName(StorageName.CONFIG).getSizeInventory) + + def getSlotSize(context: Context, args: Arguments): Array[AnyRef] = { val side = args.checkSideAny(0) val part = getPart(side) - val (slot, offset) = if (args.isInteger(1)) (args.checkInteger(1), 2) else (0, 1) - val stack = - if (args.isTable(offset)) AEStackFactory.parse[IAEItemStack](args.checkTable(offset)) - else AEItemStack.create(DatabaseAccess.getStackFromDatabase(node, args, offset)) - setPartConfig(part, slot, stack) + result(getSlotSize(part)) } +} + +trait PartItemStorageBusBusBase[PartType <: PartStorageBus] extends PartStorageBusBase[PartType] { + implicit def tag: ClassTag[PartType] +} + +object PartItemStorageBusBusBase { + implicit def ConfigOps[PartType <: PartStorageBus](env: PartItemStorageBusBusBase[PartType]): PartItemConfigurablePart.ConfigOps[PartType] = new PartItemConfigurablePart.ConfigOps[PartType](env) } \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/integration/thaumicenergistics/DriverEssentiaExportBus.scala b/src/main/scala/li/cil/oc/integration/thaumicenergistics/DriverEssentiaExportBus.scala index c2e62e25e8..7466fd94e5 100644 --- a/src/main/scala/li/cil/oc/integration/thaumicenergistics/DriverEssentiaExportBus.scala +++ b/src/main/scala/li/cil/oc/integration/thaumicenergistics/DriverEssentiaExportBus.scala @@ -5,7 +5,6 @@ import li.cil.oc.api.driver import li.cil.oc.api.driver.{EnvironmentProvider, NamedBlock} import li.cil.oc.api.machine.{Arguments, Callback, Context} import li.cil.oc.integration.ManagedTileEntityEnvironment -import li.cil.oc.integration.appeng.AEStackFactory import li.cil.oc.integration.appeng.internal.PartSharedItemBusBase import li.cil.oc.util.ExtendedArguments._ import li.cil.oc.util.ResultWrapper._ @@ -16,7 +15,7 @@ import thaumicenergistics.api.ThEApi import thaumicenergistics.common.parts.PartEssentiaExportBus import thaumicenergistics.common.storage.AEEssentiaStack -import scala.reflect.{ClassTag, classTag} +import scala.reflect.ClassTag object DriverEssentiaExportBus extends driver.SidedBlock { override def worksWith(world: World, x: Int, y: Int, z: Int, side: ForgeDirection) = @@ -35,16 +34,19 @@ object DriverEssentiaExportBus extends driver.SidedBlock { override def priority = 2 @Callback(doc = "function(side:number[, slot:number]):string -- Get the configuration of the export bus pointing in the specified direction.") - def getExportConfiguration(context: Context, args: Arguments): Array[AnyRef] = result(getPartConfig(context, args)) + def getExportConfiguration(context: Context, args: Arguments): Array[AnyRef] = this.getPartConfig(context, args) @Callback(doc = "function(side:number[, slot:number][, aspect:string]):boolean -- Configure the export bus pointing in the specified direction to export essentia matching the specified type.") - def setExportConfiguration(context: Context, args: Arguments): Array[AnyRef] = { - setPartConfig[AEEssentiaStack](context, args) - result(true) - } + def setExportConfiguration(context: Context, args: Arguments): Array[AnyRef] = this.setPartConfig[AEEssentiaStack](context, args) @Callback(doc = "function(side:number):number -- Get the number of valid slots in this export bus.") - def getExportSlotSize(context: Context, args: Arguments): Array[AnyRef] = result(getSlotSize(context, args)) + def getExportSlotSize(context: Context, args: Arguments): Array[AnyRef] = getSlotSize(context, args) + + @Callback(doc = "function(side:number):boolean -- Get the ore filter of the export bus pointing in the specified direction.") + def getExportOreFilter(context: Context, args: Arguments): Array[AnyRef] = this.getPartOreFilter(context, args) + + @Callback(doc = "function(side:number, filter: String):boolean -- Set the ore filter of the export bus pointing in the specified direction.") + def setExportOreFilter(context: Context, args: Arguments): Array[AnyRef] = this.setPartOreFilter(context, args) @Callback(doc = "function(side:number):boolean -- Get whether or not essentia exported into a void jar will allow voiding") def getVoidAllowed(context: Context, args: Arguments): Array[AnyRef] = { diff --git a/src/main/scala/li/cil/oc/integration/thaumicenergistics/DriverEssentiaImportBus.scala b/src/main/scala/li/cil/oc/integration/thaumicenergistics/DriverEssentiaImportBus.scala index a662917675..ad92f0920f 100644 --- a/src/main/scala/li/cil/oc/integration/thaumicenergistics/DriverEssentiaImportBus.scala +++ b/src/main/scala/li/cil/oc/integration/thaumicenergistics/DriverEssentiaImportBus.scala @@ -5,9 +5,7 @@ import li.cil.oc.api.driver import li.cil.oc.api.driver.{EnvironmentProvider, NamedBlock} import li.cil.oc.api.machine.{Arguments, Callback, Context} import li.cil.oc.integration.ManagedTileEntityEnvironment -import li.cil.oc.integration.appeng.AEStackFactory import li.cil.oc.integration.appeng.internal.PartSharedItemBusBase -import li.cil.oc.util.ResultWrapper.result import net.minecraft.item.ItemStack import net.minecraft.world.World import net.minecraftforge.common.util.ForgeDirection @@ -15,7 +13,7 @@ import thaumicenergistics.api.ThEApi import thaumicenergistics.common.parts.PartEssentiaImportBus import thaumicenergistics.common.storage.AEEssentiaStack -import scala.reflect.{ClassTag, classTag} +import scala.reflect.ClassTag object DriverEssentiaImportBus extends driver.SidedBlock { override def worksWith(world: World, x: Int, y: Int, z: Int, side: ForgeDirection) = @@ -34,16 +32,19 @@ object DriverEssentiaImportBus extends driver.SidedBlock { override def priority = 2 @Callback(doc = "function(side:number[, slot:number]):string -- Get the configuration of the import bus pointing in the specified direction.") - def getImportConfiguration(context: Context, args: Arguments): Array[AnyRef] = result(getPartConfig(context, args)) + def getImportConfiguration(context: Context, args: Arguments): Array[AnyRef] = this.getPartConfig(context, args) @Callback(doc = "function(side:number[, slot:number][, aspect:string]):boolean -- Configure the import bus pointing in the specified direction to import essentia matching the specified type.") - def setImportConfiguration(context: Context, args: Arguments): Array[AnyRef] = { - setPartConfig[AEEssentiaStack](context, args) - result(true) - } + def setImportConfiguration(context: Context, args: Arguments): Array[AnyRef] = this.setPartConfig[AEEssentiaStack](context, args) @Callback(doc = "function(side:number):number -- Get the number of valid slots in this import bus.") - def getImportSlotSize(context: Context, args: Arguments): Array[AnyRef] = result(getSlotSize(context, args)) + def getImportSlotSize(context: Context, args: Arguments): Array[AnyRef] = getSlotSize(context, args) + + @Callback(doc = "function(side:number):boolean -- Get the ore filter of the import bus pointing in the specified direction.") + def getImportOreFilter(context: Context, args: Arguments): Array[AnyRef] = this.getPartOreFilter(context, args) + + @Callback(doc = "function(side:number, filter: String):boolean -- Set the ore filter of the import bus pointing in the specified direction.") + def setImportOreFilter(context: Context, args: Arguments): Array[AnyRef] = this.setPartOreFilter(context, args) } object Provider extends EnvironmentProvider { diff --git a/src/main/scala/li/cil/oc/integration/thaumicenergistics/DriverEssentiaStorageBus.scala b/src/main/scala/li/cil/oc/integration/thaumicenergistics/DriverEssentiaStorageBus.scala new file mode 100644 index 0000000000..bfbfc950db --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/thaumicenergistics/DriverEssentiaStorageBus.scala @@ -0,0 +1,56 @@ +package li.cil.oc.integration.thaumicenergistics + +import appeng.api.parts.IPartHost +import li.cil.oc.api.driver +import li.cil.oc.api.driver.{EnvironmentProvider, NamedBlock} +import li.cil.oc.api.machine.{Arguments, Callback, Context} +import li.cil.oc.integration.ManagedTileEntityEnvironment +import li.cil.oc.integration.appeng.internal.PartStorageBusBase +import net.minecraft.item.ItemStack +import net.minecraft.world.World +import net.minecraftforge.common.util.ForgeDirection +import thaumicenergistics.api.ThEApi +import thaumicenergistics.common.parts.PartEssentiaStorageBus +import thaumicenergistics.common.storage.AEEssentiaStack + +import scala.reflect.ClassTag + +object DriverEssentiaStorageBus extends driver.SidedBlock { + override def worksWith(world: World, x: Int, y: Int, z: Int, side: ForgeDirection) = + world.getTileEntity(x, y, z) match { + case container: IPartHost => ForgeDirection.VALID_DIRECTIONS.map(container.getPart).filter(obj => { + obj != null + }).exists(_.isInstanceOf[PartEssentiaStorageBus]) + case _ => false + } + + override def createEnvironment(world: World, x: Int, y: Int, z: Int, side: ForgeDirection) = new Environment(world.getTileEntity(x, y, z).asInstanceOf[IPartHost]) + + final class Environment(val host: IPartHost)(implicit val tag: ClassTag[PartEssentiaStorageBus]) extends ManagedTileEntityEnvironment[IPartHost](host, "essentia_storagebus") with NamedBlock with PartStorageBusBase[PartEssentiaStorageBus] { + override def preferredName = "essentia_storagebus" + + override def priority = 2 + + @Callback(doc = "function(side:number[, slot:number]):boolean -- Get the configuration of the storage bus pointing in the specified direction.") + def getStorageConfiguration(context: Context, args: Arguments): Array[AnyRef] = this.getPartConfig(context, args) + + @Callback(doc = "function(side:number[, slot:number][, database:address, entry:number]):boolean OR function(side:number[, slot:number][, detail:table]):boolean -- Configure the storage bus pointing in the specified direction to storage item stacks matching the specified descriptor.") + def setStorageConfiguration(context: Context, args: Arguments): Array[AnyRef] = this.setPartConfig[AEEssentiaStack](context, args) + + @Callback(doc = "function(side:number):boolean -- Get the ore filter of the storage bus pointing in the specified direction.") + def getStorageOreFilter(context: Context, args: Arguments): Array[AnyRef] = this.getPartOreFilter(context, args) + + @Callback(doc = "function(side:number, filter: String):boolean -- Set the ore filter of the storage bus pointing in the specified direction.") + def setStorageOreFilter(context: Context, args: Arguments): Array[AnyRef] = this.setPartOreFilter(context, args) + + @Callback(doc = "function(side:number):number -- Get the number of valid slots in this storage bus.") + def getStorageSlotSize(context: Context, args: Arguments): Array[AnyRef] = this.getSlotSize(context, args) + } + + object Provider extends EnvironmentProvider { + override def getEnvironment(stack: ItemStack): Class[_] = + if (ThEApi.instance.parts.Essentia_StorageBus.getStack.isItemEqual(stack)) + classOf[Environment] + else null + } +} diff --git a/src/main/scala/li/cil/oc/integration/thaumicenergistics/ModThaumicEnergistics.scala b/src/main/scala/li/cil/oc/integration/thaumicenergistics/ModThaumicEnergistics.scala index ede39cc164..8937bce540 100644 --- a/src/main/scala/li/cil/oc/integration/thaumicenergistics/ModThaumicEnergistics.scala +++ b/src/main/scala/li/cil/oc/integration/thaumicenergistics/ModThaumicEnergistics.scala @@ -1,8 +1,8 @@ package li.cil.oc.integration.thaumicenergistics import li.cil.oc.api.Driver -import li.cil.oc.integration.{Mod, ModProxy, Mods} import li.cil.oc.integration.appeng.AEStackFactory +import li.cil.oc.integration.{Mod, ModProxy, Mods} import thaumicenergistics.common.storage.{AEEssentiaStack, AEEssentiaStackType} object ModThaumicEnergistics extends ModProxy { @@ -16,11 +16,13 @@ object ModThaumicEnergistics extends ModProxy { Driver.add(DriverBlockInterface) Driver.add(DriverEssentiaExportBus) Driver.add(DriverEssentiaImportBus) + Driver.add(DriverEssentiaStorageBus) Driver.add(DriverController.Provider) Driver.add(DriverBlockInterface.Provider) Driver.add(DriverEssentiaExportBus.Provider) Driver.add(DriverEssentiaImportBus.Provider) + Driver.add(DriverEssentiaStorageBus.Provider) Driver.add(ConvertAspectCraftable) } } \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/integration/thaumicenergistics/ThaumicEnergisticsUtils.scala b/src/main/scala/li/cil/oc/integration/thaumicenergistics/ThaumicEnergisticsUtils.scala index 0f1079c8f2..4ededaaf40 100644 --- a/src/main/scala/li/cil/oc/integration/thaumicenergistics/ThaumicEnergisticsUtils.scala +++ b/src/main/scala/li/cil/oc/integration/thaumicenergistics/ThaumicEnergisticsUtils.scala @@ -1,11 +1,4 @@ package li.cil.oc.integration.thaumicenergistics -import appeng.api.storage.data.IAEFluidStack - object ThaumicEnergisticsUtils { - def getAspect(fluid: IAEFluidStack) = { - val aspect = fluid.getFluidStack.copy() - aspect.amount = (fluid.getStackSize / 128).toInt - aspect - } }