blob: 4fa8d96d6858985ed3aa5fd1d16352e23531dfe7 [file] [log] [blame] [raw]
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.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.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.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.Block {
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 int x, final int y, final int z) {
try {
final IPeripheral p = dan200.computercraft.ComputerCraft.getPeripheralAt(world, x, y, z, -1);
if (!isBlacklisted(p)) {
return p;
}
} catch (Exception e) {
OpenComputers.log().warn(String.format("Error accessing ComputerCraft peripheral @ (%d, %d, %d).", x, y, z), e);
}
return null;
}
@Override
public boolean worksWith(final World world, final int x, final int y, final int z) {
final TileEntity tileEntity = world.getTileEntity(x, y, z);
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, x, y, z) != null;
}
@Override
public ManagedEnvironment createEnvironment(final World world, final int x, final int y, final int z) {
return new Environment(findPeripheral(world, x, y, z));
}
public static class Environment extends li.cil.oc.api.prefab.ManagedEnvironment implements li.cil.oc.api.network.ManagedPeripheral {
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();
}
}
/**
* 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(FileSystem.fromComputerCraft(mount)));
}
@Override
public String mount(String desiredLocation, IMount mount, String driveName) {
if (fileSystems.containsKey(desiredLocation)) {
return null;
}
return mount(desiredLocation, FileSystem.asManagedEnvironment(FileSystem.fromComputerCraft(mount), driveName));
}
@Override
public String mountWritable(final String desiredLocation, final IWritableMount mount) {
if (fileSystems.containsKey(desiredLocation)) {
return null;
}
return mount(desiredLocation, FileSystem.asManagedEnvironment(FileSystem.fromComputerCraft(mount)));
}
@Override
public String mountWritable(String desiredLocation, IWritableMount mount, String driveName) {
if (fileSystems.containsKey(desiredLocation)) {
return null;
}
return mount(desiredLocation, FileSystem.asManagedEnvironment(FileSystem.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 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();
}
}
}
}