| package li.cil.oc.common.tileentity |
| |
| import cpw.mods.fml.common.{Loader, Optional} |
| import dan200.computer.api.{ILuaContext, IComputerAccess, IPeripheral} |
| import li.cil.oc.api.network._ |
| import li.cil.oc.server.driver |
| import li.cil.oc.{Settings, api} |
| import net.minecraft.nbt.{NBTTagList, NBTTagCompound} |
| import net.minecraftforge.common.ForgeDirection |
| import scala.collection.convert.WrapAsScala._ |
| import scala.collection.mutable |
| import scala.{Array, Some} |
| |
| @Optional.Interface(iface = "dan200.computer.api.IPeripheral", modid = "ComputerCraft") |
| class Adapter extends Environment with IPeripheral { |
| val node = api.Network.newNode(this, Visibility.Network).create() |
| |
| private val blocks = Array.fill[Option[(ManagedEnvironment, api.driver.Block)]](6)(None) |
| |
| private val blocksData = Array.fill[Option[BlockData]](6)(None) |
| |
| // ----------------------------------------------------------------------- // |
| |
| override def updateEntity() { |
| super.updateEntity() |
| for (block <- blocks) block match { |
| case Some((environment, _)) if environment.canUpdate => environment.update() |
| case _ => // Empty. |
| } |
| } |
| |
| def neighborChanged() = if (node != null && node.network != null) { |
| for (d <- ForgeDirection.VALID_DIRECTIONS) { |
| val (x, y, z) = (this.x + d.offsetX, this.y + d.offsetY, this.z + d.offsetZ) |
| driver.Registry.driverFor(world, x, y, z) match { |
| case Some(newDriver) => blocks(d.ordinal()) match { |
| case Some((oldEnvironment, driver)) => |
| if (newDriver != driver) { |
| // This is... odd. Maybe moved by some other mod? |
| node.disconnect(oldEnvironment.node) |
| val environment = newDriver.createEnvironment(world, x, y, z) |
| blocks(d.ordinal()) = Some((environment, newDriver)) |
| blocksData(d.ordinal()) = Some(new BlockData(environment.getClass.getName, new NBTTagCompound())) |
| node.connect(environment.node) |
| } // else: the more things change, the more they stay the same. |
| case _ => |
| // A challenger appears. |
| val environment = newDriver.createEnvironment(world, x, y, z) |
| blocks(d.ordinal()) = Some((environment, newDriver)) |
| blocksData(d.ordinal()) match { |
| case Some(data) if data.name == environment.getClass.getName => |
| environment.load(data.data) |
| case _ => |
| } |
| blocksData(d.ordinal()) = Some(new BlockData(environment.getClass.getName, new NBTTagCompound())) |
| node.connect(environment.node) |
| } |
| case _ => blocks(d.ordinal()) match { |
| case Some((environment, driver)) => |
| // We had something there, but it's gone now... |
| node.disconnect(environment.node) |
| environment.save(blocksData(d.ordinal()).get.data) |
| blocks(d.ordinal()) = None |
| case _ => // Nothing before, nothing now. |
| } |
| } |
| } |
| } |
| |
| // ----------------------------------------------------------------------- // |
| |
| @LuaCallback("send") |
| @Optional.Method(modid = "ComputerCraft") |
| def send(context: Context, args: Arguments) = { |
| val port = args.checkInteger(0) |
| val answerPort = args.checkInteger(1) |
| for ((computer: IComputerAccess, ports) <- openPorts if ports.contains(port)) { |
| computer.queueEvent("modem_message", Array(Seq(computer.getAttachmentName, Int.box(port), Int.box(answerPort)) ++ args.drop(2): _*)) |
| } |
| null |
| } |
| |
| // ----------------------------------------------------------------------- // |
| |
| override def onConnect(node: Node) { |
| super.onConnect(node) |
| if (node == this.node) { |
| neighborChanged() |
| } |
| } |
| |
| override def onMessage(message: Message) { |
| super.onMessage(message) |
| if (Loader.isModLoaded("ComputerCraft")) { |
| if (message.name == "network.message") message.data match { |
| case Array(port: Integer, answerPort: java.lang.Double, args@_*) => |
| for (computer <- computers.map(_.asInstanceOf[IComputerAccess])) { |
| if (openPorts(computer).contains(port)) |
| computer.queueEvent("modem_message", Array(Seq(computer.getAttachmentName, Int.box(port), Int.box(answerPort.toInt)) ++ args.map { |
| case x: Array[Byte] => new String(x, "UTF-8") |
| case x => x |
| }: _*)) |
| } |
| case _ => |
| } |
| } |
| } |
| |
| // ----------------------------------------------------------------------- // |
| |
| override def readFromNBT(nbt: NBTTagCompound) { |
| super.readFromNBT(nbt) |
| |
| val blocksNbt = nbt.getTagList(Settings.namespace + "adapter.blocks") |
| (0 until (blocksNbt.tagCount min blocksData.length)). |
| map(blocksNbt.tagAt). |
| map(_.asInstanceOf[NBTTagCompound]). |
| zipWithIndex. |
| foreach { |
| case (blockNbt, i) => |
| if (blockNbt.hasKey("name") && blockNbt.hasKey("data")) { |
| blocksData(i) = Some(new BlockData(blockNbt.getString("name"), blockNbt.getCompoundTag("data"))) |
| } |
| } |
| } |
| |
| override def writeToNBT(nbt: NBTTagCompound) { |
| super.writeToNBT(nbt) |
| |
| val blocksNbt = new NBTTagList() |
| for (i <- 0 until blocks.length) { |
| val blockNbt = new NBTTagCompound() |
| blocksData(i) match { |
| case Some(data) => |
| blocks(i) match { |
| case Some((environment, _)) => environment.save(data.data) |
| case _ => |
| } |
| blockNbt.setString("name", data.name) |
| blockNbt.setCompoundTag("data", data.data) |
| case _ => |
| } |
| blocksNbt.appendTag(blockNbt) |
| } |
| nbt.setTag(Settings.namespace + "adapter.blocks", blocksNbt) |
| } |
| |
| // ----------------------------------------------------------------------- // |
| |
| private val computers = mutable.ArrayBuffer.empty[AnyRef] |
| |
| private val openPorts = mutable.Map.empty[AnyRef, mutable.Set[Int]] |
| |
| @Optional.Method(modid = "ComputerCraft") |
| override def getType = "oc_adapter" |
| |
| @Optional.Method(modid = "ComputerCraft") |
| override def attach(computer: IComputerAccess) { |
| computers += computer |
| openPorts += computer -> mutable.Set.empty |
| } |
| |
| @Optional.Method(modid = "ComputerCraft") |
| override def detach(computer: IComputerAccess) { |
| computers -= computer |
| openPorts -= computer |
| } |
| |
| @Optional.Method(modid = "ComputerCraft") |
| override def getMethodNames = Array("open", "isOpen", "close", "closeAll", "transmit", "isWireless") |
| |
| @Optional.Method(modid = "ComputerCraft") |
| override def callMethod(computer: IComputerAccess, context: ILuaContext, method: Int, arguments: Array[AnyRef]) = getMethodNames()(method) match { |
| case "open" => |
| val port = checkPort(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 = checkPort(arguments, 0) |
| Array(Boolean.box(openPorts(computer).contains(port))) |
| case "close" => |
| val port = checkPort(arguments, 0) |
| Array(Boolean.box(openPorts(computer).remove(port))) |
| case "closeAll" => |
| openPorts(computer).clear() |
| null |
| case "transmit" => |
| val sendPort = checkPort(arguments, 0) |
| val answerPort = checkPort(arguments, 1) |
| node.sendToReachable("network.message", Seq(Int.box(sendPort), Int.box(answerPort)) ++ arguments.drop(2): _*) |
| null |
| case "isWireless" => Array(java.lang.Boolean.FALSE) |
| case _ => null |
| } |
| |
| @Optional.Method(modid = "ComputerCraft") |
| override def canAttachToSide(side: Int) = true |
| |
| private def checkPort(args: Array[AnyRef], index: Int) = { |
| if (args.length < index - 1 || !args(index).isInstanceOf[Double]) |
| throw new IllegalArgumentException("bad argument #%d (number expected)".format(index + 1)) |
| val port = args(index).asInstanceOf[Double].toInt |
| if (port < 1 || port > 0xFFFF) |
| throw new IllegalArgumentException("bad argument #%d (number in [1, 65535] expected)".format(index + 1)) |
| port |
| } |
| |
| // ----------------------------------------------------------------------- // |
| |
| private class BlockData(val name: String, val data: NBTTagCompound) |
| |
| } |