|  | package li.cil.oc.server.network | 
|  |  | 
|  | import cpw.mods.fml.common.FMLCommonHandler | 
|  | import cpw.mods.fml.relauncher.Side | 
|  | import java.lang.reflect.{Method, InvocationTargetException} | 
|  | import li.cil.oc.api | 
|  | import li.cil.oc.api.network._ | 
|  | import li.cil.oc.server.component | 
|  | import li.cil.oc.util.Persistable | 
|  | import net.minecraft.nbt.NBTTagCompound | 
|  | import scala.Some | 
|  | import scala.collection.convert.WrapAsJava._ | 
|  | import scala.collection.convert.WrapAsScala._ | 
|  | import scala.collection.{immutable, mutable} | 
|  |  | 
|  | trait Component extends api.network.Component with Persistable { | 
|  | val name: String | 
|  |  | 
|  | def visibility = _visibility | 
|  |  | 
|  | private lazy val callbacks = Component.callbacks(host.getClass) | 
|  |  | 
|  | private var _visibility = Visibility.None | 
|  |  | 
|  | def setVisibility(value: Visibility) = { | 
|  | if (value.ordinal() > reachability.ordinal()) { | 
|  | throw new IllegalArgumentException("Trying to set computer visibility to '" + value + "' on a '" + name + | 
|  | "' node with reachability '" + reachability + "'. It will be limited to the node's reachability.") | 
|  | } | 
|  | if (FMLCommonHandler.instance.getEffectiveSide == Side.SERVER) { | 
|  | if (network != null) _visibility match { | 
|  | case Visibility.Neighbors => value match { | 
|  | case Visibility.Network => addTo(reachableNodes) | 
|  | case Visibility.None => removeFrom(neighbors) | 
|  | case _ => | 
|  | } | 
|  | case Visibility.Network => value match { | 
|  | case Visibility.Neighbors => | 
|  | val neighborSet = neighbors.toSet | 
|  | removeFrom(reachableNodes.filterNot(neighborSet.contains)) | 
|  | case Visibility.None => removeFrom(reachableNodes) | 
|  | case _ => | 
|  | } | 
|  | case Visibility.None => value match { | 
|  | case Visibility.Neighbors => addTo(neighbors) | 
|  | case Visibility.Network => addTo(reachableNodes) | 
|  | case _ => | 
|  | } | 
|  | } | 
|  | _visibility = value | 
|  | } | 
|  | } | 
|  |  | 
|  | def canBeSeenFrom(other: api.network.Node) = visibility match { | 
|  | case Visibility.None => false | 
|  | case Visibility.Network => canBeReachedFrom(other) | 
|  | case Visibility.Neighbors => isNeighborOf(other) | 
|  | } | 
|  |  | 
|  | private def addTo(nodes: Iterable[api.network.Node]) = nodes.foreach(_.host match { | 
|  | case computer: component.Computer => computer.addComponent(this) | 
|  | case _ => | 
|  | }) | 
|  |  | 
|  | private def removeFrom(nodes: Iterable[api.network.Node]) = nodes.foreach(_.host match { | 
|  | case computer: component.Computer => computer.removeComponent(this) | 
|  | case _ => | 
|  | }) | 
|  |  | 
|  | // ----------------------------------------------------------------------- // | 
|  |  | 
|  | def methods = callbacks.keySet | 
|  |  | 
|  | def invoke(method: String, context: Context, arguments: AnyRef*) = | 
|  | callbacks.get(method) match { | 
|  | case Some(callback) => callback(host, context, new Component.VarArgs(Seq(arguments: _*))) | 
|  | case _ => throw new NoSuchMethodException() | 
|  | } | 
|  |  | 
|  | def isDirect(method: String) = | 
|  | callbacks.get(method) match { | 
|  | case Some(callback) => callbacks(method).direct | 
|  | case _ => throw new NoSuchMethodException() | 
|  | } | 
|  |  | 
|  | def limit(method: String) = | 
|  | callbacks.get(method) match { | 
|  | case Some(callback) => callbacks(method).limit | 
|  | case _ => throw new NoSuchMethodException() | 
|  | } | 
|  |  | 
|  | // ----------------------------------------------------------------------- // | 
|  |  | 
|  | override def load(nbt: NBTTagCompound) { | 
|  | super.load(nbt) | 
|  | if (nbt.hasKey("visibility")) { | 
|  | _visibility = Visibility.values()(nbt.getInteger("visibility")) | 
|  | } | 
|  | } | 
|  |  | 
|  | override def save(nbt: NBTTagCompound) { | 
|  | super.save(nbt) | 
|  | nbt.setInteger("visibility", _visibility.ordinal()) | 
|  | } | 
|  | } | 
|  |  | 
|  | object Component { | 
|  | private val cache = mutable.Map.empty[Class[_], immutable.Map[String, Callback]] | 
|  |  | 
|  | def callbacks(clazz: Class[_]) = cache.getOrElseUpdate(clazz, analyze(clazz)) | 
|  |  | 
|  | private def analyze(clazz: Class[_]) = { | 
|  | val callbacks = mutable.Map.empty[String, Callback] | 
|  | var c = clazz | 
|  | while (c != classOf[Object]) { | 
|  | val ms = c.getDeclaredMethods | 
|  |  | 
|  | ms.filter(_.isAnnotationPresent(classOf[LuaCallback])).foreach(m => | 
|  | if (m.getParameterTypes.size != 2 || | 
|  | (m.getParameterTypes()(0) != classOf[Context] && m.getParameterTypes()(0) != classOf[RobotContext]) || | 
|  | m.getParameterTypes()(1) != classOf[Arguments]) { | 
|  | throw new IllegalArgumentException("Invalid use of LuaCallback annotation (invalid signature).") | 
|  | } | 
|  | else if (m.getReturnType != classOf[Array[AnyRef]]) { | 
|  | throw new IllegalArgumentException("Invalid use of LuaCallback annotation (invalid signature).") | 
|  | } | 
|  | else { | 
|  | val a = m.getAnnotation[LuaCallback](classOf[LuaCallback]) | 
|  | if (a.value == null || a.value == "") { | 
|  | throw new IllegalArgumentException("Invalid use of LuaCallback annotation (name must not be null or empty).") | 
|  | } | 
|  | else if (!callbacks.contains(a.value)) { | 
|  | callbacks += a.value -> new Callback(m, a.direct, a.limit) | 
|  | } | 
|  | } | 
|  | ) | 
|  |  | 
|  | c = c.getSuperclass | 
|  | } | 
|  | callbacks.toMap | 
|  | } | 
|  |  | 
|  | // ----------------------------------------------------------------------- // | 
|  |  | 
|  | class Callback(val method: Method, val direct: Boolean, val limit: Int) { | 
|  | def apply(instance: AnyRef, context: Context, args: Arguments): Array[AnyRef] = try { | 
|  | method.invoke(instance, context, args).asInstanceOf[Array[AnyRef]] | 
|  | } catch { | 
|  | case e: InvocationTargetException => throw e.getCause | 
|  | } | 
|  | } | 
|  |  | 
|  | class VarArgs(val args: Seq[AnyRef]) extends Arguments { | 
|  | def iterator() = args.iterator | 
|  |  | 
|  | def count() = args.length | 
|  |  | 
|  | def checkAny(index: Int) = { | 
|  | checkIndex(index, "value") | 
|  | args(index) match { | 
|  | case Unit | None => null | 
|  | case arg => arg | 
|  | } | 
|  | } | 
|  |  | 
|  | def checkBoolean(index: Int) = { | 
|  | checkIndex(index, "boolean") | 
|  | args(index) match { | 
|  | case value: java.lang.Boolean => value | 
|  | case value => throw typeError(index, value, "boolean") | 
|  | } | 
|  | } | 
|  |  | 
|  | def checkDouble(index: Int) = { | 
|  | checkIndex(index, "number") | 
|  | args(index) match { | 
|  | case value: java.lang.Double => value | 
|  | case value => throw typeError(index, value, "number") | 
|  | } | 
|  | } | 
|  |  | 
|  | def checkInteger(index: Int) = { | 
|  | checkIndex(index, "number") | 
|  | args(index) match { | 
|  | case value: java.lang.Double => value.intValue | 
|  | case value => throw typeError(index, value, "number") | 
|  | } | 
|  | } | 
|  |  | 
|  | def checkString(index: Int) = { | 
|  | checkIndex(index, "string") | 
|  | args(index) match { | 
|  | case value: java.lang.String => value | 
|  | case value: Array[Byte] => new String(value, "UTF-8") | 
|  | case value => throw typeError(index, value, "string") | 
|  | } | 
|  | } | 
|  |  | 
|  | def checkByteArray(index: Int) = { | 
|  | checkIndex(index, "string") | 
|  | args(index) match { | 
|  | case value: Array[Byte] => value | 
|  | case value => throw typeError(index, value, "string") | 
|  | } | 
|  | } | 
|  |  | 
|  | def checkTable(index: Int) = { | 
|  | checkIndex(index, "table") | 
|  | args(index) match { | 
|  | case value: java.util.Map[_, _] => value | 
|  | case value: Map[_, _] => value | 
|  | case value: mutable.Map[_, _] => value | 
|  | case value => throw typeError(index, value, "table") | 
|  | } | 
|  | } | 
|  |  | 
|  | def isBoolean(index: Int) = | 
|  | index >= 0 && index < count && (args(index) match { | 
|  | case value: java.lang.Boolean => true | 
|  | case _ => false | 
|  | }) | 
|  |  | 
|  | def isDouble(index: Int) = | 
|  | index >= 0 && index < count && (args(index) match { | 
|  | case value: java.lang.Double => true | 
|  | case _ => false | 
|  | }) | 
|  |  | 
|  | def isInteger(index: Int) = | 
|  | index >= 0 && index < count && (args(index) match { | 
|  | case value: java.lang.Integer => true | 
|  | case value: java.lang.Double => true | 
|  | case _ => false | 
|  | }) | 
|  |  | 
|  | def isString(index: Int) = | 
|  | index >= 0 && index < count && (args(index) match { | 
|  | case value: java.lang.String => true | 
|  | case value: Array[Byte] => true | 
|  | case _ => false | 
|  | }) | 
|  |  | 
|  | def isByteArray(index: Int) = | 
|  | index >= 0 && index < count && (args(index) match { | 
|  | case value: Array[Byte] => true | 
|  | case _ => false | 
|  | }) | 
|  |  | 
|  | def isTable(index: Int) = | 
|  | index >= 0 && index < count && (args(index) match { | 
|  | case value: java.util.Map[_, _] => true | 
|  | case value: Map[_, _] => true | 
|  | case value: mutable.Map[_, _] => true | 
|  | case _ => false | 
|  | }) | 
|  |  | 
|  | private def checkIndex(index: Int, name: String) = | 
|  | if (index < 0) throw new IndexOutOfBoundsException() | 
|  | else if (args.length <= index) throw new IllegalArgumentException( | 
|  | "bad arguments #%d (%s expected, got no value)". | 
|  | format(index + 1, name)) | 
|  |  | 
|  | private def typeError(index: Int, have: AnyRef, want: String) = | 
|  | new IllegalArgumentException( | 
|  | "bad argument #%d (%s expected, got %s)". | 
|  | format(index + 1, want, typeName(have))) | 
|  |  | 
|  | private def typeName(value: AnyRef): String = value match { | 
|  | case null | Unit | None => "nil" | 
|  | case _: java.lang.Boolean => "boolean" | 
|  | case _: java.lang.Double => "double" | 
|  | case _: java.lang.String => "string" | 
|  | case _: Array[Byte] => "string" | 
|  | case value: java.util.Map[_, _] => "table" | 
|  | case value: Map[_, _] => "table" | 
|  | case value: mutable.Map[_, _] => "table" | 
|  | case _ => value.getClass.getSimpleName | 
|  | } | 
|  | } | 
|  |  | 
|  | } |