blob: aa308f40773771fb85876b689d92b33f7e15c3c9 [file] [log] [blame] [raw]
package li.cil.oc.common.tileentity
import cpw.mods.fml.common.{Loader, Optional}
import cpw.mods.fml.relauncher.{Side, SideOnly}
import dan200.computer.api.{ILuaContext, IComputerAccess, IPeripheral}
import li.cil.oc.api.network.{Node, Message, Visibility}
import li.cil.oc.util.ExtendedNBT._
import li.cil.oc.{Blocks, Settings, api}
import net.minecraft.nbt.NBTTagCompound
import net.minecraftforge.common.ForgeDirection
import scala.collection.mutable
@Optional.Interface(iface = "dan200.computer.api.IPeripheral", modid = "ComputerCraft")
class Router extends net.minecraft.tileentity.TileEntity with api.network.SidedEnvironment with IPeripheral {
private val plugs = ForgeDirection.VALID_DIRECTIONS.map(side => new Plug(side))
// ----------------------------------------------------------------------- //
@SideOnly(Side.CLIENT)
def canConnect(side: ForgeDirection) = true
def sidedNode(side: ForgeDirection) = plugs(side.ordinal()).node
// ----------------------------------------------------------------------- //
override def canUpdate = false
override def validate() {
super.validate()
worldObj.scheduleBlockUpdateFromLoad(xCoord, yCoord, zCoord, Blocks.router.parent.blockID, Int.MinValue, 0)
}
override def invalidate() {
super.invalidate()
for (plug <- plugs if plug.node != null) {
plug.node.remove()
}
}
override def onChunkUnload() {
super.onChunkUnload()
for (plug <- plugs if plug.node != null) {
plug.node.remove()
}
}
// ----------------------------------------------------------------------- //
override def readFromNBT(nbt: NBTTagCompound) {
super.readFromNBT(nbt)
nbt.getTagList(Settings.namespace + "plugs").iterator[NBTTagCompound].zip(plugs).foreach {
case (plugNbt, plug) => plug.node.load(plugNbt)
}
}
override def writeToNBT(nbt: NBTTagCompound) {
super.writeToNBT(nbt)
nbt.setNewTagList(Settings.namespace + "plugs", plugs.map(plug => {
val plugNbt = new NBTTagCompound()
plug.node.save(plugNbt)
plugNbt
}))
}
// ----------------------------------------------------------------------- //
// Peripheral
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)
plugs.foreach(_.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 def queueMessage(port: Int, answerPort: Int, args: Seq[AnyRef]) {
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)) ++ args.map {
case x: Array[Byte] => new String(x, "UTF-8")
case x => x
}: _*))
}
}
private class Plug(val side: ForgeDirection) extends api.network.Environment {
val node = api.Network.newNode(this, Visibility.Network).create()
def onMessage(message: Message) {
if (isPrimary && message.name == "network.message") {
plugsInOtherNetworks.foreach(_.node.sendToReachable(message.name, message.data: _*))
if (Loader.isModLoaded("ComputerCraft")) {
message.data match {
case Array(port: Integer, answerPort: java.lang.Double, args@_*) =>
queueMessage(port, answerPort.toInt, args)
case Array(port: Integer, args@_*) =>
queueMessage(port, -1, args)
case _ =>
}
}
}
}
def onDisconnect(node: Node) {}
def onConnect(node: Node) {}
private def isPrimary = plugs(plugs.indexWhere(_.node.network == node.network)) == this
private def plugsInOtherNetworks = plugs.filter(_.node.network != node.network)
}
}