Conflicts:
	src/main/java/li/cil/oc/common/asm/SimpleComponentTickHandler.java
This commit is contained in:
Florian Nücke 2014-02-10 18:24:40 +01:00
commit a401858a80
14 changed files with 146 additions and 206 deletions

View File

@ -1,5 +1,6 @@
package li.cil.oc.api;
import cpw.mods.fml.common.Optional;
import dan200.computer.api.IMount;
import dan200.computer.api.IWritableMount;
import li.cil.oc.api.detail.FileSystemAPI;
@ -112,6 +113,7 @@ public final class FileSystem {
* @param mount the mount to wrap with a file system.
* @return a file system wrapping the specified mount.
*/
@Optional.Method(modid = "ComputerCraft")
public static li.cil.oc.api.fs.FileSystem fromComputerCraft(final IMount mount) {
if (instance != null) return instance.fromComputerCraft(mount);
return null;
@ -123,6 +125,7 @@ public final class FileSystem {
* @param mount the mount to wrap with a file system.
* @return a file system wrapping the specified mount.
*/
@Optional.Method(modid = "ComputerCraft")
public static li.cil.oc.api.fs.FileSystem fromComputerCraft(final IWritableMount mount) {
if (instance != null) return instance.fromComputerCraft(mount);
return null;

View File

@ -0,0 +1,85 @@
The API can be used to either interact with existing implementations in OpenComputers or to implement your own extensions for OpenComputers.
Extending OpenComputers
========================
Making a tile entity available as a component / peripheral
--------------------------------------------------
If you simply wish to expose a couple of methods that can be called from a computer if your tile entity's block is 'connected' to the computer, you can use the `SimpleComponent` interface. This interface serves as a marker for OpenComputers to know it has to inject code that converts your tile entity into a component using its class transformer. It is an interface instead of an annotation to allow stripping it, removing any dependencies on OpenComputers. Here is an example implementation:
```java
@Optional.Interface(iface = "li.cil.oc.api.network.SimpleComponent", modid = "OpenComputers")
public class TileEntityMyFancyThing extends TileEntity
implements SimpleComponent
{
@Override
public String getComponentName() {
return "fancy_thing";
}
@Callback
@Optional.Method(modid = "OpenComputers")
public Object[] greet(Context context, Arguments args) {
return new Object[]{String.format("Hello, %s!", args.checkString(0))};
}
}
```
The `getComponentName` determines with which name the tile entity will be available to computers. The `Callback` annotation tells OpenComputers to make the annotated method available to the computer. See the documentation on the `Callback` annotation for more information, in particular how it can be used to manipulate the call behavior (synchronized to the main thread vs. in the thread driving the calling computer).
So to call the greeter method, in Lua you'd do this:
```lua
print(component.fancy_thing.greet("Steve")) -- prints "Hello, Steve!"
````
More control
------------
If you really need more control over how how your tile entity interacts with OpenComputer's internal network, you will have to implement the `Environment` interface on your tile entity. There's a basic implementation in the prefab package, named `TileEntityEnvironment`. Doing so will give you access to the `Node` that connects to the component network, and you must take care of the construction of the node itself (using the factory method in `api.Network`). This allows you to make the node a `Connector` node, which will allow you to draw internal power from OpenComputers or feed energy into it. You will also be able to send messages over the component network, see the `send...` methods in the `Node` interface. See the documentation on those interfaces to get a better idea on how they work together.
Making a thrid-party block available as component / peripheral
--------------------------------------------------------------
Blocks from other mods, i.e. blocks where you have no control over the tile entity implementation, can be accessed using the Adapter block as long as there is a driver available that supports the block. If there are multiple drivers they are automatically merged. Please see the [OpenComponents][] project for examples, and consider contributing any block drivers you write to it. Thank you.
Making items available as components
------------------------------------
To make items usable as components in computers, such as cards or hard drives, you have to provide a driver for that item. This means you have to implement the `driver.Item` interface on a class and register an instance of it via the `api.Driver` registry. You can base your item drivers on the `DriverItem` prefab. Please see the example project on Github for a working example, and read the documentation of the driver interface for more information.
FileSystem API
==============
If you'd like to make some files/scripts you ship with your mod available to a computer, you can do so by wrapping those files using an OpenComputers file system. Use the factory methods in `api.FileSystem` to wrap the location your files are stored at in a file system, use the `asManagedEnvironment` methods to wrap it in a node that can be attached to the component network. For example, in an environment of a tile entity or created by an item driver you could do this in the `onConnect` method whenever a computer is connected (i.e. `node.host() instanceof Context`). Code-wise it may look something like this:
```java
public class TileEntityWithFileSystem extends TileEntityEnvironment {
private final Node fileSystem;
public TileEntityWithFileSystem() {
node = Network.newNode(this, Visibility.Network).create();
fileSystem = FileSystem.asManagedEnvironment(FileSystem.fromClass(getClass, "yourmodid/lua"), "my_files");
}
@Override
public void onConnect(final Node node) {
if (node.host() instanceof Context) {
// Attach our file system to new computers we get connected to.
// Note that this is also called for all already present computers
// when we're added to an already existing network, so we don't
// have to loop through the existing nodes manually.
node.connect(fileSystem);
}
}
@Override
public void onDisconnect(final Node node) {
if (node.host() instanceof Context) {
// Remove our file systems when we get disconnected from a
// computer.
node.disconnect(fileSystem);
} else if (node == this.node) {
// Remove the file system if we are disconnected, because in that
// case this method is only called once.
fileSystem.node.remove();
}
}
}
```
[OpenComponents]: https://github.com/MightyPirates/OpenComponents

View File

@ -1,5 +1,6 @@
package li.cil.oc.api.detail;
import cpw.mods.fml.common.Optional;
import dan200.computer.api.IMount;
import dan200.computer.api.IWritableMount;
import li.cil.oc.api.fs.FileSystem;
@ -13,8 +14,10 @@ public interface FileSystemAPI {
FileSystem fromMemory(long capacity);
@Optional.Method(modid = "ComputerCraft")
FileSystem fromComputerCraft(IMount mount);
@Optional.Method(modid = "ComputerCraft")
FileSystem fromComputerCraft(IWritableMount mount);
ManagedEnvironment asManagedEnvironment(FileSystem fs, Label label);

View File

@ -66,6 +66,16 @@ public interface Environment {
* At this point the node's network is never <tt>null</tt> and you can use
* it to query it for other nodes. Use this to perform initialization logic,
* such as building lists of nodes of a certain type in the network.
* <p/>
* For example, if node C is added to a network with nodes A and B, these
* calls are made:
* <ul>
* <li>A.onConnect(A)</li>
* <li>A.onConnect(B)</li>
* <li>A.onConnect(C)</li>
* <li>B.onConnect(A)</li>
* <li>C.onConnect(A)</li>
* </ul>
*/
void onConnect(Node node);
@ -73,11 +83,20 @@ public interface Environment {
* This is called when a node is removed from the network.
* <p/>
* This is also called for the node itself, when it has been removed from
* its network.
* its network. Note that this is called on the node that is being removed
* <em>only once</em> with the node itself as the parameter.
* <p/>
* At this point the node's network is no longer available (<tt>null</tt>).
* Use this to perform clean-up logic such as removing references to the
* removed node.
* <p/>
* For example, if node C is removed from a network with nodes A, B and C,
* these calls are made:
* <ul>
* <li>A.onDisconnect(A)</li>
* <li>B.onDisconnect(A)</li>
* <li>C.onDisconnect(A)</li>
* </ul>
*/
void onDisconnect(Node node);

View File

@ -1,13 +1,13 @@
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.
* This interface can be used with an {@link li.cil.oc.api.network.Environment}
* and is intended to be used for environments wrapping a ComputerCraft
* peripheral. Tt could be used for other purposes as well, though. 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 Environment {
public interface ManagedPeripheral {
/**
* Get the list of methods provided by this environment, in
* <em>addition</em> to methods marked as callbacks.

View File

@ -40,7 +40,13 @@ package li.cil.oc.api.network;
* public class TileEntityMyFancyThing extends TileEntity
* implements SimpleComponent
* {
* {@literal @}Override
* public String getComponentName() {
* return "fancy_thing";
* }
*
* {@literal @}Callback
* {@literal @}Optional.Method(modid = "OpenComputers")
* public Object[] greet(Context context, Arguments args) {
* return new Object[]{String.format("Hello, %s!", args.checkString(0))};
* }
@ -55,10 +61,16 @@ package li.cil.oc.api.network;
* public class TileEntityMyFancyThing extends TileEntity
* implements SimpleComponent, ManagedPeripheral
* {
* {@literal @}Override
* public String getComponentName() {
* return "fancy_thing";
* }
*
* public String[] methods() {
* return new String[] {"greet"};
* }
*
* {@literal @}Optional.Method(modid = "OpenComputers")
* public Object[] invoke(String method, Context context, Arguments args) {
* if ("greet".equals(method)) {
* return new Object[]{String.format("Hello, %s!", args.checkString(0))};

View File

@ -37,5 +37,5 @@
@cpw.mods.fml.common.API(
owner = "OpenComputers|Core",
provides = "OpenComputersAPI",
apiVersion = "1.2.0")
apiVersion = "1.3.0")
package li.cil.oc.api;

View File

@ -1,184 +0,0 @@
package li.cil.oc.api.prefab;
import com.google.common.collect.Iterables;
import dan200.computer.api.*;
import li.cil.oc.api.FileSystem;
import li.cil.oc.api.Network;
import li.cil.oc.api.network.Arguments;
import li.cil.oc.api.network.Context;
import li.cil.oc.api.network.Node;
import li.cil.oc.api.network.Visibility;
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());
node = 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 = _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.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.
*/
private static class FakeComputerAccess implements IComputerAccess {
protected final ManagedPeripheral owner;
protected final Context context;
protected final Map<String, li.cil.oc.api.network.ManagedEnvironment> fileSystems = new HashMap<String, li.cil.oc.api.network.ManagedEnvironment>();
public FakeComputerAccess(final ManagedPeripheral 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 mountWritable(final String desiredLocation, final IWritableMount mount) {
if (fileSystems.containsKey(desiredLocation)) {
return null;
}
return mount(desiredLocation, FileSystem.asManagedEnvironment(FileSystem.fromComputerCraft(mount)));
}
private String mount(final String path, final li.cil.oc.api.network.ManagedEnvironment fileSystem) {
fileSystems.put(path, fileSystem);
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.
*/
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

@ -12,6 +12,9 @@ public final class SimpleComponentTickHandler {
public static final SimpleComponentTickHandler Instance = new SimpleComponentTickHandler();
private SimpleComponentTickHandler() {
}
@SubscribeEvent
public void onTick(TickEvent.ServerTickEvent e) {
synchronized (pendingOperations) {

View File

@ -13,13 +13,13 @@ opencomputers {
# performance. Note that this needs OpenGL 1.4 to work, otherwise text
# will always just instantly disappear when moving away from the screen
# displaying it.
screenTextFadeStartDistance: 8.0
screenTextFadeStartDistance: 15.0
# The maximum distance at which to render text on screens. Rendering text
# can be pretty expensive, so if you have a lot of screens you'll want to
# avoid huge numbers here. Note that this setting is client-sided, and
# only has an impact on render performance on clients.
maxScreenTextRenderDistance: 10.0
maxScreenTextRenderDistance: 20.0
# Whether to apply linear filtering for text displayed on screens when the
# screen has to be scaled down - i.e. the text is rendered at a resolution
@ -56,7 +56,7 @@ opencomputers {
# programs blocking other computers by locking down the executor threads.
# Note that changing this won't have any effect on computers that are
# already running - they'll have to be rebooted for this to take effect.
timeout: 1.0
timeout: 5.0
# Whether to allow loading precompiled bytecode via Lua's `load` function,
# or related functions (`loadfile`, `dofile`). Enable this only if you

View File

@ -74,6 +74,8 @@ trait AbstractBusAware extends TileEntity with network.Environment { self: IBusD
abstract override def onDisconnect(node: network.Node) {
super.onDisconnect(node)
isAbstractBusAvailable = false
if (node == this.node) {
isAbstractBusAvailable = false
}
}
}

View File

@ -13,8 +13,6 @@ import li.cil.oc.util.ThreadPoolFactory
import li.cil.oc.{OpenComputers, Settings}
import net.minecraft.nbt.NBTTagCompound
import net.minecraft.server.MinecraftServer
import scala.Array
import scala.collection.convert.WrapAsScala._
import scala.collection.mutable
class InternetCard extends ManagedComponent {
@ -216,20 +214,16 @@ class InternetCard extends ManagedComponent {
override def onConnect(node: Node) {
super.onConnect(node)
if (owner.isEmpty && node.host.isInstanceOf[Context]) {
if (owner.isEmpty && node.host.isInstanceOf[Context] && node.isNeighborOf(this.node)) {
owner = Some(node.host.asInstanceOf[Context])
}
if (node == this.node) {
romInternet.foreach(rom => node.neighbors.head.connect(rom.node))
romInternet.foreach(rom => node.connect(rom.node))
}
}
override def onDisconnect(node: Node) {
super.onDisconnect(node)
if (owner.isDefined && node.host.isInstanceOf[Context] && (node.host.asInstanceOf[Context] == owner.get)) {
if (owner.isDefined && (node == this.node || node.host.isInstanceOf[Context] && (node.host.asInstanceOf[Context] == owner.get))) {
owner = None
}
if (node == this.node) {
for ((_, socket) <- connections) {
socket.close()
}

View File

@ -119,8 +119,8 @@ class UpgradeGenerator(val owner: TileEntity) extends ManagedComponent {
inventory = None
case _ =>
}
remainingTicks = 0
}
remainingTicks = 0
}
override def load(nbt: NBTTagCompound) {

View File

@ -1,5 +1,6 @@
package li.cil.oc.server.fs
import cpw.mods.fml.common.Optional
import dan200.computer.api.{IWritableMount, IMount}
import java.io
import java.net.URL
@ -64,8 +65,10 @@ object FileSystem extends api.detail.FileSystemAPI {
def fromMemory(capacity: Long): api.fs.FileSystem = new RamFileSystem(capacity)
@Optional.Method(modid = "ComputerCraft")
def fromComputerCraft(mount: IMount) = new ComputerCraftFileSystem(mount)
@Optional.Method(modid = "ComputerCraft")
def fromComputerCraft(mount: IWritableMount) = new ComputerCraftWritableFileSystem(mount)
def asManagedEnvironment(fileSystem: api.fs.FileSystem, label: Label) =