diff --git a/assets/opencomputers/lua/drivers/network.lua b/assets/opencomputers/lua/drivers/network.lua index 74eac0783..22608290c 100644 --- a/assets/opencomputers/lua/drivers/network.lua +++ b/assets/opencomputers/lua/drivers/network.lua @@ -1,6 +1,12 @@ driver.network = {} function driver.network.open(card, port) + checkArg(1, card, "string") + checkArg(2, port, "number") + return send(card, "network.open=", port) +end + +function driver.network.isOpen(card, port) checkArg(1, card, "string") checkArg(2, port, "number") return send(card, "network.open", port) @@ -8,8 +14,12 @@ end function driver.network.close(card, port) checkArg(1, card, "string") - checkArg(2, port, "number") - return send(card, "network.close", port) + if port then + checkArg(2, port, "number") + return send(card, "network.close", port) + else + return send(card, "network.close") + end end function driver.network.send(card, target, port, ...) diff --git a/li/cil/oc/api/network/Node.scala b/li/cil/oc/api/network/Node.scala index 8cc77cd70..2e8cf5f03 100644 --- a/li/cil/oc/api/network/Node.scala +++ b/li/cil/oc/api/network/Node.scala @@ -114,6 +114,20 @@ trait Node extends Persistable { None } + /** + * This is called once per tick, if the node is owned either by a computer + * (meaning it's an item component installed in a computer) or is managed by + * an adapter (meaning it was acquired via a block driver for a block whose + * tile entity is not a node). + *

+ * For nodes implemented directly in tile entities you should just call this + * from the tile entity's `updateEntity` function yourself, as necessary. + *

+ * When implementing an "inventory", i.e. something that holds item + * components, be sure to call `update` for the installed components' nodes. + */ + def update() {} + // ----------------------------------------------------------------------- // /** diff --git a/li/cil/oc/common/tileentity/Adapter.scala b/li/cil/oc/common/tileentity/Adapter.scala index 9d9c7328d..e5647b736 100644 --- a/li/cil/oc/common/tileentity/Adapter.scala +++ b/li/cil/oc/common/tileentity/Adapter.scala @@ -1,17 +1,41 @@ package li.cil.oc.common.tileentity +import dan200.computer.api.{ILuaContext, IComputerAccess, IPeripheral} import li.cil.oc.api -import li.cil.oc.api.network.{Visibility, Node} +import li.cil.oc.api.network.{Message, Visibility, Node} import li.cil.oc.server.driver import net.minecraftforge.common.ForgeDirection +import scala.collection.mutable -class Adapter extends Rotatable with Node { +class Adapter extends Rotatable with Node with IPeripheral { val name = "adapter" val visibility = Visibility.None private val blocks = Array.fill[Option[(Node, api.driver.Block)]](6)(None) + private val computers = mutable.ArrayBuffer.empty[IComputerAccess] + + private val openPorts = mutable.Map.empty[IComputerAccess, mutable.Set[Int]] + + override def updateEntity() { + for (block <- blocks) block match { + case Some((node, driver)) => node.update() + case _ => // Empty. + } + } + + override def receive(message: Message) = super.receive(message) orElse { + message.data match { + case Array(port: Int, answerPort: Double, data: AnyRef) if message.name == "network.message" => + for ((computer, ports) <- openPorts) if (ports.contains(port)) { + computer.queueEvent("modem_message", Array(computer.getAttachmentName, Int.box(port), Int.box(answerPort.toInt), data)) + } + case _ => // Ignore. + } + None + } + override protected def onConnect() { super.onConnect() neighborChanged() @@ -46,4 +70,55 @@ class Adapter extends Rotatable with Node { } } } + + // ----------------------------------------------------------------------- // + + override def getType = "oc_adapter" + + override def attach(computer: IComputerAccess) { + computers += computer + openPorts += computer -> mutable.Set.empty + } + + override def detach(computer: IComputerAccess) { + computers -= computer + openPorts -= computer + } + + override def getMethodNames = Array("open", "isOpen", "close", "closeAll", "transmit", "isWireless") + + override def callMethod(computer: IComputerAccess, context: ILuaContext, method: Int, arguments: Array[AnyRef]) = getMethodNames()(method) match { + case "open" => + val port = parsePort(arguments, 0) + if (openPorts(computer).size >= 128) + throw new IllegalArgumentException("too many open channels") + Array(Boolean.box(openPorts(computer).add(port))) + case "isOpen" => + val port = parsePort(arguments, 0) + Array(Boolean.box(openPorts(computer).contains(port))) + case "close" => + val port = parsePort(arguments, 0) + Array(Boolean.box(openPorts(computer).remove(port))) + case "closeAll" => + openPorts(computer).clear() + null + case "transmit" => + val sendPort = parsePort(arguments, 0) + val answerPort = parsePort(arguments, 1) + network.foreach(_.sendToVisible(this, "network.message", sendPort, answerPort, arguments(2))) + null + case "isWireless" => Array(Boolean.box(false)) + case _ => null + } + + override def canAttachToSide(side: Int) = true + + private def parsePort(args: Array[AnyRef], index: Int) = { + if (args.length < index - 1 || !args(index).isInstanceOf[Int]) + throw new IllegalArgumentException("bad argument #%d (number expected)".format(index)) + val port = args(index).asInstanceOf[Int] + if (port < 1 || port > 0xFFFF) + throw new IllegalArgumentException("bad argument #%d (number in [1, 65535] expected)".format(index)) + port + } } diff --git a/li/cil/oc/common/tileentity/Computer.scala b/li/cil/oc/common/tileentity/Computer.scala index c267a23af..9510a2cba 100644 --- a/li/cil/oc/common/tileentity/Computer.scala +++ b/li/cil/oc/common/tileentity/Computer.scala @@ -60,11 +60,17 @@ class Computer(isClient: Boolean) extends Rotatable with component.Computer.Envi override def updateEntity() = if (!worldObj.isRemote) { computer.update() + if (hasChanged.get) worldObj.markTileEntityChunkModified(xCoord, yCoord, zCoord, this) if (isRunning != computer.isRunning) ServerPacketSender.sendComputerState(this, computer.isRunning) isRunning = computer.isRunning + + for (component <- itemComponents) component match { + case Some(node) => node.update() + case _ => // Empty. + } } override def validate() = { diff --git a/li/cil/oc/server/component/NetworkCard.scala b/li/cil/oc/server/component/NetworkCard.scala index bae156329..4d84905e6 100644 --- a/li/cil/oc/server/component/NetworkCard.scala +++ b/li/cil/oc/server/component/NetworkCard.scala @@ -20,12 +20,18 @@ class NetworkCard extends Node { openPorts.clear() None - case Array(port: Double) if message.name == "network.open" => + case Array(port: Double) if message.name == "network.open=" => if (isPortValid(port.toInt)) result(openPorts.add(port.toInt)) else result(Unit, "invalid port number") + case Array(port: Double) if message.name == "network.open" => + if (isPortValid(port.toInt)) result(openPorts.contains(port.toInt)) + else result(Unit, "invalid port number") case Array(port: Double) if message.name == "network.close" => if (isPortValid(port.toInt)) result(openPorts.remove(port.toInt)) else result(Unit, "invalid port number") + case Array() if message.name == "network.close" => + openPorts.clear() + result(true) case Array(address: Array[Byte], port: Double, args@_*) if message.name == "network.send" => if (isPortValid(port.toInt)) network.get.sendToAddress(this, new String(address, "UTF-8"), "network.message", Seq(port.toInt) ++ args: _*)