Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
232 changes: 154 additions & 78 deletions src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@ package li.cil.oc.integration.appeng
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.crafting._
import appeng.api.networking.security.{BaseActionSource, IActionHost, MachineSource}
import appeng.api.networking.storage.IBaseMonitor
import appeng.api.storage.data.{IAEFluidStack, IAEItemStack, IAEStack, IItemList}
import appeng.api.storage.{IMEInventory, IMEMonitor, IMEMonitorHandlerReceiver}
import appeng.api.storage.{IMEMonitor, IMEMonitorHandlerReceiver}
import appeng.api.util.AECableType
import appeng.crafting.CraftingLink
import appeng.crafting.v2.CraftingJobV2
import appeng.me.GridAccessException
import appeng.me.cache.CraftingGridCache
import appeng.me.cluster.implementations.CraftingCPUCluster
import appeng.me.helpers.IGridProxyable
import appeng.tile.crafting.TileCraftingMonitorTile
Expand All @@ -21,20 +25,19 @@ import li.cil.oc.api.machine.{Arguments, Callback, Context}
import li.cil.oc.api.network.{ManagedEnvironment, Node}
import li.cil.oc.api.prefab.AbstractValue
import li.cil.oc.common.EventHandler
import li.cil.oc.common.tileentity.Adapter
import li.cil.oc.integration.Mods
import li.cil.oc.integration.ae2fc.Ae2FcUtil
import li.cil.oc.integration.appeng.NetworkControl._
import li.cil.oc.integration.appeng.internal.SubscriptionBase
import li.cil.oc.integration.ec.ECUtil
import li.cil.oc.util.ExtendedArguments._
import li.cil.oc.util.ExtendedNBT._
import li.cil.oc.util.ResultWrapper._
import li.cil.oc.util.{AE2Bridge, DatabaseAccess}
import net.minecraft.item.{Item, ItemStack}
import net.minecraft.nbt.{JsonToNBT, NBTTagCompound}
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}

Expand All @@ -43,8 +46,6 @@ import javax.annotation.Nonnull
import scala.collection.convert.WrapAsJava._
import scala.collection.convert.WrapAsScala._
import scala.collection.mutable
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.language.existentials
import scala.reflect.ClassTag

Expand Down Expand Up @@ -92,7 +93,7 @@ trait NetworkControl[AETile >: Null <: TileEntity with IGridProxyable with IActi
val monitor = tile.getProxy.getStorage.getMEMonitor(stack.getStackType).asInstanceOf[IMEMonitor[AEStack]]
if (monitor != null) {
val s = monitor.getAvailableItem(stack, IterationCounter.fetchNewId())
if (s != null && s.isCraftable) result(new Craftable(tile, asCraft(s, tile)))
if (s != null && s.isCraftable) result(new Craftable(tile, asCraft(s, tile), node))
else result(null)
}
else {
Expand All @@ -119,7 +120,7 @@ trait NetworkControl[AETile >: Null <: TileEntity with IGridProxyable with IActi
if (s.isCraftable) {
val c = asCraft(s, tile)
if (matches(convert(c, tile), filter)) {
builder += new Craftable(tile, c)
builder += new Craftable(tile, c, node)
}
}
}
Expand Down Expand Up @@ -214,7 +215,7 @@ trait NetworkControl[AETile >: Null <: TileEntity with IGridProxyable with IActi
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(_.asInstanceOf[IAEItemStack].getItemStack.copy()).orNull
val stack = Option(items(i)).map(_.getItemStack.copy()).orNull
while (database.getStackInSlot(slot) != null && slot < database.size) slot += 1
if (database.getStackInSlot(slot) == null) {
database.setStackInSlot(slot, stack)
Expand Down Expand Up @@ -334,38 +335,61 @@ trait NetworkControl[AETile >: Null <: TileEntity with IGridProxyable with IActi
}

object NetworkControl extends AETypes {

//noinspection ScalaUnusedSymbol
private 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]

// ----------------------------------------------------------------------- //

//noinspection RedundantCollectionConversion
override def getRequestedJobs: ImmutableSet[ICraftingLink] = ImmutableSet.copyOf(links.toIterable)

override def jobStateChange(link: ICraftingLink): Unit = {
links -= link
private trait AdapterNode extends AbstractValue {
private def findInNetwork[T](node: Node, f: PartialFunction[Node, T]): Option[T] = {
val visited = mutable.HashSet[Node](node)
var queue = List(node)
var result: Option[T] = None
while (result.isEmpty && queue.nonEmpty) {
result = queue.iterator.collectFirst(f)
if (result.isEmpty)
queue = queue.flatMap { n =>
n.neighbors.filter(visited.add)
}
}
result
}

override def injectCraftedItems(link: ICraftingLink, stack: IAEStack[_], p3: Actionable): IAEStack[_] = stack

override def getActionableNode: IGridNode = controller.getActionableNode
var node: Node
// it's a hacky way to do this, but I think it's fine here
private var adapter: Option[Adapter] = Option(node).flatMap(findInNetwork(_, { case a if a.host.isInstanceOf[Adapter] => a.host.asInstanceOf[Adapter] }))
private var address: String = if (node != null) node.address() else null

override def getCableConnectionType(side: ForgeDirection): AECableType = controller.getCableConnectionType(side)
protected def getNode: Option[Node] = {
Option(node).orElse {
val found = adapter.flatMap(a => findInNetwork(a.node, { case n if n.address() == address =>
n
}))
found.foreach(node = _)
found
}
}

override def securityBreak(): Unit = controller.securityBreak()
abstract override def load(nbt: NBTTagCompound): Unit = {
if (nbt.hasKey("adapter")) loadTileEntity(nbt.getCompoundTag("adapter"), {
case a: Adapter => adapter = Some(a)
})
if (nbt.hasKey("address")) address = nbt.getString("address")
}

override def getGridNode(side: ForgeDirection): IGridNode = controller.getGridNode(side)
abstract override def save(nbt: NBTTagCompound): Unit = {
adapter.foreach { a =>
val tag = new NBTTagCompound
saveTileEntity(a, tag)
nbt.setTag("adapter", tag)
}
if (address != null) nbt.setString("address", address)
}
}

// ----------------------------------------------------------------------- //
//noinspection ScalaUnusedSymbol
private class Craftable(var controller: TileEntity with IGridProxyable with IActionHost, var stack: AEStack, var node: Node) extends AdapterNode {
def this() = this(null, null, null)

@Callback(doc = "function():table -- Returns the item stack representation of the crafting result.")
def getStack(context: Context, args: Arguments): Array[AnyRef] = result(stack)

@Callback(doc = "function([amount:number[, prioritizePower:boolean[, cpuName:string]]]):userdata -- Requests the item to be crafted, returning an object that allows tracking the crafting status.")
@Callback(doc = "function([amount:number[, prioritizePower:boolean[, cpuName:string]]]):userdata -- Requests the item to be crafted, returning an object that allows tracking the crafting status. Event 'network_crafting_state_change(isDone:boolean, craftingID:string)' will be sent when done or cancelled.")
def request(context: Context, args: Arguments): Array[AnyRef] = {
if (controller == null || controller.isInvalid) {
return result(Unit, "no controller")
Expand All @@ -377,47 +401,39 @@ object NetworkControl extends AETypes {

val craftingGrid = controller.getProxy.getCrafting
val source = new MachineSource(controller)
val future = craftingGrid.beginCraftingJob(controller.getWorldObj, controller.getProxy.getGrid, source, request, null)
val prioritizePower = args.optBoolean(1, true)
val cpuName = args.optString(2, "")
val cpu = if (cpuName.nonEmpty) {
controller.getProxy.getCrafting.getCpus.collectFirst({
case c if cpuName.equals(c.getName) => c
}).orNull
} else null

val status = new CraftingStatus()
Future {
try {
while (!future.isDone) {
Thread.sleep(10)
}

val job = future.get()

if (future.isCancelled) {
status.fail("missing resources")
} else {
EventHandler.scheduleServer(() => {
val link = craftingGrid.submitJob(job, Craftable.this, cpu, prioritizePower, source)
if (link != null) {
status.setLink(link)
links += link
getNode match {
case Some(n) =>
val status = new CraftingStatus(controller, n)
craftingGrid.beginCraftingJob(controller.getWorldObj, controller.getProxy.getGrid, source, request, new ICraftingCallback {
@Override
override def calculationComplete(job: (ICraftingJob[StackType]) forSome {type StackType <: IAEStack[StackType]}): Unit = {
job match {
case craftingJobV2: CraftingJobV2[_] =>
if (craftingJobV2.isCancelled) status.fail(craftingJobV2.getErrorMessage)
else if (craftingJobV2.isSimulation) status.fail("missing resources")
else {
val link = craftingGrid.submitJob(job, status, cpu, prioritizePower, source)
if (link != null) {
status.setLink(link)
}
else {
status.fail("no available cpu")
}
}
case _ =>
}
else {
status.fail("missing resources?")
}
})
}
}
catch {
case e: Exception =>
OpenComputers.log.debug("Error submitting job to AE2.", e)
status.fail(e.toString)
}
}
})
result(status)
case None => result(null, "no network node")
}

result(status)
}

// ----------------------------------------------------------------------- //
Expand All @@ -430,16 +446,12 @@ object NetworkControl extends AETypes {
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))
}

override def save(nbt: NBTTagCompound): Unit = {
super.save(nbt)
Platform.writeStackNBT(stack, nbt, true)
saveController(controller, nbt)
nbt.setNewTagList("links", links.map(link => link.writeToNBT _))
saveTileEntity(controller, nbt)
}
}

Expand Down Expand Up @@ -520,7 +532,7 @@ object NetworkControl extends AETypes {
override def save(nbt: NBTTagCompound): Unit = {
super.save(nbt)
nbt.setInteger("index", index)
saveController(controller, nbt)
saveTileEntity(controller, nbt)
}

override def load(nbt: NBTTagCompound): Unit = {
Expand All @@ -531,15 +543,35 @@ object NetworkControl extends AETypes {
}

//noinspection ScalaUnusedSymbol
private class CraftingStatus extends AbstractValue {
private class CraftingStatus(var controller: TileEntity with IGridProxyable with IActionHost, var node: Node) extends AdapterNode with ICraftingRequester {
def this() = this(null, null)

override def getRequestedJobs: ImmutableSet[ICraftingLink] = ImmutableSet.of()

override def jobStateChange(link: ICraftingLink): Unit = {
getNode.foreach { n =>
n.sendToReachable("computer.signal", "network_crafting_state_change", Boolean.box(link.isDone), link.getCraftingID)
}
}

override def injectCraftedItems(link: ICraftingLink, stack: IAEStack[_], p3: Actionable): IAEStack[_] = stack

override def getActionableNode: IGridNode = controller.getActionableNode

override def getCableConnectionType(side: ForgeDirection): AECableType = controller.getCableConnectionType(side)

override def securityBreak(): Unit = controller.securityBreak()

override def getGridNode(side: ForgeDirection): IGridNode = controller.getGridNode(side)

private var isComputing = true
private var link: Option[ICraftingLink] = None
private var failed = false
private var reason = "no link"
private var link: Option[ICraftingLink] = None

def setLink(value: ICraftingLink): Unit = {
def setLink(l: ICraftingLink): Unit = {
isComputing = false
link = Option(value)
link = Some(l)
}

def fail(reason: String): Unit = {
Expand All @@ -566,13 +598,23 @@ object NetworkControl extends AETypes {
link.fold(result(!failed, reason))(l => result(l.isDone))
}

@Callback(doc = "function():string -- Get Crafting ID.")
def getCraftingID(context: Context, args: Arguments): Array[AnyRef] = {
link.fold(result(null))(l => result(l.getCraftingID))
}

override def save(nbt: NBTTagCompound): Unit = {
super.save(nbt)
failed = link.fold(true)(!_.isDone)
nbt.setBoolean("failed", failed)
if (failed && reason != null) {
nbt.setString("reason", reason)
}
link.foreach(_.writeToNBT(nbt))
if (controller != null) {
val tag = new NBTTagCompound
saveTileEntity(controller, tag)
nbt.setTag("controller", tag)
}
}

override def load(nbt: NBTTagCompound): Unit = {
Expand All @@ -582,6 +624,32 @@ object NetworkControl extends AETypes {
if (failed && nbt.hasKey("reason")) {
reason = nbt.getString("reason")
}
if (nbt.hasKey("CraftID")) link = Some(AEApi.instance.storage.loadCraftingLink(nbt, this))
if (nbt.hasKey("controller")) loadController(nbt.getCompoundTag("controller"), c => {
controller = c

def bindLink(): Unit = {
try {
if (!c.isInvalid)
c.getProxy.getCrafting match {
case craftingGridCache: CraftingGridCache =>
link.foreach {
case c: CraftingLink =>
craftingGridCache.addLink(c)
case _ =>
}
case _ =>
}
} catch {
case e: GridAccessException =>
EventHandler.scheduleServer(() => {
bindLink()
})
}
}

bindLink()
})
}
}

Expand Down Expand Up @@ -628,7 +696,7 @@ object NetworkControl extends AETypes {
override def save(nbt: NBTTagCompound): Unit = {
super.save(nbt)
nbt.setInteger("index", index)
saveController(controller, nbt)
saveTileEntity(controller, nbt)
}

private var valid = true
Expand Down Expand Up @@ -686,6 +754,14 @@ object NetworkControl extends AETypes {
}

private def loadController(nbt: NBTTagCompound, f: TileEntity with IGridProxyable with IActionHost => Unit): Unit = {
loadTileEntity(nbt, {
case c: TileEntity with IGridProxyable with IActionHost =>
f(c)
case _ =>
})
}

private def loadTileEntity(nbt: NBTTagCompound, f: TileEntity => Unit): Unit = {
if (nbt.hasKey("dimension")) {
val dimension = nbt.getInteger("dimension")
val x = nbt.getInteger("x")
Expand All @@ -694,14 +770,14 @@ object NetworkControl extends AETypes {
EventHandler.scheduleServer(() => {
val world = DimensionManager.getWorld(dimension)
val tileEntity = world.getTileEntity(x, y, z)
if (tileEntity != null && tileEntity.isInstanceOf[TileEntity with IGridProxyable with IActionHost]) {
f(tileEntity.asInstanceOf[TileEntity with IGridProxyable with IActionHost])
if (tileEntity != null) {
f(tileEntity)
}
})
}
}

private def saveController(controller: TileEntity, nbt: NBTTagCompound): Unit = {
private def saveTileEntity(controller: TileEntity, nbt: NBTTagCompound): Unit = {
if (controller != null && !controller.isInvalid) {
nbt.setInteger("dimension", controller.getWorldObj.provider.dimensionId)
nbt.setInteger("x", controller.xCoord)
Expand Down