From 19e39a2f1346df7a002791fe64a83928a02db1b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Mon, 19 Jan 2015 23:43:00 +0100 Subject: [PATCH] Extended implementation of switch CC peripheral to properly "fake" connected modems (all the remote stuffs). This means CC can now call methods on OC peripherals \o/ --- .../computercraft/SwitchPeripheral.scala | 143 ++++++++++++++---- 1 file changed, 116 insertions(+), 27 deletions(-) diff --git a/src/main/scala/li/cil/oc/integration/computercraft/SwitchPeripheral.scala b/src/main/scala/li/cil/oc/integration/computercraft/SwitchPeripheral.scala index a5ed79c98..c8d07ccd5 100644 --- a/src/main/scala/li/cil/oc/integration/computercraft/SwitchPeripheral.scala +++ b/src/main/scala/li/cil/oc/integration/computercraft/SwitchPeripheral.scala @@ -5,13 +5,90 @@ import dan200.computercraft.api.peripheral.IComputerAccess import dan200.computercraft.api.peripheral.IPeripheral import li.cil.oc.Settings import li.cil.oc.api +import li.cil.oc.api.machine.Context +import li.cil.oc.api.network.Component import li.cil.oc.common.tileentity.AccessPoint import li.cil.oc.common.tileentity.Switch import li.cil.oc.util.ResultWrapper._ +import net.minecraftforge.common.util.ForgeDirection +import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.WrapAsScala._ import scala.collection.mutable class SwitchPeripheral(val switch: Switch) extends IPeripheral { + private val methods = Map[String, (IComputerAccess, ILuaContext, Array[AnyRef]) => Array[AnyRef]]( + // Generic modem methods. + "open" -> ((computer, context, arguments) => { + val port = checkPort(arguments, 0) + if (switch.openPorts(computer).size >= 128) + throw new IllegalArgumentException("too many open channels") + result(switch.openPorts(computer).add(port)) + }), + "isOpen" -> ((computer, context, arguments) => { + val port = checkPort(arguments, 0) + result(switch.openPorts(computer).contains(port)) + }), + "close" -> ((computer, context, arguments) => { + val port = checkPort(arguments, 0) + result(switch.openPorts(computer).remove(port)) + }), + "closeAll" -> ((computer, context, arguments) => { + switch.openPorts(computer).clear() + null + }), + "transmit" -> ((computer, context, arguments) => { + val sendPort = checkPort(arguments, 0) + val answerPort = checkPort(arguments, 1) + val data = Seq(Int.box(answerPort)) ++ arguments.drop(2) + val packet = api.Network.newPacket(s"cc${computer.getID}_${computer.getAttachmentName}", null, sendPort, data.toArray) + result(switch.tryEnqueuePacket(None, packet)) + }), + "isWireless" -> ((computer, context, arguments) => { + result(switch.isInstanceOf[AccessPoint]) + }), + + // Undocumented modem messages. + "callRemote" -> ((computer, context, arguments) => { + val address = checkString(arguments, 0) + visibleComponents.find(_.address == address) match { + case Some(component) => + val method = checkString(arguments, 1) + val fakeContext = new CCContext(computer, context) + component.invoke(method, fakeContext, arguments.drop(2): _*) + case _ => null + } + }), + "getMethodsRemote" -> ((computer, context, arguments) => { + val address = checkString(arguments, 0) + visibleComponents.find(_.address == address) match { + case Some(component) => result(mapAsJavaMap(component.methods.zipWithIndex.map(t => (t._2 + 1, t._1)).toMap)) + case _ => null + } + }), + "getNamesRemote" -> ((computer, context, arguments) => { + result(mapAsJavaMap(visibleComponents.map(_.address).zipWithIndex.map(t => (t._2 + 1, t._1)).toMap)) + }), + "getTypeRemote" -> ((computer, context, arguments) => { + val address = checkString(arguments, 0) + visibleComponents.find(_.address == address) match { + case Some(component) => result(component.name) + case _ => null + } + }), + "isPresentRemote" -> ((computer, context, arguments) => { + val address = checkString(arguments, 0) + result(visibleComponents.exists(_.address == address)) + }), + + // OC specific. + "maxPacketSize" -> ((computer, context, arguments) => { + result(Settings.get.maxNetworkPacketSize) + }) + ) + + private val methodNames = methods.keys.toArray.sorted + override def getType = "modem" override def attach(computer: IComputerAccess) { @@ -24,34 +101,9 @@ class SwitchPeripheral(val switch: Switch) extends IPeripheral { switch.openPorts -= computer } - override def getMethodNames = Array("open", "isOpen", "close", "closeAll", "maxPacketSize", "transmit", "isWireless") + override def getMethodNames = methodNames - override def callMethod(computer: IComputerAccess, context: ILuaContext, method: Int, arguments: Array[AnyRef]) = getMethodNames()(method) match { - case "open" => - val port = checkPort(arguments, 0) - if (switch.openPorts(computer).size >= 128) - throw new IllegalArgumentException("too many open channels") - result(switch.openPorts(computer).add(port)) - case "isOpen" => - val port = checkPort(arguments, 0) - result(switch.openPorts(computer).contains(port)) - case "close" => - val port = checkPort(arguments, 0) - result(switch.openPorts(computer).remove(port)) - case "closeAll" => - switch.openPorts(computer).clear() - null - case "maxPacketSize" => - result(Settings.get.maxNetworkPacketSize) - case "transmit" => - val sendPort = checkPort(arguments, 0) - val answerPort = checkPort(arguments, 1) - val data = Seq(Int.box(answerPort)) ++ arguments.drop(2) - val packet = api.Network.newPacket(s"cc${computer.getID}_${computer.getAttachmentName}", null, sendPort, data.toArray) - result(switch.tryEnqueuePacket(None, packet)) - case "isWireless" => result(switch.isInstanceOf[AccessPoint]) - case _ => null - } + override def callMethod(computer: IComputerAccess, context: ILuaContext, method: Int, arguments: Array[AnyRef]) = methods(methodNames(method))(computer, context, arguments) override def equals(other: IPeripheral) = other match { case peripheral: SwitchPeripheral => peripheral.switch == switch @@ -66,4 +118,41 @@ class SwitchPeripheral(val switch: Switch) extends IPeripheral { throw new IllegalArgumentException(s"bad argument #${index + 1} (number in [1, 65535] expected)") port } + + private def checkString(args: Array[AnyRef], index: Int) = { + if (args.length < index - 1 || !args(index).isInstanceOf[String]) + throw new IllegalArgumentException(s"bad argument #${index + 1} (string expected)") + args(index).asInstanceOf[String] + } + + private def visibleComponents = { + ForgeDirection.VALID_DIRECTIONS.flatMap(side => { + val node = switch.sidedNode(side) + node.reachableNodes.collect { + case component: Component if component.canBeSeenFrom(node) => component + } + }) + } + + class CCContext(val computer: IComputerAccess, val context: ILuaContext) extends Context { + override def node() = switch.node + + override def isPaused = false + + override def stop() = false + + override def canInteract(player: String) = true + + override def signal(name: String, args: AnyRef*) = { + computer.queueEvent(name, args.toArray) + true + } + + override def pause(seconds: Double) = false + + override def isRunning = true + + override def start() = false + } + }