This commit is contained in:
Florian Nücke 2014-02-08 01:48:59 +01:00
commit e39bb6ab6b
4 changed files with 241 additions and 17 deletions

View File

@ -81,6 +81,5 @@ public interface Component extends Node {
* @return the list of results, or <tt>null</tt> if there is no result.
* @throws NoSuchMethodException if there is no method with that name.
*/
Object[] invoke(String method, Context context, Object... arguments)
throws NoSuchMethodException;
Object[] invoke(String method, Context context, Object... arguments) throws Exception;
}

View File

@ -0,0 +1,35 @@
package li.cil.oc.api.network;
/**
* This specialization of the managed environment is intended to be used for
* environments wrapping a ComputerCraft peripheral, although it could be used
* for other purposes as well. It allows providing method names in addition to
* those defined via the {@link li.cil.oc.api.network.Callback} annotation, and
* invoking said methods.
*/
public interface ManagedPeripheral extends ManagedEnvironment {
/**
* Get the list of methods provided by this environment, in
* <em>addition</em> to methods marked as callbacks.
* <p/>
* Returning <tt>null</tt> has the same meaning as returning an empty array,
* that being that there are no methods - in which case you don't need this
* interface!
*
* @return the list of methods provided by the environment.
*/
String[] methods();
/**
* Calls a method from the list provided by {@link #methods()}.
* <p/>
*
* @param method the name of the method to call.
* @param context the context from which the method is called.
* @param args the arguments to pass to the method.
* @return the result of calling the method. Same as for callbacks.
* @throws java.lang.NoSuchMethodException if there is no method with the
* specified name.
*/
Object[] invoke(String method, Context context, Arguments args) throws Exception;
}

View File

@ -0,0 +1,156 @@
package li.cil.oc.api.prefab;
import com.google.common.collect.Iterables;
import dan200.computer.api.*;
import li.cil.oc.api.network.Arguments;
import li.cil.oc.api.network.Context;
import li.cil.oc.api.network.Node;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Implementation of the <tt>ManagedPeripheral</tt> interface for simple
* wrapping of ComputerCraft peripherals.
*/
public class ManagedPeripheral extends ManagedEnvironment implements li.cil.oc.api.network.ManagedPeripheral {
protected final IPeripheral peripheral;
protected final List<String> _methods;
protected final Map<String, FakeComputerAccess> accesses = new HashMap<String, FakeComputerAccess>();
public ManagedPeripheral(final IPeripheral peripheral) {
this.peripheral = peripheral;
_methods = Arrays.asList(peripheral.getMethodNames());
}
@Override
public String[] methods() {
return peripheral.getMethodNames();
}
@Override
public Object[] invoke(final String method, final Context context, final Arguments args) throws Exception {
final int index = _methods.indexOf(method);
if (index < 0) {
throw new NoSuchMethodException();
}
final Object[] argArray = Iterables.toArray(args, Object.class);
for (int i = 0; i < argArray.length; ++i) {
if (argArray[i] instanceof byte[]) {
argArray[i] = new String((byte[]) argArray[i], "UTF-8");
}
}
final FakeComputerAccess access;
if (accesses.containsKey(context.address())) {
access = accesses.get(context.address());
} else {
// The calling contexts is not visible to us, meaning we never got
// an onConnect for it. Create a temporary access.
access = new FakeComputerAccess(this, context);
}
return peripheral.callMethod(access, UnsupportedLuaContext.instance(), index, argArray);
}
@Override
public void onConnect(final Node node) {
super.onConnect(node);
if (node.host() instanceof Context) {
final FakeComputerAccess access = new FakeComputerAccess(this, (Context) node.host());
accesses.put(node.address(), access);
peripheral.attach(access);
}
}
@Override
public void onDisconnect(final Node node) {
super.onDisconnect(node);
if (node.host() instanceof Context) {
final FakeComputerAccess access = accesses.remove(node.address());
if (access != null) {
peripheral.detach(access);
}
} else if (node == this.node) {
for (FakeComputerAccess access : accesses.values()) {
peripheral.detach(access);
}
accesses.clear();
}
}
/**
* Map interaction with the computer to our format as good as we can.
*/
private static class FakeComputerAccess implements IComputerAccess {
protected final ManagedPeripheral owner;
protected final Context context;
public FakeComputerAccess(final ManagedPeripheral owner, final Context context) {
this.owner = owner;
this.context = context;
}
@Override
public String mount(final String desiredLocation, final IMount mount) {
throw new UnsupportedOperationException();
}
@Override
public String mountWritable(final String desiredLocation, final IWritableMount mount) {
throw new UnsupportedOperationException();
}
@Override
public void unmount(final String location) {
throw new UnsupportedOperationException();
}
@Override
public int getID() {
return context.address().hashCode();
}
@Override
public void queueEvent(final String event, final Object[] arguments) {
context.signal(event, arguments);
}
@Override
public String getAttachmentName() {
return owner.node.address();
}
}
/**
* Since we abstract away anything language specific, we cannot support the
* Lua context specific operations ComputerCraft provides.
*/
private final static class UnsupportedLuaContext implements ILuaContext {
protected static final UnsupportedLuaContext Instance = new UnsupportedLuaContext();
private UnsupportedLuaContext() {
}
public static UnsupportedLuaContext instance() {
return Instance;
}
@Override
public Object[] pullEvent(final String filter) throws Exception {
throw new UnsupportedOperationException();
}
@Override
public Object[] pullEventRaw(final String filter) throws InterruptedException {
throw new UnsupportedOperationException();
}
@Override
public Object[] yield(final Object[] arguments) throws InterruptedException {
throw new UnsupportedOperationException();
}
}
}

View File

@ -8,6 +8,7 @@ import li.cil.oc.api.network
import li.cil.oc.api.network.{Node => ImmutableNode, _}
import li.cil.oc.server.component.machine.Machine
import li.cil.oc.server.driver.CompoundBlockEnvironment
import li.cil.oc.server.network.Component.{PeripheralCallback, ComponentCallback}
import net.minecraft.nbt.NBTTagCompound
import scala.Some
import scala.collection.convert.WrapAsJava._
@ -24,18 +25,26 @@ trait Component extends network.Component with Node {
private lazy val hosts = host match {
case multi: CompoundBlockEnvironment =>
callbacks.map {
case (method, callback) =>
multi.environments.find {
case (_, environment) => environment.getClass == callback.method.getDeclaringClass
} match {
case Some((_, environment)) => method -> Some(environment)
case _ => method -> None
}
}
case _ =>
callbacks.map {
case (method, callback) => method -> Some(host)
case (method, callback) => callback match {
case component: ComponentCallback =>
multi.environments.find {
case (_, environment) => environment.getClass == component.method.getDeclaringClass
} match {
case Some((_, environment)) => method -> Some(environment)
case _ => method -> None
}
case peripheral: PeripheralCallback =>
multi.environments.find {
case (_, environment: ManagedPeripheral) => environment.methods.contains(peripheral.name)
} match {
case Some((_, environment)) => method -> Some(environment)
case _ => method -> None
}
}
}
case _ => callbacks.map {
case (method, callback) => method -> Some(host)
}
}
private var _visibility = Visibility.None
@ -130,6 +139,7 @@ object Component {
def callbacks(host: Environment) = host match {
case multi: CompoundBlockEnvironment => analyze(host)
case peripheral: ManagedPeripheral => analyze(host)
case _ => cache.getOrElseUpdate(host.getClass, analyze(host))
}
@ -137,8 +147,20 @@ object Component {
val callbacks = mutable.Map.empty[String, Callback]
val seeds = host match {
case multi: CompoundBlockEnvironment => multi.environments.map {
case (_, environment) => environment.getClass: Class[_]
case (_, environment) =>
environment match {
case peripheral: ManagedPeripheral => for (name <- peripheral.methods() if !callbacks.contains(name)) {
callbacks += name -> new PeripheralCallback(name)
}
case _ =>
}
environment.getClass: Class[_]
}
case peripheral: ManagedPeripheral =>
for (name <- peripheral.methods() if !callbacks.contains(name)) {
callbacks += name -> new PeripheralCallback(name)
}
Seq(host.getClass: Class[_])
case _ => Seq(host.getClass: Class[_])
}
for (seed <- seeds) {
@ -162,7 +184,7 @@ object Component {
val a = m.getAnnotation[network.Callback](classOf[network.Callback])
val name = if (a.value != null && a.value.trim != "") a.value else m.getName
if (!callbacks.contains(name)) {
callbacks += name -> new Callback(m, a.direct, a.limit)
callbacks += name -> new ComponentCallback(m, a.direct, a.limit)
}
}
)
@ -175,14 +197,26 @@ object Component {
// ----------------------------------------------------------------------- //
class Callback(val method: Method, val direct: Boolean, val limit: Int) {
def apply(instance: AnyRef, context: Context, args: Arguments): Array[AnyRef] = try {
abstract class Callback(val direct: Boolean, val limit: Int) {
def apply(instance: Environment, context: Context, args: Arguments): Array[AnyRef]
}
class ComponentCallback(val method: Method, direct: Boolean, limit: Int) extends Callback(direct, limit) {
override def apply(instance: Environment, context: Context, args: Arguments) = try {
method.invoke(instance, context, args).asInstanceOf[Array[AnyRef]]
} catch {
case e: InvocationTargetException => throw e.getCause
}
}
class PeripheralCallback(val name: String) extends Callback(true, 100) {
override def apply(instance: Environment, context: Context, args: Arguments) =
instance match {
case peripheral: ManagedPeripheral => peripheral.invoke(name, context, args)
case _ => throw new NoSuchMethodException()
}
}
class VarArgs(val args: Seq[AnyRef]) extends Arguments {
def iterator() = args.iterator