Added ComputerCraft support back to 1.12.

This commit is contained in:
Vexatos 2017-12-10 15:38:24 +01:00
parent c6cdd99d0e
commit 4c8799f38a
14 changed files with 767 additions and 0 deletions

View File

@ -67,6 +67,7 @@ repositories {
maven { url "http://maven.ic2.player.to/" }
maven { url "http://tehnut.info/maven" }
maven { url "http://maven.thiakil.com" }
maven { url "http://cc.crzd.me/maven/" }
ivy {
name 'ExtraCells'
artifactPattern "http://addons-origin.cursecdn.com/files/${config.extracells.cf}/[module]-[revision].[ext]"
@ -89,6 +90,7 @@ dependencies {
}
deobfCompile "net.industrial-craft:industrialcraft-2:${config.ic2.version}"
deobfCompile "mcp.mobius.waila:Hwyla:${config.hwyla.version}:api"
deobfCompile "dan200.computercraft:ComputerCraft:${config.cc.version}"
provided ("appeng:appliedenergistics2:${config.ae2.version}:api") {
transitive = false

View File

@ -7,6 +7,7 @@ mod.group=li.cil.oc
mod.version=1.7.1
ae2.version=rv5-stable-2
cc.version=1.80pr1-build0
extracells.cf=2500/596
extracells.version=api-1.12.2-2.5.3a25
forestry.version=5.5.0.157

View File

@ -1,5 +1,7 @@
package li.cil.oc.common.block
import java.util
import li.cil.oc.common.GuiType
import li.cil.oc.common.block.property.PropertyRotatable
import li.cil.oc.common.tileentity
@ -7,6 +9,7 @@ import li.cil.oc.integration.Mods
import li.cil.oc.util.Tooltip
import net.minecraft.block.state.BlockStateContainer
import net.minecraft.block.state.IBlockState
import net.minecraft.client.util.ITooltipFlag
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.item.ItemStack
import net.minecraft.util.EnumFacing
@ -23,6 +26,15 @@ class DiskDrive extends SimpleBlock with traits.GUI {
// ----------------------------------------------------------------------- //
override protected def tooltipTail(metadata: Int, stack: ItemStack, world: World, tooltip: util.List[String], flag: ITooltipFlag) {
super.tooltipTail(metadata, stack, world, tooltip, flag)
if (Mods.ComputerCraft.isModAvailable) {
tooltip.addAll(Tooltip.get(getClass.getSimpleName + ".CC"))
}
}
// ----------------------------------------------------------------------- //
override def guiType = GuiType.DiskDrive
override def createNewTileEntity(world: World, metadata: Int) = new tileentity.DiskDrive()

View File

@ -1,5 +1,7 @@
package li.cil.oc.common.tileentity
import com.google.common.base.Charsets
import dan200.computercraft.api.peripheral.IComputerAccess
import li.cil.oc.api.detail.ItemInfo
import li.cil.oc.api.network.Component
import li.cil.oc.server.PacketSender
@ -23,6 +25,7 @@ import li.cil.oc.common.InventorySlots
import li.cil.oc.common.Slot
import li.cil.oc.common.item
import li.cil.oc.common.item.Delegator
import li.cil.oc.integration.Mods
import li.cil.oc.integration.opencomputers.DriverLinkedCard
import li.cil.oc.server.network.QuantumNetwork
import li.cil.oc.util.ExtendedNBT._
@ -108,6 +111,19 @@ class Relay extends traits.Hub with traits.ComponentInventory with traits.PowerA
// ----------------------------------------------------------------------- //
protected def queueMessage(source: String, destination: String, port: Int, answerPort: Int, args: Array[AnyRef]) {
for (computer <- computers.map(_.asInstanceOf[IComputerAccess])) {
val address = s"cc${computer.getID}_${computer.getAttachmentName}"
if (source != address && Option(destination).forall(_ == address) && 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, Charsets.UTF_8)
case x => x
}: _*))
}
}
// ----------------------------------------------------------------------- //
override def receivePacket(packet: Packet, source: WirelessEndpoint): Unit = {
if (isWirelessEnabled) {
tryEnqueuePacket(None, packet)
@ -120,6 +136,18 @@ class Relay extends traits.Hub with traits.ComponentInventory with traits.PowerA
}
}
val computers = mutable.Buffer.empty[AnyRef]
override def tryEnqueuePacket(sourceSide: Option[EnumFacing], packet: Packet): Boolean = {
if (Mods.ComputerCraft.isModAvailable) {
packet.data.headOption match {
case Some(answerPort: java.lang.Double) => queueMessage(packet.source, packet.destination, packet.port, answerPort.toInt, packet.data.drop(1))
case _ => queueMessage(packet.source, packet.destination, packet.port, -1, packet.data)
}
}
super.tryEnqueuePacket(sourceSide, packet)
}
override protected def relayPacket(sourceSide: Option[EnumFacing], packet: Packet): Unit = {
super.relayPacket(sourceSide, packet)

View File

@ -19,6 +19,7 @@ object Mods {
def All: ArrayBuffer[ModBase] = knownMods.clone()
val AppliedEnergistics2 = new SimpleMod(IDs.AppliedEnergistics2)
val ComputerCraft = new SimpleMod(IDs.ComputerCraft)
val ExtraCells = new SimpleMod(IDs.ExtraCells, version = "@[2.5.2,)")
val Forestry = new SimpleMod(IDs.Forestry, version = "@[5.2,)")
val IndustrialCraft2 = new SimpleMod(IDs.IndustrialCraft2)
@ -45,6 +46,8 @@ object Mods {
integration.minecraft.ModMinecraft,
integration.waila.ModWaila,
integration.computercraft.ModComputerCraft,
// We go late to ensure all other mod integration is done, e.g. to
// allow properly checking if wireless redstone is present.
integration.opencomputers.ModOpenComputers
@ -72,6 +75,7 @@ object Mods {
object IDs {
final val AppliedEnergistics2 = "appliedenergistics2"
final val ComputerCraft = "computercraft"
final val ExtraCells = "extracells"
final val Forestry = "forestry"
final val Forge = "forge"

View File

@ -0,0 +1,34 @@
package li.cil.oc.integration.computercraft;
import com.google.common.collect.Iterables;
import li.cil.oc.api.machine.Arguments;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.List;
public final class CallableHelper {
private final List<String> _methods;
public CallableHelper(final String[] methods) {
_methods = Arrays.asList(methods);
}
public int methodIndex(final String method) throws NoSuchMethodException {
final int index = _methods.indexOf(method);
if (index < 0) {
throw new NoSuchMethodException();
}
return index;
}
public Object[] convertArguments(final Arguments args) throws UnsupportedEncodingException {
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");
}
}
return argArray;
}
}

View File

@ -0,0 +1,34 @@
package li.cil.oc.integration.computercraft
import dan200.computercraft.api.filesystem.IMount
import li.cil.oc.server.fs.InputStreamFileSystem
class ComputerCraftFileSystem(val mount: IMount) extends InputStreamFileSystem {
override def spaceTotal = 0
override def spaceUsed = 0
// ----------------------------------------------------------------------- //
override def exists(path: String) = mount.exists(path)
override def isDirectory(path: String) = mount.isDirectory(path)
override def lastModified(path: String) = 0L
override def list(path: String) = {
val result = new java.util.ArrayList[String]
mount.list(path, result)
result.toArray.map(_.asInstanceOf[String])
}
override def size(path: String) = mount.getSize(path)
// ----------------------------------------------------------------------- //
protected def openInputChannel(path: String) = try {
Some(new InputStreamChannel(mount.openForRead(path)))
} catch {
case _: Throwable => None
}
}

View File

@ -0,0 +1,46 @@
package li.cil.oc.integration.computercraft
import java.io.IOException
import java.io.OutputStream
import dan200.computercraft.api.filesystem.IWritableMount
import li.cil.oc.api.fs.Mode
import li.cil.oc.server.fs.OutputStreamFileSystem
class ComputerCraftWritableFileSystem(override val mount: IWritableMount)
extends ComputerCraftFileSystem(mount)
with OutputStreamFileSystem {
override def delete(path: String) = try {
mount.delete(path)
true
} catch {
case _: Throwable => false
}
override def makeDirectory(path: String) = try {
mount.makeDirectory(path)
true
} catch {
case _: Throwable => false
}
override protected def openOutputHandle(id: Int, path: String, mode: Mode): Option[OutputHandle] = try {
Some(new ComputerCraftOutputHandle(mount, mode match {
case Mode.Append => mount.openForAppend(path)
case Mode.Write => mount.openForWrite(path)
case _ => throw new IllegalArgumentException()
}, this, id, path))
} catch {
case _: Throwable => None
}
protected class ComputerCraftOutputHandle(val mount: IWritableMount, val stream: OutputStream, owner: OutputStreamFileSystem, handle: Int, path: String) extends OutputHandle(owner, handle, path) {
override def length() = mount.getSize(path)
override def position() = throw new IOException("bad file descriptor")
override def write(value: Array[Byte]) = stream.write(value)
}
}

View File

@ -0,0 +1,53 @@
package li.cil.oc.integration.computercraft;
import dan200.computercraft.api.lua.ILuaObject;
import li.cil.oc.api.driver.Converter;
import li.cil.oc.api.machine.Arguments;
import li.cil.oc.api.machine.Context;
import li.cil.oc.api.network.ManagedPeripheral;
import li.cil.oc.api.prefab.AbstractValue;
import java.util.Map;
public final class ConverterLuaObject implements Converter {
@Override
public void convert(final Object value, final Map<Object, Object> output) {
if (value instanceof ILuaObject) {
output.put("value", new LuaObjectValue((ILuaObject) value));
}
}
public static final class LuaObjectValue extends AbstractValue implements ManagedPeripheral {
private final ILuaObject value;
protected final CallableHelper helper;
// For loading when values were saved in a computer state.
public LuaObjectValue() {
value = null;
helper = null;
}
public LuaObjectValue(final ILuaObject value) {
this.value = value;
helper = new CallableHelper(value.getMethodNames());
}
@Override
public String[] methods() {
if (value != null)
return value.getMethodNames();
return new String[0]; // Loaded userdata, missing context.
}
@Override
public Object[] invoke(final String method, final Context context, final Arguments args) throws Exception {
if (value != null) {
final int index = helper.methodIndex(method);
final Object[] argArray = helper.convertArguments(args);
return value.callMethod(DriverPeripheral.Environment.UnsupportedLuaContext.instance(), index, argArray);
}
return new Object[]{null, "ComputerCraft userdata cannot be persisted"};
}
}
}

View File

@ -0,0 +1,59 @@
package li.cil.oc.integration.computercraft
import dan200.computercraft.api.filesystem.IMount
import dan200.computercraft.api.filesystem.IWritableMount
import dan200.computercraft.api.media.IMedia
import li.cil.oc
import li.cil.oc.Settings
import li.cil.oc.api.fs.FileSystem
import li.cil.oc.api.fs.Label
import li.cil.oc.api.network.EnvironmentHost
import li.cil.oc.common.Slot
import li.cil.oc.integration.opencomputers.Item
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NBTTagCompound
object DriverComputerCraftMedia extends Item {
override def worksWith(stack: ItemStack) = stack.getItem.isInstanceOf[IMedia]
override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = if (!host.world.isRemote) {
val address = addressFromTag(dataTag(stack))
val mount = fromComputerCraft(stack.getItem.asInstanceOf[IMedia].createDataMount(stack, host.world))
Option(oc.api.FileSystem.asManagedEnvironment(mount, new ComputerCraftLabel(stack), host, Settings.resourceDomain + ":floppy_access")) match {
case Some(environment) =>
environment.node.asInstanceOf[oc.server.network.Node].address = address
environment
case _ => null
}
} else null
def fromComputerCraft(mount: AnyRef): FileSystem = DriverComputerCraftMedia.createFileSystem(mount).orNull
override def slot(stack: ItemStack) = Slot.Floppy
def createFileSystem(mount: AnyRef) = Option(mount) collect {
case rw: IWritableMount => new ComputerCraftWritableFileSystem(rw)
case ro: IMount => new ComputerCraftFileSystem(ro)
}
private def addressFromTag(tag: NBTTagCompound) =
if (tag.hasKey("node") && tag.getCompoundTag("node").hasKey("address")) {
tag.getCompoundTag("node").getString("address")
}
else java.util.UUID.randomUUID().toString
class ComputerCraftLabel(val stack: ItemStack) extends Label {
val media = stack.getItem.asInstanceOf[IMedia]
override def getLabel = media.getLabel(stack)
override def setLabel(value: String) {
media.setLabel(stack, value)
}
override def load(nbt: NBTTagCompound) {}
override def save(nbt: NBTTagCompound) {}
}
}

View File

@ -0,0 +1,282 @@
package li.cil.oc.integration.computercraft;
import dan200.computercraft.api.filesystem.IMount;
import dan200.computercraft.api.filesystem.IWritableMount;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.ILuaTask;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral;
import li.cil.oc.OpenComputers;
import li.cil.oc.Settings;
import li.cil.oc.api.FileSystem;
import li.cil.oc.api.Network;
import li.cil.oc.api.driver.NamedBlock;
import li.cil.oc.api.machine.Arguments;
import li.cil.oc.api.machine.Context;
import li.cil.oc.api.network.BlacklistedPeripheral;
import li.cil.oc.api.network.ManagedEnvironment;
import li.cil.oc.api.network.Node;
import li.cil.oc.api.network.Visibility;
import li.cil.oc.util.Reflection;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.EnumFacing;
import net.minecraft.world.World;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public final class DriverPeripheral implements li.cil.oc.api.driver.DriverBlock {
private static Set<Class<?>> blacklist;
private boolean isBlacklisted(final Object o) {
// Check for our interface first, as that has priority.
if (o instanceof BlacklistedPeripheral) {
return ((BlacklistedPeripheral) o).isPeripheralBlacklisted();
}
// Delayed initialization of the resolved classes to allow registering
// additional entries via IMC.
if (blacklist == null) {
blacklist = new HashSet<Class<?>>();
for (String name : Settings.get().peripheralBlacklist()) {
final Class<?> clazz = Reflection.getClass(name);
if (clazz != null) {
blacklist.add(clazz);
}
}
}
for (Class<?> clazz : blacklist) {
if (clazz.isInstance(o))
return true;
}
return false;
}
private IPeripheral findPeripheral(final World world, final BlockPos pos, final EnumFacing side) {
try {
final IPeripheral p = dan200.computercraft.ComputerCraft.getPeripheralAt(world, pos, side);
if (!isBlacklisted(p)) {
return p;
}
} catch (Exception e) {
OpenComputers.log().warn(String.format("Error accessing ComputerCraft peripheral @ (%d, %d, %d).", pos.getX(), pos.getY(), pos.getZ()), e);
}
return null;
}
@Override
public boolean worksWith(final World world, final BlockPos pos, final EnumFacing side) {
final TileEntity tileEntity = world.getTileEntity(pos);
return tileEntity != null
// This ensures we don't get duplicate components, in case the
// tile entity is natively compatible with OpenComputers.
&& !li.cil.oc.api.network.Environment.class.isAssignableFrom(tileEntity.getClass())
// The black list is used to avoid peripherals that are known
// to be incompatible with OpenComputers when used directly.
&& !isBlacklisted(tileEntity)
// Actual check if it's a peripheral.
&& findPeripheral(world, pos, side) != null;
}
@Override
public ManagedEnvironment createEnvironment(final World world, final BlockPos pos, final EnumFacing side) {
return new Environment(findPeripheral(world, pos, side));
}
public static class Environment extends li.cil.oc.api.prefab.AbstractManagedEnvironment implements li.cil.oc.api.network.ManagedPeripheral, NamedBlock {
protected final IPeripheral peripheral;
protected final CallableHelper helper;
protected final Map<String, FakeComputerAccess> accesses = new HashMap<String, FakeComputerAccess>();
public Environment(final IPeripheral peripheral) {
this.peripheral = peripheral;
helper = new CallableHelper(peripheral.getMethodNames());
setNode(Network.newNode(this, Visibility.Network).create());
}
@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 = helper.methodIndex(method);
final Object[] argArray = helper.convertArguments(args);
final FakeComputerAccess access;
if (accesses.containsKey(context.node().address())) {
access = accesses.get(context.node().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);
access.close();
}
accesses.clear();
}
}
@Override
public String preferredName() {
return peripheral.getType();
}
@Override
public int priority() {
return -1; // Lower than 'real' OC components
}
/**
* Map interaction with the computer to our format as good as we can.
*/
public static class FakeComputerAccess implements IComputerAccess {
protected final Environment owner;
protected final Context context;
protected final Map<String, ManagedEnvironment> fileSystems = new HashMap<String, ManagedEnvironment>();
public FakeComputerAccess(final Environment owner, final Context context) {
this.owner = owner;
this.context = context;
}
public void close() {
for (li.cil.oc.api.network.ManagedEnvironment fileSystem : fileSystems.values()) {
fileSystem.node().remove();
}
fileSystems.clear();
}
@Override
public String mount(final String desiredLocation, final IMount mount) {
if (fileSystems.containsKey(desiredLocation)) {
return null;
}
return mount(desiredLocation, FileSystem.asManagedEnvironment(DriverComputerCraftMedia.fromComputerCraft(mount)));
}
@Override
public String mount(String desiredLocation, IMount mount, String driveName) {
if (fileSystems.containsKey(desiredLocation)) {
return null;
}
return mount(desiredLocation, FileSystem.asManagedEnvironment(DriverComputerCraftMedia.fromComputerCraft(mount), driveName));
}
@Override
public String mountWritable(final String desiredLocation, final IWritableMount mount) {
if (fileSystems.containsKey(desiredLocation)) {
return null;
}
return mount(desiredLocation, FileSystem.asManagedEnvironment(DriverComputerCraftMedia.fromComputerCraft(mount)));
}
@Override
public String mountWritable(String desiredLocation, IWritableMount mount, String driveName) {
if (fileSystems.containsKey(desiredLocation)) {
return null;
}
return mount(desiredLocation, FileSystem.asManagedEnvironment(DriverComputerCraftMedia.fromComputerCraft(mount), driveName));
}
private String mount(final String path, final li.cil.oc.api.network.ManagedEnvironment fileSystem) {
fileSystems.put(path, fileSystem); //TODO This is per peripheral/Environment. It would be far better with per computer
context.node().connect(fileSystem.node());
return path;
}
@Override
public void unmount(final String location) {
final li.cil.oc.api.network.ManagedEnvironment fileSystem = fileSystems.remove(location);
if (fileSystem != null) {
fileSystem.node().remove();
}
}
@Override
public int getID() {
return context.node().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.
*/
public final static class UnsupportedLuaContext implements ILuaContext {
protected static final UnsupportedLuaContext Instance = new UnsupportedLuaContext();
private UnsupportedLuaContext() {
}
public static UnsupportedLuaContext instance() {
return Instance;
}
@Override
public long issueMainThreadTask(ILuaTask task) throws LuaException {
throw new UnsupportedOperationException();
}
@Override
public Object[] executeMainThreadTask(ILuaTask task) throws LuaException, InterruptedException {
throw new UnsupportedOperationException();
}
@Override
public Object[] pullEvent(final String filter) throws LuaException, InterruptedException {
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

@ -0,0 +1,18 @@
package li.cil.oc.integration.computercraft
import li.cil.oc.api.Driver
import li.cil.oc.integration.ModProxy
import li.cil.oc.integration.Mods
object ModComputerCraft extends ModProxy {
override def getMod = Mods.ComputerCraft
override def initialize() {
PeripheralProvider.init()
Driver.add(DriverComputerCraftMedia)
Driver.add(new DriverPeripheral())
Driver.add(new ConverterLuaObject)
}
}

View File

@ -0,0 +1,20 @@
package li.cil.oc.integration.computercraft
import dan200.computercraft.api.ComputerCraftAPI
import dan200.computercraft.api.peripheral.IPeripheral
import dan200.computercraft.api.peripheral.IPeripheralProvider
import li.cil.oc.common.tileentity.Relay
import net.minecraft.util.EnumFacing
import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
object PeripheralProvider extends IPeripheralProvider {
def init() {
ComputerCraftAPI.registerPeripheralProvider(this)
}
override def getPeripheral(world: World, blockPos: BlockPos, enumFacing: EnumFacing): IPeripheral = world.getTileEntity(blockPos) match {
case relay: Relay => new RelayPeripheral(relay)
case _ => null
}
}

View File

@ -0,0 +1,174 @@
package li.cil.oc.integration.computercraft
import dan200.computercraft.api.lua.ILuaContext
import dan200.computercraft.api.lua.LuaException
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.Relay
import li.cil.oc.util.ResultWrapper._
import net.minecraft.util.EnumFacing
import scala.collection.convert.WrapAsJava._
import scala.collection.convert.WrapAsScala._
import scala.collection.mutable
class RelayPeripheral(val relay: Relay) 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 (relay.openPorts(computer).size >= 128)
throw new IllegalArgumentException("too many open channels")
result(relay.openPorts(computer).add(port))
}),
"isOpen" -> ((computer, context, arguments) => {
val port = checkPort(arguments, 0)
result(relay.openPorts(computer).contains(port))
}),
"close" -> ((computer, context, arguments) => {
val port = checkPort(arguments, 0)
result(relay.openPorts(computer).remove(port))
}),
"closeAll" -> ((computer, context, arguments) => {
relay.openPorts(computer).clear()
null
}),
"transmit" -> ((computer, context, arguments) => {
val sendPort = checkPort(arguments, 0)
val answerPort = checkPort(arguments, 1)
val data = arguments.drop(2) ++ Seq(Int.box(answerPort))
val packet = api.Network.newPacket(s"cc${computer.getID}_${computer.getAttachmentName}", null, sendPort, data.toArray)
result(relay.tryEnqueuePacket(None, packet))
}),
"isWireless" -> ((computer, context, arguments) => {
// Let's pretend we're always wired, to allow accessing OC components
// as remote peripherals when using an Access Point, too...
result(false)
}),
// 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.
"isAccessPoint" -> ((computer, context, arguments) => {
result(relay.isWirelessEnabled)
}),
"isTunnel" -> ((computer, context, arguments) => {
result(relay.isLinkedEnabled)
}),
"maxPacketSize" -> ((computer, context, arguments) => {
result(Settings.get.maxNetworkPacketSize)
})
)
private val methodNames = methods.keys.toArray.sorted
override def getType = "modem"
override def attach(computer: IComputerAccess) {
relay.computers += computer
relay.openPorts += computer -> mutable.Set.empty
}
override def detach(computer: IComputerAccess) {
relay.computers -= computer
relay.openPorts -= computer
}
override def getMethodNames = methodNames
override def callMethod(computer: IComputerAccess, context: ILuaContext, method: Int, arguments: Array[AnyRef]) =
try methods(methodNames(method))(computer, context, arguments) catch {
case e: LuaException => throw e
case t: Throwable =>
t.printStackTrace()
throw new LuaException(t.getMessage)
}
override def equals(other: IPeripheral) = other match {
case peripheral: RelayPeripheral => peripheral.relay == relay
case _ => false
}
private def checkPort(args: Array[AnyRef], index: Int) = {
if (args.length <= index || !args(index).isInstanceOf[Number])
throw new IllegalArgumentException(s"bad argument #${index + 1} (number expected)")
val port = args(index).asInstanceOf[Double].toInt
if (port < 0 || port > 0xFFFF)
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 || !args(index).isInstanceOf[String])
throw new IllegalArgumentException(s"bad argument #${index + 1} (string expected)")
args(index).asInstanceOf[String]
}
private def visibleComponents = {
EnumFacing.values().flatMap(side => {
val node = relay.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() = relay.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
override def consumeCallBudget(callCost: Double): Unit = {}
}
}