diff --git a/src/main/java/li/cil/oc2/api/API.java b/src/main/java/li/cil/oc2/api/API.java index 76e89fc8..0d547666 100644 --- a/src/main/java/li/cil/oc2/api/API.java +++ b/src/main/java/li/cil/oc2/api/API.java @@ -1,10 +1,10 @@ package li.cil.oc2.api; import com.google.gson.GsonBuilder; -import li.cil.oc2.api.bus.device.DeviceMethod; import li.cil.oc2.api.bus.device.object.Callback; -import li.cil.oc2.api.provider.DeviceInterfaceProvider; +import li.cil.oc2.api.bus.device.rpc.RPCMethod; import li.cil.oc2.api.imc.DeviceMethodParameterTypeAdapter; +import li.cil.oc2.api.provider.DeviceProvider; import java.lang.reflect.Type; @@ -12,7 +12,7 @@ public final class API { public static final String MOD_ID = "oc2"; /** - * IMC message for registering a {@link DeviceInterfaceProvider}. + * IMC message for registering a {@link DeviceProvider}. *
* Example: *
@@ -27,11 +27,11 @@ public final class API {
*
* Must be called with a supplier that provides an instance of {@link DeviceMethodParameterTypeAdapter}.
*
- * It can be necessary to register additional serializers when implementing {@link DeviceMethod}s
+ * It can be necessary to register additional serializers when implementing {@link RPCMethod}s
* that use custom parameter types.
*
* @see GsonBuilder#registerTypeAdapter(Type, Object)
- * @see DeviceMethod
+ * @see RPCMethod
* @see Callback
*/
public static final String IMC_ADD_DEVICE_METHOD_PARAMETER_TYPE_ADAPTER = "addDeviceMethodParameterTypeAdapter";
diff --git a/src/main/java/li/cil/oc2/api/bus/Device.java b/src/main/java/li/cil/oc2/api/bus/Device.java
new file mode 100644
index 00000000..8a6c8851
--- /dev/null
+++ b/src/main/java/li/cil/oc2/api/bus/Device.java
@@ -0,0 +1,14 @@
+package li.cil.oc2.api.bus;
+
+/**
+ * Base interface for objects that can be registered as devices on a {@link DeviceBus}.
+ *
+ * Which types are handled/supported by a bus depends on the {@link DeviceBusController}
+ * managing the bus.
+ *
+ * Note that it is strongly encouraged for implementations to provide an overloaded
+ * {@link #equals(Object)} and {@link #hashCode()} so that identical devices can be
+ * detected.
+ */
+public interface Device {
+}
diff --git a/src/main/java/li/cil/oc2/api/bus/DeviceBus.java b/src/main/java/li/cil/oc2/api/bus/DeviceBus.java
index 4605c6e5..0fe4e8a0 100644
--- a/src/main/java/li/cil/oc2/api/bus/DeviceBus.java
+++ b/src/main/java/li/cil/oc2/api/bus/DeviceBus.java
@@ -1,12 +1,11 @@
package li.cil.oc2.api.bus;
-import li.cil.oc2.api.bus.device.Device;
-import li.cil.oc2.api.bus.device.DeviceInterface;
+import li.cil.oc2.api.bus.device.rpc.RPCDevice;
import java.util.Collection;
/**
- * A device bus provides the interface by which {@link DeviceInterface} can be made available
+ * A device bus provides the interface by which {@link RPCDevice} can be made available
* to a {@link DeviceBusController}, which is usually used by VMs to access devices.
*/
public interface DeviceBus {
@@ -58,10 +57,4 @@ public interface DeviceBus {
* have to interact with this interface.
*/
void scheduleScan();
-
-// long addDevice(MemoryMappedDevice device);
-//
-// void addDevice(final long address, MemoryMappedDevice device);
-//
-// void removeDevice(MemoryMappedDevice device);
}
diff --git a/src/main/java/li/cil/oc2/api/bus/DeviceBusController.java b/src/main/java/li/cil/oc2/api/bus/DeviceBusController.java
index fa377b85..f2d7db66 100644
--- a/src/main/java/li/cil/oc2/api/bus/DeviceBusController.java
+++ b/src/main/java/li/cil/oc2/api/bus/DeviceBusController.java
@@ -1,10 +1,6 @@
package li.cil.oc2.api.bus;
-import li.cil.oc2.api.bus.device.Device;
-import li.cil.oc2.api.bus.device.DeviceInterface;
-
-import java.util.Collection;
-import java.util.Optional;
+import java.util.Set;
import java.util.UUID;
/**
@@ -13,7 +9,7 @@ import java.util.UUID;
* {@link DeviceBusElement#addController(DeviceBusController)}.
*
* This interface is usually provided by VM containers and used to collect connected
- * {@link DeviceInterface}s by aggregating the devices that were added to the device bus elements
+ * {@link Device}s by aggregating the devices that were added to the device bus elements
* via {@link DeviceBus#addDevice(Device)}.
*
* The only way for {@link DeviceBusElement}s to be added to a bus is for a
@@ -52,18 +48,23 @@ public interface DeviceBusController {
/**
* The list of all devices currently known to this controller.
*
- * This is the aggregation of all {@link DeviceInterface} added to all {@link DeviceBusElement}s known
+ * This is the aggregation of all {@link Device}s added to all {@link DeviceBusElement}s known
* to the controller as found during the last scan scheduled via {@link #scheduleBusScan()}.
*
* @return the list of all devices on the bus managed by this controller.
*/
- Collection getDevices();
+ Set getDevices();
/**
- * Get the device with the specified unique identifier, if possible.
+ * Obtain the unique identifiers for the specified device, if any, as
+ * provided by the {@link DeviceBusElement}s that provided this device.
+ *
+ * If the device was added to multiple {@link DeviceBusElement}s this
+ * may return multiple {@link UUID}s.
*
- * @param uuid the id of the device to get.
- * @return the device with the specified id, if possible.
+ * @param device the device to get the identifiers for.
+ * @return the identifiers for the device, if any.
+ * @see DeviceBusElement#getDeviceIdentifier(Device)
*/
- Optional getDevice(final UUID uuid);
+ Set getDeviceIdentifiers(Device device);
}
diff --git a/src/main/java/li/cil/oc2/api/bus/DeviceBusElement.java b/src/main/java/li/cil/oc2/api/bus/DeviceBusElement.java
index 9df659f1..e619f3a4 100644
--- a/src/main/java/li/cil/oc2/api/bus/DeviceBusElement.java
+++ b/src/main/java/li/cil/oc2/api/bus/DeviceBusElement.java
@@ -1,8 +1,10 @@
package li.cil.oc2.api.bus;
-import li.cil.oc2.api.bus.device.Device;
+import li.cil.oc2.api.bus.device.rpc.RPCDevice;
import java.util.Collection;
+import java.util.Optional;
+import java.util.UUID;
/**
* Represents a single connection point on a device bus.
@@ -33,14 +35,14 @@ public interface DeviceBusElement extends DeviceBus {
*
* @param controller the controller to add.
*/
- void addController(final DeviceBusController controller);
+ void addController(DeviceBusController controller);
/**
* Unregisters a controller from this bus element.
*
* @param controller the controller to remove.
*/
- void removeController(final DeviceBusController controller);
+ void removeController(DeviceBusController controller);
/**
* Returns the list of devices connected specifically by this element.
@@ -56,4 +58,23 @@ public interface DeviceBusElement extends DeviceBus {
* @return the devices that have been added to this element.
*/
Collection getLocalDevices();
+
+ /**
+ * Returns an identifier unique to the specified device.
+ *
+ * This id must persist over save/load to prevent code in a running VM losing
+ * track of the device. Note that some device types (e.g. {@link RPCDevice}s)
+ * require for an ID to be provided for them to work at all.
+ *
+ * It is possible for multiple devices to have the same identifier. Typically
+ * this means they represent a view on the same underlying object. How this is
+ * handled depends on the device type and may or may not be supported.
+ *
+ * Only devices retrieved by calling {@link #getLocalDevices()} should be passed
+ * to this method.
+ *
+ * @param device the device to obtain the ID for.
+ * @return the stable id for the specified device.
+ */
+ Optional getDeviceIdentifier(Device device);
}
diff --git a/src/main/java/li/cil/oc2/api/bus/device/AbstractDeviceMethod.java b/src/main/java/li/cil/oc2/api/bus/device/AbstractDeviceMethod.java
deleted file mode 100644
index 80bdc552..00000000
--- a/src/main/java/li/cil/oc2/api/bus/device/AbstractDeviceMethod.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package li.cil.oc2.api.bus.device;
-
-/**
- * Convenience base class for {@link DeviceMethod} implementations.
- */
-public abstract class AbstractDeviceMethod implements DeviceMethod {
- protected final String name;
- protected final boolean synchronize;
- protected final Class> returnType;
- protected final DeviceMethodParameter[] parameters;
-
- protected AbstractDeviceMethod(final String name, final boolean synchronize, final Class> returnType, final DeviceMethodParameter... parameters) {
- this.name = name;
- this.synchronize = synchronize;
- this.returnType = returnType;
- this.parameters = parameters;
- }
-
- protected AbstractDeviceMethod(final String name, final Class> returnType, final DeviceMethodParameter... parameters) {
- this(name, false, returnType, parameters);
- }
-
- protected AbstractDeviceMethod(final String name, final boolean synchronize, final DeviceMethodParameter... parameters) {
- this(name, synchronize, void.class, parameters);
- }
-
- protected AbstractDeviceMethod(final String name, final DeviceMethodParameter... parameters) {
- this(name, false, void.class, parameters);
- }
-
- @Override
- public String getName() {
- return name;
- }
-
- @Override
- public boolean isSynchronized() {
- return synchronize;
- }
-
- @Override
- public Class> getReturnType() {
- return returnType;
- }
-
- @Override
- public DeviceMethodParameter[] getParameters() {
- return parameters;
- }
-}
diff --git a/src/main/java/li/cil/oc2/api/bus/device/Device.java b/src/main/java/li/cil/oc2/api/bus/device/Device.java
deleted file mode 100644
index a7214b02..00000000
--- a/src/main/java/li/cil/oc2/api/bus/device/Device.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package li.cil.oc2.api.bus.device;
-
-import li.cil.oc2.api.bus.DeviceBus;
-import li.cil.oc2.api.provider.DeviceInterfaceProvider;
-
-import java.util.UUID;
-
-/**
- * Specialization of a device interface that can be referenced by a {@link UUID} and
- * represents a device as a whole.
- *
- * Implementations will typically act as a top-level wrapper for one singular device
- * as viewed by a list of {@link DeviceInterface}s.
- *
- * A unique ID is required when adding devices to a {@link DeviceBus} such that they
- * may then be referenced.
- *
- * Note that {@link DeviceInterfaceProvider}s are not expected to return instances
- * implementing this interface, but return regular {@link DeviceInterface}s instead.
- */
-public interface Device extends DeviceInterface {
- /**
- * An id unique to this device.
- *
- * This id must persist over save/load to prevent code in a running VM losing
- * track of the device.
- */
- UUID getUniqueIdentifier();
-
- /**
- * Returns a possible underlying instance of a device.
- *
- * Frequently some {@link DeviceInterface} obtained from a {@link DeviceInterfaceProvider} will be
- * wrapped by an instance of this interface. To prevent this leading to duplicated
- * device listings this allows accessing the device proper for equality checks.
- *
- * @return the underlying device. May be this device itself.
- */
- default DeviceInterface getIdentifiedDevice() {
- return this;
- }
-}
diff --git a/src/main/java/li/cil/oc2/api/bus/device/object/Callback.java b/src/main/java/li/cil/oc2/api/bus/device/object/Callback.java
index 1e4b7fd5..f8ed367e 100644
--- a/src/main/java/li/cil/oc2/api/bus/device/object/Callback.java
+++ b/src/main/java/li/cil/oc2/api/bus/device/object/Callback.java
@@ -1,6 +1,6 @@
package li.cil.oc2.api.bus.device.object;
-import li.cil.oc2.api.bus.device.DeviceMethod;
+import li.cil.oc2.api.bus.device.rpc.RPCMethod;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
@@ -8,10 +8,10 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
- * Utility annotation to allow generating lists of {@link DeviceMethod}s using
+ * Utility annotation to allow generating lists of {@link RPCMethod}s using
* {@link Callbacks#collectMethods(Object)}.
*
- * Intended to be used in classes instances of which are used as a target of {@link ObjectDeviceInterface}.
+ * Intended to be used in classes instances of which are used as a target of {@link ObjectDevice}.
*
* Method parameters are serialized and deserialized using Gson. When using custom
* parameter types it may be necessary to register a custom type adapter for them
diff --git a/src/main/java/li/cil/oc2/api/bus/device/object/Callbacks.java b/src/main/java/li/cil/oc2/api/bus/device/object/Callbacks.java
index 22a19c05..a44b5d64 100644
--- a/src/main/java/li/cil/oc2/api/bus/device/object/Callbacks.java
+++ b/src/main/java/li/cil/oc2/api/bus/device/object/Callbacks.java
@@ -1,21 +1,24 @@
package li.cil.oc2.api.bus.device.object;
-import li.cil.oc2.api.bus.device.DeviceMethod;
+import li.cil.oc2.api.bus.device.rpc.AbstractRPCMethod;
+import li.cil.oc2.api.bus.device.rpc.RPCMethod;
+import li.cil.oc2.api.bus.device.rpc.RPCParameter;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.util.Strings;
+import javax.annotation.Nullable;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
+import java.util.*;
import java.util.stream.Collectors;
/**
- * Provides automated extraction of {@link DeviceMethod}s from instances of
+ * Provides automated extraction of {@link RPCMethod}s from instances of
* class with methods annotated with the {@link Callback} annotation.
*
- * Prefer using {@link ObjectDeviceInterface} instead of using this class directly.
+ * Prefer using {@link ObjectDevice} instead of using this class directly.
*/
public final class Callbacks {
private static final Logger LOGGER = LogManager.getLogger();
@@ -23,9 +26,9 @@ public final class Callbacks {
/**
* Collects all methods annotated with {@link Callback} in the specified object
- * and generated {@link DeviceMethod}s for each one.
+ * and generated {@link RPCMethod}s for each one.
*
- * The generated {@link DeviceMethod} will be bound to the passed object and
+ * The generated {@link RPCMethod} will be bound to the passed object and
* can be called without needing to pass the object.
*
* For example:
@@ -42,10 +45,10 @@ public final class Callbacks {
* @param methodContainer an instance of a class with annotated methods.
* @return the list of methods extracted from the specified object.
*/
- public static List collectMethods(final Object methodContainer) {
+ public static List collectMethods(final Object methodContainer) {
final List reflectedMethods = getMethods(methodContainer.getClass());
- final ArrayList methods = new ArrayList<>();
+ final ArrayList methods = new ArrayList<>();
for (final Method method : reflectedMethods) {
try {
methods.add(new ObjectDeviceMethod(methodContainer, method));
@@ -59,7 +62,7 @@ public final class Callbacks {
/**
* Returns whether any {@link Callback} annotated methods are present on the specified
- * object without creating bound {@link DeviceMethod} instances.
+ * object without creating bound {@link RPCMethod} instances.
*
* The specified {@code object} can be an instance or a {@link Class}.
*
@@ -80,4 +83,94 @@ public final class Callbacks {
.filter(m -> m.isAnnotationPresent(Callback.class))
.collect(Collectors.toList()));
}
+
+ private static final class ObjectDeviceMethod extends AbstractRPCMethod {
+ private final MethodHandle handle;
+ private final String description;
+ private final String returnValueDescription;
+
+ public ObjectDeviceMethod(final Object target, final Method method) throws IllegalAccessException {
+ super(method.getName(),
+ Objects.requireNonNull(method.getAnnotation(Callback.class), "Method without Callback annotation.").synchronize(),
+ method.getReturnType(),
+ getParameters(method));
+
+ final Callback annotation = method.getAnnotation(Callback.class);
+ this.handle = MethodHandles.lookup().unreflect(method).bindTo(target);
+ this.description = Strings.isNotBlank(annotation.description()) ? annotation.description() : null;
+ this.returnValueDescription = Strings.isNotBlank(annotation.returnValueDescription()) ? annotation.returnValueDescription() : null;
+ }
+
+ @Nullable
+ @Override
+ public Object invoke(final Object... parameters) throws Throwable {
+ return handle.invokeWithArguments(parameters);
+ }
+
+ @Override
+ public Optional getDescription() {
+ return Optional.ofNullable(description);
+ }
+
+ @Override
+ public Optional getReturnValueDescription() {
+ return Optional.ofNullable(returnValueDescription);
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ final ObjectDeviceMethod that = (ObjectDeviceMethod) o;
+ return handle.equals(that.handle);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(handle);
+ }
+
+ @Override
+ public String toString() {
+ return handle.toString();
+ }
+
+ private static RPCParameter[] getParameters(final Method method) {
+ return Arrays.stream(method.getParameters())
+ .map(ReflectionParameter::new)
+ .toArray(RPCParameter[]::new);
+ }
+
+ private static final class ReflectionParameter implements RPCParameter {
+ private final Class> type;
+ @Nullable private final String name;
+ @Nullable private final String description;
+
+ public ReflectionParameter(final java.lang.reflect.Parameter parameter) {
+ this.type = parameter.getType();
+
+ final Parameter annotation = parameter.getAnnotation(Parameter.class);
+ final boolean hasName = annotation != null && Strings.isNotBlank(annotation.value());
+ final boolean hasDescription = annotation != null && Strings.isNotBlank(annotation.description());
+
+ this.name = hasName ? annotation.value() : null;
+ this.description = hasDescription ? annotation.description() : null;
+ }
+
+ @Override
+ public Class> getType() {
+ return type;
+ }
+
+ @Override
+ public Optional getName() {
+ return Optional.ofNullable(name);
+ }
+
+ @Override
+ public Optional getDescription() {
+ return Optional.ofNullable(description);
+ }
+ }
+ }
}
diff --git a/src/main/java/li/cil/oc2/api/bus/device/object/NamedDevice.java b/src/main/java/li/cil/oc2/api/bus/device/object/NamedDevice.java
index ba19ba5c..2227db85 100644
--- a/src/main/java/li/cil/oc2/api/bus/device/object/NamedDevice.java
+++ b/src/main/java/li/cil/oc2/api/bus/device/object/NamedDevice.java
@@ -6,14 +6,14 @@ import net.minecraft.tileentity.TileEntity;
import java.util.Collection;
/**
- * This interface is used to declare additional type names for a device on targets of an {@link ObjectDeviceInterface}.
+ * This interface is used to declare additional type names for a device on targets of an {@link ObjectDevice}.
*
- * In particular {@link Block}s and {@link TileEntity}s that contain {@link Callback} methods may implement
+ * For example: {@link Block}s and {@link TileEntity}s that contain {@link Callback} methods may implement
* this interface to provide additional type names.
*/
public interface NamedDevice {
/**
- * A list of additional type names to associate with any {@link ObjectDeviceInterface} used
+ * A list of additional type names to associate with any {@link ObjectDevice} used
* to make available methods in an instance of a class implementing this interface.
*
* @return the list of additional type names.
diff --git a/src/main/java/li/cil/oc2/api/bus/device/object/ObjectDeviceInterface.java b/src/main/java/li/cil/oc2/api/bus/device/object/ObjectDevice.java
similarity index 74%
rename from src/main/java/li/cil/oc2/api/bus/device/object/ObjectDeviceInterface.java
rename to src/main/java/li/cil/oc2/api/bus/device/object/ObjectDevice.java
index a3fbd407..3e60a055 100644
--- a/src/main/java/li/cil/oc2/api/bus/device/object/ObjectDeviceInterface.java
+++ b/src/main/java/li/cil/oc2/api/bus/device/object/ObjectDevice.java
@@ -1,7 +1,7 @@
package li.cil.oc2.api.bus.device.object;
-import li.cil.oc2.api.bus.device.DeviceInterface;
-import li.cil.oc2.api.bus.device.DeviceMethod;
+import li.cil.oc2.api.bus.device.rpc.RPCDevice;
+import li.cil.oc2.api.bus.device.rpc.RPCMethod;
import javax.annotation.Nullable;
import java.util.ArrayList;
@@ -9,14 +9,14 @@ import java.util.Collections;
import java.util.List;
/**
- * A reflection based implementation of {@link DeviceInterface} using the {@link Callback}
- * annotation to discover {@link DeviceMethod}s in a target object via
+ * A reflection based implementation of {@link RPCDevice} using the {@link Callback}
+ * annotation to discover {@link RPCMethod}s in a target object via
* {@link Callbacks#collectMethods(Object)}.
*/
-public final class ObjectDeviceInterface implements DeviceInterface {
+public final class ObjectDevice implements RPCDevice {
private final Object object;
private final ArrayList typeNames;
- private final List methods;
+ private final List methods;
private final String className;
/**
@@ -26,7 +26,7 @@ public final class ObjectDeviceInterface implements DeviceInterface {
* @param object the object containing methods provided by this device.
* @param typeNames the type names of the device.
*/
- public ObjectDeviceInterface(final Object object, final List typeNames) {
+ public ObjectDevice(final Object object, final List typeNames) {
this.object = object;
this.typeNames = new ArrayList<>(typeNames);
this.methods = Callbacks.collectMethods(object);
@@ -40,12 +40,12 @@ public final class ObjectDeviceInterface implements DeviceInterface {
/**
* Creates a new object device with methods in the specified object and the specified
* type name. For convenience, the type name may be {@code null}, in which case using
- * this constructor is equivalent to using {@link #ObjectDeviceInterface(Object)}.
+ * this constructor is equivalent to using {@link #ObjectDevice(Object)}.
*
* @param object the object containing methods provided by this device.
* @param typeName the type name of the device.
*/
- public ObjectDeviceInterface(final Object object, @Nullable final String typeName) {
+ public ObjectDevice(final Object object, @Nullable final String typeName) {
this(object, typeName != null ? Collections.singletonList(typeName) : Collections.emptyList());
}
@@ -54,7 +54,7 @@ public final class ObjectDeviceInterface implements DeviceInterface {
*
* @param object the object containing the methods provided by this device.
*/
- public ObjectDeviceInterface(final Object object) {
+ public ObjectDevice(final Object object) {
this(object, Collections.emptyList());
}
@@ -64,7 +64,7 @@ public final class ObjectDeviceInterface implements DeviceInterface {
}
@Override
- public List getMethods() {
+ public List getMethods() {
return methods;
}
@@ -72,7 +72,7 @@ public final class ObjectDeviceInterface implements DeviceInterface {
public boolean equals(@Nullable final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
- final ObjectDeviceInterface that = (ObjectDeviceInterface) o;
+ final ObjectDevice that = (ObjectDevice) o;
return object.equals(that.object);
}
diff --git a/src/main/java/li/cil/oc2/api/bus/device/object/ObjectDeviceMethod.java b/src/main/java/li/cil/oc2/api/bus/device/object/ObjectDeviceMethod.java
deleted file mode 100644
index 958991a6..00000000
--- a/src/main/java/li/cil/oc2/api/bus/device/object/ObjectDeviceMethod.java
+++ /dev/null
@@ -1,108 +0,0 @@
-package li.cil.oc2.api.bus.device.object;
-
-import li.cil.oc2.api.bus.device.AbstractDeviceMethod;
-import li.cil.oc2.api.bus.device.DeviceMethodParameter;
-import org.apache.logging.log4j.util.Strings;
-
-import javax.annotation.Nullable;
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import java.lang.reflect.Method;
-import java.util.Arrays;
-import java.util.Objects;
-import java.util.Optional;
-
-/**
- * Not intended to be instantiated directly, see {@link Callbacks}.
- *
- * @see Callbacks
- */
-public final class ObjectDeviceMethod extends AbstractDeviceMethod {
- private final MethodHandle handle;
- private final String description;
- private final String returnValueDescription;
-
- public ObjectDeviceMethod(final Object target, final Method method) throws IllegalAccessException {
- super(method.getName(),
- Objects.requireNonNull(method.getAnnotation(Callback.class), "Method without Callback annotation.").synchronize(),
- method.getReturnType(),
- getParameters(method));
-
- final Callback annotation = method.getAnnotation(Callback.class);
- this.handle = MethodHandles.lookup().unreflect(method).bindTo(target);
- this.description = Strings.isNotBlank(annotation.description()) ? annotation.description() : null;
- this.returnValueDescription = Strings.isNotBlank(annotation.returnValueDescription()) ? annotation.returnValueDescription() : null;
- }
-
- @Nullable
- @Override
- public Object invoke(final Object... parameters) throws Throwable {
- return handle.invokeWithArguments(parameters);
- }
-
- @Override
- public Optional getDescription() {
- return Optional.ofNullable(description);
- }
-
- @Override
- public Optional getReturnValueDescription() {
- return Optional.ofNullable(returnValueDescription);
- }
-
- @Override
- public boolean equals(final Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- final ObjectDeviceMethod that = (ObjectDeviceMethod) o;
- return handle.equals(that.handle);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(handle);
- }
-
- @Override
- public String toString() {
- return handle.toString();
- }
-
- private static DeviceMethodParameter[] getParameters(final Method method) {
- return Arrays.stream(method.getParameters())
- .map(ReflectionParameter::new)
- .toArray(DeviceMethodParameter[]::new);
- }
-
- private static final class ReflectionParameter implements DeviceMethodParameter {
- private final Class> type;
- @Nullable private final String name;
- @Nullable private final String description;
-
- public ReflectionParameter(final java.lang.reflect.Parameter parameter) {
- this.type = parameter.getType();
-
- final li.cil.oc2.api.bus.device.object.Parameter annotation = parameter.getAnnotation(li.cil.oc2.api.bus.device.object.Parameter.class);
- final boolean hasName = annotation != null && Strings.isNotBlank(annotation.value());
- final boolean hasDescription = annotation != null && Strings.isNotBlank(annotation.description());
-
- this.name = hasName ? annotation.value() : null;
- this.description = hasDescription ? annotation.description() : null;
- }
-
- @Override
- public Class> getType() {
- return type;
- }
-
- @Override
- public Optional getName() {
- return Optional.ofNullable(name);
- }
-
- @Override
- public Optional getDescription() {
- return Optional.ofNullable(description);
- }
- }
-}
diff --git a/src/main/java/li/cil/oc2/api/bus/device/rpc/AbstractRPCMethod.java b/src/main/java/li/cil/oc2/api/bus/device/rpc/AbstractRPCMethod.java
new file mode 100644
index 00000000..365b4265
--- /dev/null
+++ b/src/main/java/li/cil/oc2/api/bus/device/rpc/AbstractRPCMethod.java
@@ -0,0 +1,50 @@
+package li.cil.oc2.api.bus.device.rpc;
+
+/**
+ * Convenience base class for {@link RPCMethod} implementations.
+ */
+public abstract class AbstractRPCMethod implements RPCMethod {
+ protected final String name;
+ protected final boolean synchronize;
+ protected final Class> returnType;
+ protected final RPCParameter[] parameters;
+
+ protected AbstractRPCMethod(final String name, final boolean synchronize, final Class> returnType, final RPCParameter... parameters) {
+ this.name = name;
+ this.synchronize = synchronize;
+ this.returnType = returnType;
+ this.parameters = parameters;
+ }
+
+ protected AbstractRPCMethod(final String name, final Class> returnType, final RPCParameter... parameters) {
+ this(name, false, returnType, parameters);
+ }
+
+ protected AbstractRPCMethod(final String name, final boolean synchronize, final RPCParameter... parameters) {
+ this(name, synchronize, void.class, parameters);
+ }
+
+ protected AbstractRPCMethod(final String name, final RPCParameter... parameters) {
+ this(name, false, void.class, parameters);
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public boolean isSynchronized() {
+ return synchronize;
+ }
+
+ @Override
+ public Class> getReturnType() {
+ return returnType;
+ }
+
+ @Override
+ public RPCParameter[] getParameters() {
+ return parameters;
+ }
+}
diff --git a/src/main/java/li/cil/oc2/api/bus/device/DeviceInterface.java b/src/main/java/li/cil/oc2/api/bus/device/rpc/RPCDevice.java
similarity index 53%
rename from src/main/java/li/cil/oc2/api/bus/device/DeviceInterface.java
rename to src/main/java/li/cil/oc2/api/bus/device/rpc/RPCDevice.java
index c217b83d..ad8a302e 100644
--- a/src/main/java/li/cil/oc2/api/bus/device/DeviceInterface.java
+++ b/src/main/java/li/cil/oc2/api/bus/device/rpc/RPCDevice.java
@@ -1,29 +1,28 @@
-package li.cil.oc2.api.bus.device;
+package li.cil.oc2.api.bus.device.rpc;
-import li.cil.oc2.api.bus.device.object.ObjectDeviceInterface;
-import li.cil.oc2.api.provider.DeviceInterfaceProvider;
+import li.cil.oc2.api.bus.Device;
+import li.cil.oc2.api.bus.device.object.ObjectDevice;
+import li.cil.oc2.api.provider.DeviceProvider;
import java.util.List;
/**
- * Implementations act as an interface for a device.
+ * Provides an interface for an RPC device, describing the methods that can be
+ * called on it and the type names it can be detected by/is compatible with.
*
- * A {@link DeviceInterface} represents a single view onto some device. One device
- * may have multiple {@code DeviceInterfaces} providing different methods for the
+ * A {@link RPCDevice} may represent a single view onto some device or be a
+ * collection of multiple aggregated {@link RPCDevice}s. One underlying device
+ * may have multiple {@link RPCDevice}s providing different methods for the
* device. This allows specifying general purpose interfaces which provide logic
* for some aspect of an underlying device which may be shared with other devices.
*
* The easiest and hence recommended way of implementing this interface is to use
- * the {@link ObjectDeviceInterface} class.
- *
- * Note that it is strongly encouraged for implementations to provide an overloaded
- * {@link #equals(Object)} and {@link #hashCode()} so that identical devices can be
- * detected.
+ * the {@link ObjectDevice} class.
*
- * @see ObjectDeviceInterface
- * @see DeviceInterfaceProvider
+ * @see ObjectDevice
+ * @see DeviceProvider
*/
-public interface DeviceInterface {
+public interface RPCDevice extends Device {
/**
* A list of type names identifying this interface.
*
@@ -39,5 +38,5 @@ public interface DeviceInterface {
/**
* The list of methods provided by this interface.
*/
- List getMethods();
+ List getMethods();
}
diff --git a/src/main/java/li/cil/oc2/api/bus/device/DeviceMethod.java b/src/main/java/li/cil/oc2/api/bus/device/rpc/RPCMethod.java
similarity index 88%
rename from src/main/java/li/cil/oc2/api/bus/device/DeviceMethod.java
rename to src/main/java/li/cil/oc2/api/bus/device/rpc/RPCMethod.java
index 6fe117b4..97747a96 100644
--- a/src/main/java/li/cil/oc2/api/bus/device/DeviceMethod.java
+++ b/src/main/java/li/cil/oc2/api/bus/device/rpc/RPCMethod.java
@@ -1,24 +1,24 @@
-package li.cil.oc2.api.bus.device;
+package li.cil.oc2.api.bus.device.rpc;
import li.cil.oc2.api.bus.DeviceBusController;
-import li.cil.oc2.api.bus.device.object.ObjectDeviceInterface;
+import li.cil.oc2.api.bus.device.object.ObjectDevice;
import javax.annotation.Nullable;
import java.util.Optional;
/**
- * Represents a single method that can be exposed by a {@link DeviceInterface}.
+ * Represents a single method that can be exposed by a {@link RPCDevice}.
*
* The easiest and hence recommended way of implementing this interface is to use
- * the {@link ObjectDeviceInterface} class.
+ * the {@link ObjectDevice} class.
*
* Method parameters are serialized and deserialized using Gson. When using custom
* parameter types it may be necessary to register a custom type adapter for them
* via {@link li.cil.oc2.api.API#IMC_ADD_DEVICE_METHOD_PARAMETER_TYPE_ADAPTER}.
*
- * @see ObjectDeviceInterface
+ * @see ObjectDevice
*/
-public interface DeviceMethod {
+public interface RPCMethod {
/**
* The name of the method.
*
@@ -41,7 +41,7 @@ public interface DeviceMethod {
/**
* The list of parameters this method accepts.
*/
- DeviceMethodParameter[] getParameters();
+ RPCParameter[] getParameters();
/**
* Called to run this method.
diff --git a/src/main/java/li/cil/oc2/api/bus/device/DeviceMethodParameter.java b/src/main/java/li/cil/oc2/api/bus/device/rpc/RPCParameter.java
similarity index 79%
rename from src/main/java/li/cil/oc2/api/bus/device/DeviceMethodParameter.java
rename to src/main/java/li/cil/oc2/api/bus/device/rpc/RPCParameter.java
index 9f8d14b0..2e8c8c6c 100644
--- a/src/main/java/li/cil/oc2/api/bus/device/DeviceMethodParameter.java
+++ b/src/main/java/li/cil/oc2/api/bus/device/rpc/RPCParameter.java
@@ -1,18 +1,18 @@
-package li.cil.oc2.api.bus.device;
+package li.cil.oc2.api.bus.device.rpc;
import li.cil.oc2.api.bus.DeviceBusController;
import java.util.Optional;
/**
- * Describes a single parameter of a {@link DeviceMethod}.
+ * Describes a single parameter of a {@link RPCMethod}.
*/
-public interface DeviceMethodParameter {
+public interface RPCParameter {
/**
* The type of this parameter.
*
* This is used by {@link DeviceBusController}s to convert parameters from a lower
- * level representation before passing it to {@link DeviceMethod#invoke(Object...)}.
+ * level representation before passing it to {@link RPCMethod#invoke(Object...)}.
* As such, the types used must be kept simple. As a rule of thumb, only primitives
* and POJOs should be used.
*
diff --git a/src/main/java/li/cil/oc2/api/bus/device/rpc/package-info.java b/src/main/java/li/cil/oc2/api/bus/device/rpc/package-info.java
new file mode 100644
index 00000000..b3bef420
--- /dev/null
+++ b/src/main/java/li/cil/oc2/api/bus/device/rpc/package-info.java
@@ -0,0 +1,7 @@
+@ParametersAreNonnullByDefault
+@MethodsReturnNonnullByDefault
+package li.cil.oc2.api.bus.device.rpc;
+
+import mcp.MethodsReturnNonnullByDefault;
+
+import javax.annotation.ParametersAreNonnullByDefault;
\ No newline at end of file
diff --git a/src/main/java/li/cil/oc2/api/provider/DeviceInterfaceProvider.java b/src/main/java/li/cil/oc2/api/provider/DeviceProvider.java
similarity index 68%
rename from src/main/java/li/cil/oc2/api/provider/DeviceInterfaceProvider.java
rename to src/main/java/li/cil/oc2/api/provider/DeviceProvider.java
index ba193e0d..69458796 100644
--- a/src/main/java/li/cil/oc2/api/provider/DeviceInterfaceProvider.java
+++ b/src/main/java/li/cil/oc2/api/provider/DeviceProvider.java
@@ -1,23 +1,24 @@
package li.cil.oc2.api.provider;
-import li.cil.oc2.api.bus.device.DeviceInterface;
-import li.cil.oc2.api.bus.device.object.ObjectDeviceInterface;
+import li.cil.oc2.api.bus.Device;
+import li.cil.oc2.api.bus.device.object.ObjectDevice;
+import li.cil.oc2.api.bus.device.rpc.RPCDevice;
import net.minecraftforge.common.util.LazyOptional;
/**
- * Allows querying for device interfaces given some context.
+ * Allows querying for devices given some context.
*
* See the specializations of {@link DeviceQuery} for possible queries.
*
- * Returning a device interface does not transfer ownership of the device in terms
+ * Returning a devices does not transfer ownership of the device in terms
* of responsibility for persistence. Callers of this method will not attempt
* to persist objects returned by this method. It is the responsibility of the provider
* to ensure persistence where required.
*
* - Implementations may handle multiple query types and return various device
- * interface types depending on the query.
+ * types depending on the query.
* -
- * Implementations should return the same device interface for the same query.
+ * Implementations should return the same device for the same query.
*
* Failing that, implementations should return instances that are equal to each
* other when compared using {@link #equals(Object)} and have equal {@link #hashCode()}s.
@@ -30,18 +31,18 @@ import net.minecraftforge.common.util.LazyOptional;
*
* Providers can be registered with the IMC message {@link li.cil.oc2.api.API#IMC_ADD_DEVICE_PROVIDER}.
*
- * @see DeviceInterface
- * @see ObjectDeviceInterface
+ * @see RPCDevice
+ * @see ObjectDevice
* @see DeviceQuery
* @see BlockDeviceQuery
*/
@FunctionalInterface
-public interface DeviceInterfaceProvider {
+public interface DeviceProvider {
/**
* Get a device for the specified query.
*
- * @param query the query describing the object to get a {@link DeviceInterface} for.
+ * @param query the query describing the object to get a {@link Device} for.
* @return a device for the specified query, if available.
*/
- LazyOptional getDeviceInterface(DeviceQuery query);
+ LazyOptional getDevice(DeviceQuery query);
}
diff --git a/src/main/java/li/cil/oc2/api/provider/DeviceQuery.java b/src/main/java/li/cil/oc2/api/provider/DeviceQuery.java
index a4c9b213..1a909cdf 100644
--- a/src/main/java/li/cil/oc2/api/provider/DeviceQuery.java
+++ b/src/main/java/li/cil/oc2/api/provider/DeviceQuery.java
@@ -1,9 +1,9 @@
package li.cil.oc2.api.provider;
/**
- * Base interface for all queries to {@link DeviceInterfaceProvider}s.
+ * Base interface for all queries to {@link DeviceProvider}s.
*
- * @see DeviceInterfaceProvider
+ * @see DeviceProvider
* @see BlockDeviceQuery
*/
public interface DeviceQuery {
diff --git a/src/main/java/li/cil/oc2/api/vm/InterruptAllocator.java b/src/main/java/li/cil/oc2/api/vm/InterruptAllocator.java
new file mode 100644
index 00000000..e7dfafb7
--- /dev/null
+++ b/src/main/java/li/cil/oc2/api/vm/InterruptAllocator.java
@@ -0,0 +1,9 @@
+package li.cil.oc2.api.vm;
+
+import java.util.OptionalInt;
+
+public interface InterruptAllocator {
+ OptionalInt claimInterrupt(int interrupt);
+
+ OptionalInt claimInterrupt();
+}
diff --git a/src/main/java/li/cil/oc2/api/vm/MemoryMappedDeviceReference.java b/src/main/java/li/cil/oc2/api/vm/MemoryMappedDeviceReference.java
new file mode 100644
index 00000000..1a523330
--- /dev/null
+++ b/src/main/java/li/cil/oc2/api/vm/MemoryMappedDeviceReference.java
@@ -0,0 +1,10 @@
+package li.cil.oc2.api.vm;
+
+import li.cil.sedna.api.device.InterruptController;
+import li.cil.sedna.api.memory.MemoryMap;
+
+public interface MemoryMappedDeviceReference {
+ boolean load(final MemoryMap memoryMap, final InterruptController interruptController);
+
+ void unload();
+}
diff --git a/src/main/java/li/cil/oc2/api/vm/VirtualMachineContext.java b/src/main/java/li/cil/oc2/api/vm/VirtualMachineContext.java
new file mode 100644
index 00000000..5b4722a6
--- /dev/null
+++ b/src/main/java/li/cil/oc2/api/vm/VirtualMachineContext.java
@@ -0,0 +1,12 @@
+package li.cil.oc2.api.vm;
+
+import li.cil.sedna.api.device.InterruptController;
+import li.cil.sedna.api.memory.MemoryMap;
+
+public interface VirtualMachineContext {
+ MemoryMap getMemoryMap();
+
+ InterruptAllocator getInterruptAllocator();
+
+ InterruptController getInterruptController();
+}
diff --git a/src/main/java/li/cil/oc2/common/IMC.java b/src/main/java/li/cil/oc2/common/IMC.java
index b51b318e..d5b052ba 100644
--- a/src/main/java/li/cil/oc2/common/IMC.java
+++ b/src/main/java/li/cil/oc2/common/IMC.java
@@ -1,7 +1,7 @@
package li.cil.oc2.common;
import li.cil.oc2.api.API;
-import li.cil.oc2.api.provider.DeviceInterfaceProvider;
+import li.cil.oc2.api.provider.DeviceProvider;
import li.cil.oc2.api.imc.DeviceMethodParameterTypeAdapter;
import li.cil.oc2.common.device.DeviceMethodParameterTypeAdapters;
import li.cil.oc2.common.device.provider.Providers;
@@ -39,7 +39,7 @@ public final class IMC {
}
private static void addDeviceProvider(final InterModComms.IMCMessage message) {
- getMessageParameter(message, DeviceInterfaceProvider.class).ifPresent(Providers::addProvider);
+ getMessageParameter(message, DeviceProvider.class).ifPresent(Providers::addProvider);
}
private static void addDeviceMethodParameterTypeAdapter(final InterModComms.IMCMessage message) {
diff --git a/src/main/java/li/cil/oc2/common/bus/AbstractDeviceBusElement.java b/src/main/java/li/cil/oc2/common/bus/AbstractDeviceBusElement.java
new file mode 100644
index 00000000..d8c86236
--- /dev/null
+++ b/src/main/java/li/cil/oc2/common/bus/AbstractDeviceBusElement.java
@@ -0,0 +1,69 @@
+package li.cil.oc2.common.bus;
+
+import li.cil.oc2.api.bus.Device;
+import li.cil.oc2.api.bus.DeviceBusController;
+import li.cil.oc2.api.bus.DeviceBusElement;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+public abstract class AbstractDeviceBusElement implements DeviceBusElement {
+ protected final List devices = new ArrayList<>();
+ protected final HashSet controllers = new HashSet<>();
+
+ public void addController(final DeviceBusController controller) {
+ controllers.add(controller);
+ }
+
+ @Override
+ public void removeController(final DeviceBusController controller) {
+ controllers.remove(controller);
+ }
+
+ @Override
+ public Collection getLocalDevices() {
+ return devices;
+ }
+
+ @Override
+ public Optional getDeviceIdentifier(final Device device) {
+ return Optional.empty();
+ }
+
+ @Override
+ public void addDevice(final Device device) {
+ devices.add(device);
+ scanDevices();
+ }
+
+ @Override
+ public void removeDevice(final Device device) {
+ devices.remove(device);
+ scanDevices();
+ }
+
+ @Override
+ public Collection getDevices() {
+ if (!controllers.isEmpty()) {
+ return controllers.stream().flatMap(controller -> getDevices().stream()).collect(Collectors.toList());
+ } else {
+ return getLocalDevices();
+ }
+ }
+
+ @Override
+ public void scheduleScan() {
+ // Controllers are expected to remove themselves when a scan is scheduled.
+ final ArrayList oldControllers = new ArrayList<>(controllers);
+ for (final DeviceBusController controller : oldControllers) {
+ controller.scheduleBusScan();
+ }
+ assert controllers.isEmpty();
+ }
+
+ protected void scanDevices() {
+ for (final DeviceBusController controller : controllers) {
+ controller.scanDevices();
+ }
+ }
+}
diff --git a/src/main/java/li/cil/oc2/common/bus/RPCAdapter.java b/src/main/java/li/cil/oc2/common/bus/RPCAdapter.java
index d81d32ca..f8aeb882 100644
--- a/src/main/java/li/cil/oc2/common/bus/RPCAdapter.java
+++ b/src/main/java/li/cil/oc2/common/bus/RPCAdapter.java
@@ -3,16 +3,17 @@ package li.cil.oc2.common.bus;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import li.cil.ceres.api.Serialized;
+import li.cil.oc2.api.bus.Device;
import li.cil.oc2.api.bus.DeviceBusController;
-import li.cil.oc2.api.bus.device.Device;
-import li.cil.oc2.api.bus.device.DeviceInterface;
-import li.cil.oc2.api.bus.device.DeviceMethod;
-import li.cil.oc2.api.bus.device.DeviceMethodParameter;
+import li.cil.oc2.api.bus.device.rpc.RPCDevice;
+import li.cil.oc2.api.bus.device.rpc.RPCMethod;
+import li.cil.oc2.api.bus.device.rpc.RPCParameter;
import li.cil.oc2.common.device.DeviceMethodParameterTypeAdapters;
-import li.cil.oc2.serialization.serializers.DeviceJsonSerializer;
-import li.cil.oc2.serialization.serializers.DeviceMethodJsonSerializer;
+import li.cil.oc2.common.device.RPCDeviceList;
import li.cil.oc2.serialization.serializers.MessageJsonDeserializer;
import li.cil.oc2.serialization.serializers.MethodInvocationJsonDeserializer;
+import li.cil.oc2.serialization.serializers.RPCDeviceWithIdentifierJsonSerializer;
+import li.cil.oc2.serialization.serializers.RPCMethodJsonSerializer;
import li.cil.sedna.api.device.Steppable;
import li.cil.sedna.api.device.serial.SerialDevice;
@@ -20,9 +21,9 @@ import javax.annotation.Nullable;
import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.UUID;
+import java.util.*;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
public final class RPCAdapter implements Steppable {
private static final int DEFAULT_MAX_MESSAGE_SIZE = 4 * 1024;
@@ -35,10 +36,14 @@ public final class RPCAdapter implements Steppable {
public static final String ERROR_INVALID_PARAMETER_SIGNATURE = "invalid parameter signature";
private final DeviceBusController controller;
-
private final SerialDevice serialDevice;
private final Gson gson;
+ private final ArrayList devices = new ArrayList<>();
+ private final HashMap devicesById = new HashMap<>();
+ private final Lock pauseLock = new ReentrantLock();
+ private boolean isPaused;
+
@Serialized private final ByteBuffer transmitBuffer; // for data written to device by VM
@Serialized private ByteBuffer receiveBuffer; // for data written by device to VM
@Serialized private MethodInvocation synchronizedInvocation; // pending main thread invocation
@@ -54,18 +59,88 @@ public final class RPCAdapter implements Steppable {
this.gson = DeviceMethodParameterTypeAdapters.beginBuildGson()
.registerTypeAdapter(MethodInvocation.class, new MethodInvocationJsonDeserializer())
.registerTypeAdapter(Message.class, new MessageJsonDeserializer())
- .registerTypeAdapter(DeviceInterface.class, new DeviceJsonSerializer())
- .registerTypeAdapter(DeviceMethod.class, new DeviceMethodJsonSerializer())
+ .registerTypeAdapter(RPCDeviceWithIdentifier.class, new RPCDeviceWithIdentifierJsonSerializer())
+ .registerTypeAdapter(RPCMethod.class, new RPCMethodJsonSerializer())
.create();
}
public void reset() {
+ devices.clear();
+ devicesById.clear();
+ isPaused = false;
transmitBuffer.clear();
receiveBuffer = null;
synchronizedInvocation = null;
}
+ public void pause() {
+ if (isPaused) {
+ return;
+ }
+
+ pauseLock.lock();
+ isPaused = true;
+ pauseLock.unlock();
+
+ devices.clear();
+ devicesById.clear();
+ }
+
+ public void resume() {
+ if (!isPaused) {
+ return;
+ }
+
+ // How device grouping works:
+ // Each device can have multiple UUIDs due to being attached to multiple bus elements.
+ // There is no guarantee that for each device D1 present on bus elements E1 and E2,
+ // where device D2 is present on E1 it will also be present on E2. This is completely
+ // up to the device providers.
+ // Therefore we must group all devices by their identifiers to then remove duplicate
+ // groups. This is fragile because it will depend on the order the devices appear in
+ // the list. However, since we add devices to bus elements in the order of their
+ // providers, then add devices to the controller in the order of their elements, this
+ // will work. And even if it does not, it only leads to duplicate devices popping up
+ // in the VM, which, while annoying, is not breaking anything.
+ // In a final step, when we know which devices are duplicates and what identifiers
+ // they have, we pick a single identifier in a deterministic way, given the list of
+ // identifiers is the same.
+
+ final HashMap> devicesByIdentifier = new HashMap<>();
+ for (final Device device : controller.getDevices()) {
+ if (device instanceof RPCDevice) {
+ final RPCDevice rpcDevice = (RPCDevice) device;
+ final Set identifiers = controller.getDeviceIdentifiers(device);
+ for (final UUID identifier : identifiers) {
+ devicesByIdentifier
+ .computeIfAbsent(identifier, unused -> new ArrayList<>())
+ .add(rpcDevice);
+ }
+ }
+ }
+
+ final HashMap> identifiersByDevice = new HashMap<>();
+ devicesByIdentifier.forEach((identifier, devices) -> {
+ final RPCDeviceList device = new RPCDeviceList(devices);
+ identifiersByDevice
+ .computeIfAbsent(device, unused -> new ArrayList<>())
+ .add(identifier);
+ });
+
+ identifiersByDevice.forEach((device, identifiers) -> {
+ final UUID identifier = selectIdentifierDeterministically(identifiers);
+ devices.add(new RPCDeviceWithIdentifier(identifier, device));
+ devicesById.put(identifier, device);
+ });
+
+ isPaused = false;
+ }
+
public void tick() {
+ if (isPaused) {
+ return;
+ }
+
if (synchronizedInvocation != null) {
final MethodInvocation methodInvocation = synchronizedInvocation;
synchronizedInvocation = null;
@@ -74,8 +149,27 @@ public final class RPCAdapter implements Steppable {
}
public void step(final int cycles) {
- readFromDevice();
- writeToDevice();
+ if (isPaused || !pauseLock.tryLock()) {
+ return;
+ }
+
+ try {
+ readFromDevice();
+ writeToDevice();
+ } finally {
+ pauseLock.unlock();
+ }
+ }
+
+ private UUID selectIdentifierDeterministically(final ArrayList identifiers) {
+ UUID lowestIdentifier = identifiers.get(0);
+ for (int i = 1; i < identifiers.size(); i++) {
+ final UUID identifier = identifiers.get(i);
+ if (identifier.compareTo(lowestIdentifier) < 0) {
+ lowestIdentifier = identifier;
+ }
+ }
+ return lowestIdentifier;
}
private void readFromDevice() {
@@ -151,8 +245,8 @@ public final class RPCAdapter implements Steppable {
}
private void processMethodInvocation(final MethodInvocation methodInvocation, final boolean isMainThread) {
- final Optional device = controller.getDevice(methodInvocation.deviceId);
- if (!device.isPresent()) {
+ final RPCDevice device = devicesById.get(methodInvocation.deviceId);
+ if (device == null) {
writeError(ERROR_UNKNOWN_DEVICE);
return;
}
@@ -163,12 +257,12 @@ public final class RPCAdapter implements Steppable {
// flexibility for free (devices may dynamically change their methods).
String error = ERROR_UNKNOWN_METHOD;
outer:
- for (final DeviceMethod method : device.get().getMethods()) {
+ for (final RPCMethod method : device.getMethods()) {
if (!Objects.equals(method.getName(), methodInvocation.methodName)) {
continue;
}
- final DeviceMethodParameter[] parametersSpec = method.getParameters();
+ final RPCParameter[] parametersSpec = method.getParameters();
if (methodInvocation.parameters.size() != parametersSpec.length) {
error = ERROR_INVALID_PARAMETER_SIGNATURE;
continue; // There may be an overload with matching parameter count.
@@ -176,7 +270,7 @@ public final class RPCAdapter implements Steppable {
final Object[] parameters = new Object[parametersSpec.length];
for (int i = 0; i < parametersSpec.length; i++) {
- final DeviceMethodParameter parameterInfo = parametersSpec[i];
+ final RPCParameter parameterInfo = parametersSpec[i];
try {
parameters[i] = gson.fromJson(methodInvocation.parameters.get(i), parameterInfo.getType());
} catch (final Throwable e) {
@@ -204,7 +298,7 @@ public final class RPCAdapter implements Steppable {
}
private void writeStatus() {
- writeMessage(Message.MESSAGE_TYPE_STATUS, controller.getDevices().toArray(new DeviceInterface[0]));
+ writeMessage(Message.MESSAGE_TYPE_STATUS, devices);
}
private void writeError(final String message) {
@@ -232,6 +326,16 @@ public final class RPCAdapter implements Steppable {
receiveBuffer.flip();
}
+ public static final class RPCDeviceWithIdentifier {
+ public final UUID identifier;
+ public final RPCDevice device;
+
+ private RPCDeviceWithIdentifier(final UUID identifier, final RPCDevice device) {
+ this.identifier = identifier;
+ this.device = device;
+ }
+ }
+
public static final class Message {
// Device -> VM
public static final String MESSAGE_TYPE_STATUS = "status";
diff --git a/src/main/java/li/cil/oc2/common/bus/TileEntityDeviceBusController.java b/src/main/java/li/cil/oc2/common/bus/TileEntityDeviceBusController.java
index dae1e531..2a3a370c 100644
--- a/src/main/java/li/cil/oc2/common/bus/TileEntityDeviceBusController.java
+++ b/src/main/java/li/cil/oc2/common/bus/TileEntityDeviceBusController.java
@@ -1,9 +1,8 @@
package li.cil.oc2.common.bus;
+import li.cil.oc2.api.bus.Device;
import li.cil.oc2.api.bus.DeviceBusController;
import li.cil.oc2.api.bus.DeviceBusElement;
-import li.cil.oc2.api.bus.device.Device;
-import li.cil.oc2.api.bus.device.DeviceInterface;
import li.cil.oc2.common.capabilities.Capabilities;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction;
@@ -12,11 +11,9 @@ import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.World;
import net.minecraftforge.common.util.LazyOptional;
-import javax.annotation.Nullable;
import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-public final class TileEntityDeviceBusController implements DeviceBusController {
+public abstract class TileEntityDeviceBusController implements DeviceBusController {
public enum State {
SCAN_PENDING,
TOO_COMPLEX,
@@ -27,27 +24,26 @@ public final class TileEntityDeviceBusController implements DeviceBusController
private static final int MAX_BUS_ELEMENT_COUNT = 128;
private final TileEntity tileEntity;
- @Nullable private final Runnable onBeforeClearDevices;
private final Set elements = new HashSet<>();
- private final ConcurrentHashMap devices = new ConcurrentHashMap<>();
+ private final HashSet devices = new HashSet<>();
+ private final HashMap> deviceIds = new HashMap<>();
private int scanDelay;
- public TileEntityDeviceBusController(final TileEntity tileEntity) {
- this(tileEntity, null);
+ protected TileEntityDeviceBusController(final TileEntity tileEntity) {
+ this.tileEntity = tileEntity;
}
- public TileEntityDeviceBusController(final TileEntity tileEntity, @Nullable final Runnable onBeforeBusScan) {
- this.tileEntity = tileEntity;
- this.onBeforeClearDevices = onBeforeBusScan;
+ protected void onDevicesInvalid() {
+ }
+
+ protected void onDevicesValid() {
}
@Override
public void scheduleBusScan() {
- if (!devices.isEmpty() && onBeforeClearDevices != null) {
- onBeforeClearDevices.run();
- }
+ onDevicesInvalid();
for (final DeviceBusElement element : elements) {
element.removeController(this);
@@ -55,36 +51,37 @@ public final class TileEntityDeviceBusController implements DeviceBusController
elements.clear();
devices.clear();
+ deviceIds.clear();
scanDelay = 0; // scan as soon as possible
}
@Override
public void scanDevices() {
- devices.clear();
+ onDevicesInvalid();
- final HashMap> groupedDevices = new HashMap<>();
+ devices.clear();
+ deviceIds.clear();
for (final DeviceBusElement element : elements) {
for (final Device device : element.getLocalDevices()) {
- groupedDevices.computeIfAbsent(device.getIdentifiedDevice(), d -> new ArrayList<>()).add(device);
+ devices.add(device);
+ element.getDeviceIdentifier(device).ifPresent(identifier -> deviceIds
+ .computeIfAbsent(device, unused -> new HashSet<>()).add(identifier));
}
}
- for (final ArrayList group : groupedDevices.values()) {
- final Device device = selectDeviceDeterministically(group);
- devices.putIfAbsent(device.getUniqueIdentifier(), device);
- }
+ onDevicesValid();
}
@Override
- public Collection getDevices() {
- return devices.values();
+ public Set getDevices() {
+ return devices;
}
@Override
- public Optional getDevice(final UUID uuid) {
- return Optional.ofNullable(devices.get(uuid));
+ public Set getDeviceIdentifiers(final Device device) {
+ return deviceIds.getOrDefault(device, Collections.emptySet());
}
public State scan() {
@@ -189,18 +186,6 @@ public final class TileEntityDeviceBusController implements DeviceBusController
return State.READY;
}
- private static Device selectDeviceDeterministically(final ArrayList devices) {
- Device deviceWithLowestUuid = devices.get(0);
- for (int i = 1; i < devices.size(); i++) {
- final Device device = devices.get(i);
- if (device.getUniqueIdentifier().compareTo(deviceWithLowestUuid.getUniqueIdentifier()) < 0) {
- deviceWithLowestUuid = device;
- }
- }
-
- return deviceWithLowestUuid;
- }
-
private static final class ScanEdge {
public final BlockPos position;
public final Direction face;
diff --git a/src/main/java/li/cil/oc2/common/bus/TileEntityDeviceBusElement.java b/src/main/java/li/cil/oc2/common/bus/TileEntityDeviceBusElement.java
index 5496814a..6b29686a 100644
--- a/src/main/java/li/cil/oc2/common/bus/TileEntityDeviceBusElement.java
+++ b/src/main/java/li/cil/oc2/common/bus/TileEntityDeviceBusElement.java
@@ -1,47 +1,46 @@
package li.cil.oc2.common.bus;
import li.cil.ceres.api.Serialized;
+import li.cil.oc2.api.bus.Device;
import li.cil.oc2.api.bus.DeviceBus;
import li.cil.oc2.api.bus.DeviceBusElement;
import li.cil.oc2.common.ServerScheduler;
import li.cil.oc2.common.capabilities.Capabilities;
-import li.cil.oc2.common.device.DeviceImpl;
-import li.cil.oc2.common.device.DeviceInterfaceCollection;
import li.cil.oc2.common.device.provider.Providers;
import li.cil.oc2.common.util.WorldUtils;
-import net.minecraft.nbt.CompoundNBT;
-import net.minecraft.nbt.ListNBT;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.common.util.LazyOptional;
-import java.util.Objects;
-import java.util.UUID;
-
-public final class TileEntityDeviceBusElement {
- private static final String DEVICE_IDS_NBT_TAG_NAME = "deviceIds";
- private static final String DEVICE_ID_NBT_TAG_NAME = "deviceId";
+import java.util.*;
+public final class TileEntityDeviceBusElement extends AbstractDeviceBusElement {
private static final int NEIGHBOR_COUNT = 6;
private final TileEntity tileEntity;
- private final DeviceBusElement busElement = Objects.requireNonNull(Capabilities.DEVICE_BUS_ELEMENT_CAPABILITY.getDefaultInstance());
- private final DeviceImpl[] devices = new DeviceImpl[NEIGHBOR_COUNT];
- @Serialized private UUID[] deviceIds = new UUID[NEIGHBOR_COUNT];
+ private final ArrayList> sidedDevices = new ArrayList<>(6);
+ @Serialized private UUID[] sidedDeviceIds = new UUID[NEIGHBOR_COUNT];
public TileEntityDeviceBusElement(final TileEntity tileEntity) {
this.tileEntity = tileEntity;
for (int i = 0; i < NEIGHBOR_COUNT; i++) {
- deviceIds[i] = UUID.randomUUID();
+ sidedDevices.add(new HashSet<>());
+ sidedDeviceIds[i] = UUID.randomUUID();
}
}
- public DeviceBusElement getBusElement() {
- return busElement;
+ @Override
+ public Optional getDeviceIdentifier(final Device device) {
+ for (int i = 0; i < NEIGHBOR_COUNT; i++) {
+ if (sidedDevices.get(i).contains(device)) {
+ return Optional.of(sidedDeviceIds[i]);
+ }
+ }
+ return super.getDeviceIdentifier(device);
}
public void handleNeighborChanged(final BlockPos pos) {
@@ -58,30 +57,22 @@ public final class TileEntityDeviceBusElement {
final int index = direction.getIndex();
- final LazyOptional device = Providers.getDevice(world, pos, direction);
- final DeviceImpl identifiableDevice;
-
- if (device.isPresent()) {
- final String typeName = WorldUtils.getBlockName(world, pos);
- identifiableDevice = new DeviceImpl(device, deviceIds[index], typeName);
- device.addListener((ignored) -> handleNeighborChanged(pos));
- } else {
- identifiableDevice = null;
+ final HashSet newDevices = new HashSet<>();
+ for (final LazyOptional device : Providers.getDevices(world, pos, direction)) {
+ device.ifPresent(newDevices::add);
+ device.addListener(unused -> handleNeighborChanged(pos));
}
- if (Objects.equals(devices[index], identifiableDevice)) {
+ final HashSet devicesOnSide = sidedDevices.get(index);
+ if (Objects.equals(newDevices, devicesOnSide)) {
return;
}
- if (devices[index] != null) {
- busElement.removeDevice(devices[index]);
- }
-
- devices[index] = identifiableDevice;
-
- if (devices[index] != null) {
- busElement.addDevice(devices[index]);
- }
+ devices.removeAll(devicesOnSide);
+ devicesOnSide.clear();
+ devicesOnSide.addAll(newDevices);
+ devices.addAll(devicesOnSide);
+ scanDevices();
}
public void initialize() {
@@ -97,19 +88,7 @@ public final class TileEntityDeviceBusElement {
}
public void dispose() {
- busElement.scheduleScan();
- }
-
- public CompoundNBT write(final CompoundNBT compound) {
- final ListNBT deviceIdsNbt = new ListNBT();
- for (int i = 0; i < NEIGHBOR_COUNT; i++) {
- final CompoundNBT deviceIdNbt = new CompoundNBT();
- deviceIdNbt.putUniqueId(DEVICE_ID_NBT_TAG_NAME, deviceIds[i]);
- deviceIdsNbt.add(deviceIdNbt);
- }
- compound.put(DEVICE_IDS_NBT_TAG_NAME, deviceIdsNbt);
-
- return compound;
+ scheduleScan();
}
private void scanNeighborsForDevices() {
diff --git a/src/main/java/li/cil/oc2/common/capabilities/DeviceBusControllerCapability.java b/src/main/java/li/cil/oc2/common/capabilities/DeviceBusControllerCapability.java
index 7b451217..30b8ff14 100644
--- a/src/main/java/li/cil/oc2/common/capabilities/DeviceBusControllerCapability.java
+++ b/src/main/java/li/cil/oc2/common/capabilities/DeviceBusControllerCapability.java
@@ -1,12 +1,11 @@
package li.cil.oc2.common.capabilities;
+import li.cil.oc2.api.bus.Device;
import li.cil.oc2.api.bus.DeviceBusController;
-import li.cil.oc2.api.bus.device.Device;
import net.minecraftforge.common.capabilities.CapabilityManager;
-import java.util.Collection;
import java.util.Collections;
-import java.util.Optional;
+import java.util.Set;
import java.util.UUID;
public final class DeviceBusControllerCapability {
@@ -24,13 +23,13 @@ public final class DeviceBusControllerCapability {
}
@Override
- public Collection getDevices() {
- return Collections.emptyList();
+ public Set getDevices() {
+ return Collections.emptySet();
}
@Override
- public Optional getDevice(final UUID uuid) {
- return Optional.empty();
+ public Set getDeviceIdentifiers(final Device device) {
+ return Collections.emptySet();
}
}
}
diff --git a/src/main/java/li/cil/oc2/common/capabilities/DeviceBusElementCapability.java b/src/main/java/li/cil/oc2/common/capabilities/DeviceBusElementCapability.java
index 74c96c9c..a9ce30e4 100644
--- a/src/main/java/li/cil/oc2/common/capabilities/DeviceBusElementCapability.java
+++ b/src/main/java/li/cil/oc2/common/capabilities/DeviceBusElementCapability.java
@@ -2,13 +2,10 @@ package li.cil.oc2.common.capabilities;
import li.cil.oc2.api.bus.DeviceBusController;
import li.cil.oc2.api.bus.DeviceBusElement;
-import li.cil.oc2.api.bus.device.Device;
+import li.cil.oc2.api.bus.Device;
import net.minecraftforge.common.capabilities.CapabilityManager;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
+import java.util.*;
import java.util.stream.Collectors;
public final class DeviceBusElementCapability {
@@ -34,6 +31,11 @@ public final class DeviceBusElementCapability {
return devices;
}
+ @Override
+ public Optional getDeviceIdentifier(final Device device) {
+ return Optional.empty();
+ }
+
@Override
public void addDevice(final Device device) {
devices.add(device);
diff --git a/src/main/java/li/cil/oc2/common/device/DeviceImpl.java b/src/main/java/li/cil/oc2/common/device/DeviceImpl.java
deleted file mode 100644
index 161b0b13..00000000
--- a/src/main/java/li/cil/oc2/common/device/DeviceImpl.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package li.cil.oc2.common.device;
-
-import li.cil.oc2.api.bus.device.Device;
-import li.cil.oc2.api.bus.device.DeviceInterface;
-import li.cil.oc2.api.bus.device.DeviceMethod;
-import li.cil.oc2.common.util.LazyOptionalUtils;
-import net.minecraftforge.common.util.LazyOptional;
-
-import javax.annotation.Nullable;
-import java.util.*;
-
-public final class DeviceImpl implements Device {
- private final LazyOptional deviceInterface;
- private final UUID uuid;
- @Nullable private final String typeName;
-
- public DeviceImpl(final LazyOptional extends DeviceInterface> deviceInterface, final UUID uuid) {
- this(deviceInterface, uuid, null);
- }
-
- public DeviceImpl(final LazyOptional extends DeviceInterface> deviceInterface, final UUID uuid, @Nullable final String typeName) {
- this.deviceInterface = deviceInterface.cast();
- this.uuid = uuid;
- this.typeName = typeName;
- }
-
- @Override
- public UUID getUniqueIdentifier() {
- return uuid;
- }
-
- @Override
- public DeviceInterface getIdentifiedDevice() {
- return deviceInterface.orElse(this);
- }
-
- @Override
- public List getTypeNames() {
- if (typeName != null) {
- final List typeNames = new ArrayList<>(deviceInterface.map(DeviceInterface::getTypeNames).orElse(Collections.emptyList()));
- typeNames.add(typeName);
- return typeNames;
- } else {
- return deviceInterface.map(DeviceInterface::getTypeNames).orElse(Collections.emptyList());
- }
- }
-
- @Override
- public List getMethods() {
- return deviceInterface.map(DeviceInterface::getMethods).orElse(Collections.emptyList());
- }
-
- @Override
- public boolean equals(final Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- final DeviceImpl that = (DeviceImpl) o;
- return uuid.equals(that.uuid) &&
- LazyOptionalUtils.equals(deviceInterface, that.deviceInterface) &&
- Objects.equals(typeName, that.typeName);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(uuid, LazyOptionalUtils.hashCode(deviceInterface), typeName);
- }
-}
diff --git a/src/main/java/li/cil/oc2/common/device/DeviceInterfaceCollection.java b/src/main/java/li/cil/oc2/common/device/RPCDeviceList.java
similarity index 62%
rename from src/main/java/li/cil/oc2/common/device/DeviceInterfaceCollection.java
rename to src/main/java/li/cil/oc2/common/device/RPCDeviceList.java
index 6be2ef41..0ecde92e 100644
--- a/src/main/java/li/cil/oc2/common/device/DeviceInterfaceCollection.java
+++ b/src/main/java/li/cil/oc2/common/device/RPCDeviceList.java
@@ -1,7 +1,7 @@
package li.cil.oc2.common.device;
-import li.cil.oc2.api.bus.device.DeviceInterface;
-import li.cil.oc2.api.bus.device.DeviceMethod;
+import li.cil.oc2.api.bus.device.rpc.RPCDevice;
+import li.cil.oc2.api.bus.device.rpc.RPCMethod;
import java.util.ArrayList;
import java.util.Collection;
@@ -9,25 +9,25 @@ import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
-public final class DeviceInterfaceCollection implements DeviceInterface {
- private final ArrayList deviceInterfaces;
+public final class RPCDeviceList implements RPCDevice {
+ private final ArrayList deviceInterfaces;
- public DeviceInterfaceCollection(final ArrayList deviceInterfaces) {
+ public RPCDeviceList(final ArrayList deviceInterfaces) {
this.deviceInterfaces = deviceInterfaces;
}
@Override
public List getTypeNames() {
return deviceInterfaces.stream()
- .map(DeviceInterface::getTypeNames)
+ .map(RPCDevice::getTypeNames)
.flatMap(Collection::stream)
.collect(Collectors.toList());
}
@Override
- public List getMethods() {
+ public List getMethods() {
return deviceInterfaces.stream()
- .map(DeviceInterface::getMethods)
+ .map(RPCDevice::getMethods)
.flatMap(Collection::stream)
.collect(Collectors.toList());
}
@@ -36,7 +36,7 @@ public final class DeviceInterfaceCollection implements DeviceInterface {
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
- final DeviceInterfaceCollection that = (DeviceInterfaceCollection) o;
+ final RPCDeviceList that = (RPCDeviceList) o;
return deviceInterfaces.equals(that.deviceInterfaces);
}
diff --git a/src/main/java/li/cil/oc2/common/device/provider/AbstractCapabilityAnyTileEntityDeviceInterfaceProvider.java b/src/main/java/li/cil/oc2/common/device/provider/AbstractCapabilityAnyTileEntityDeviceInterfaceProvider.java
deleted file mode 100644
index 82f5721f..00000000
--- a/src/main/java/li/cil/oc2/common/device/provider/AbstractCapabilityAnyTileEntityDeviceInterfaceProvider.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package li.cil.oc2.common.device.provider;
-
-import net.minecraft.tileentity.TileEntity;
-import net.minecraftforge.common.capabilities.Capability;
-
-import java.util.function.Supplier;
-
-public abstract class AbstractCapabilityAnyTileEntityDeviceInterfaceProvider extends AbstractCapabilityTileEntityDeviceInterfaceProvider {
- public AbstractCapabilityAnyTileEntityDeviceInterfaceProvider(final Supplier> capabilitySupplier) {
- super(TileEntity.class, capabilitySupplier);
- }
-}
diff --git a/src/main/java/li/cil/oc2/common/device/provider/AbstractCapabilityAnyTileEntityDeviceProvider.java b/src/main/java/li/cil/oc2/common/device/provider/AbstractCapabilityAnyTileEntityDeviceProvider.java
new file mode 100644
index 00000000..f8ca6daa
--- /dev/null
+++ b/src/main/java/li/cil/oc2/common/device/provider/AbstractCapabilityAnyTileEntityDeviceProvider.java
@@ -0,0 +1,12 @@
+package li.cil.oc2.common.device.provider;
+
+import net.minecraft.tileentity.TileEntity;
+import net.minecraftforge.common.capabilities.Capability;
+
+import java.util.function.Supplier;
+
+public abstract class AbstractCapabilityAnyTileEntityDeviceProvider extends AbstractCapabilityTileEntityDeviceProvider {
+ public AbstractCapabilityAnyTileEntityDeviceProvider(final Supplier> capabilitySupplier) {
+ super(TileEntity.class, capabilitySupplier);
+ }
+}
diff --git a/src/main/java/li/cil/oc2/common/device/provider/AbstractCapabilityTileEntityDeviceInterfaceProvider.java b/src/main/java/li/cil/oc2/common/device/provider/AbstractCapabilityTileEntityDeviceProvider.java
similarity index 56%
rename from src/main/java/li/cil/oc2/common/device/provider/AbstractCapabilityTileEntityDeviceInterfaceProvider.java
rename to src/main/java/li/cil/oc2/common/device/provider/AbstractCapabilityTileEntityDeviceProvider.java
index 9a2c4052..84355227 100644
--- a/src/main/java/li/cil/oc2/common/device/provider/AbstractCapabilityTileEntityDeviceInterfaceProvider.java
+++ b/src/main/java/li/cil/oc2/common/device/provider/AbstractCapabilityTileEntityDeviceProvider.java
@@ -1,6 +1,6 @@
package li.cil.oc2.common.device.provider;
-import li.cil.oc2.api.bus.device.DeviceInterface;
+import li.cil.oc2.api.bus.Device;
import li.cil.oc2.api.provider.BlockDeviceQuery;
import net.minecraft.tileentity.TileEntity;
import net.minecraftforge.common.capabilities.Capability;
@@ -8,16 +8,16 @@ import net.minecraftforge.common.util.LazyOptional;
import java.util.function.Supplier;
-public abstract class AbstractCapabilityTileEntityDeviceInterfaceProvider extends AbstractTileEntityDeviceInterfaceProvider {
+public abstract class AbstractCapabilityTileEntityDeviceProvider extends AbstractTileEntityDeviceProvider {
private final Supplier> capabilitySupplier;
- protected AbstractCapabilityTileEntityDeviceInterfaceProvider(final Class tileEntityType, final Supplier> capabilitySupplier) {
+ protected AbstractCapabilityTileEntityDeviceProvider(final Class tileEntityType, final Supplier> capabilitySupplier) {
super(tileEntityType);
this.capabilitySupplier = capabilitySupplier;
}
@Override
- protected final LazyOptional getDeviceInterface(final BlockDeviceQuery blockQuery, final TileEntity tileEntity) {
+ protected final LazyOptional getDeviceInterface(final BlockDeviceQuery blockQuery, final TileEntity tileEntity) {
final Capability capability = capabilitySupplier.get();
if (capability == null) throw new IllegalStateException();
final LazyOptional optional = tileEntity.getCapability(capability, blockQuery.getQuerySide());
@@ -26,10 +26,10 @@ public abstract class AbstractCapabilityTileEntityDeviceInterfaceProvider device = getDeviceInterface(blockQuery, value);
+ final LazyOptional device = getDeviceInterface(blockQuery, value);
optional.addListener(ignored -> device.invalidate());
return device;
}
- protected abstract LazyOptional getDeviceInterface(final BlockDeviceQuery query, final TCapability value);
+ protected abstract LazyOptional getDeviceInterface(final BlockDeviceQuery query, final TCapability value);
}
diff --git a/src/main/java/li/cil/oc2/common/device/provider/AbstractTileEntityDeviceInterfaceProvider.java b/src/main/java/li/cil/oc2/common/device/provider/AbstractTileEntityDeviceProvider.java
similarity index 53%
rename from src/main/java/li/cil/oc2/common/device/provider/AbstractTileEntityDeviceInterfaceProvider.java
rename to src/main/java/li/cil/oc2/common/device/provider/AbstractTileEntityDeviceProvider.java
index 93820b1a..267a9c9b 100644
--- a/src/main/java/li/cil/oc2/common/device/provider/AbstractTileEntityDeviceInterfaceProvider.java
+++ b/src/main/java/li/cil/oc2/common/device/provider/AbstractTileEntityDeviceProvider.java
@@ -1,28 +1,29 @@
package li.cil.oc2.common.device.provider;
-import li.cil.oc2.api.bus.device.DeviceInterface;
+import li.cil.oc2.api.bus.Device;
import li.cil.oc2.api.provider.BlockDeviceQuery;
-import li.cil.oc2.api.provider.DeviceInterfaceProvider;
+import li.cil.oc2.api.provider.DeviceProvider;
import li.cil.oc2.api.provider.DeviceQuery;
+import li.cil.oc2.common.util.WorldUtils;
import net.minecraft.tileentity.TileEntity;
import net.minecraftforge.common.util.LazyOptional;
-public abstract class AbstractTileEntityDeviceInterfaceProvider implements DeviceInterfaceProvider {
+public abstract class AbstractTileEntityDeviceProvider implements DeviceProvider {
private final Class tileEntityType;
- protected AbstractTileEntityDeviceInterfaceProvider(final Class tileEntityType) {
+ protected AbstractTileEntityDeviceProvider(final Class tileEntityType) {
this.tileEntityType = tileEntityType;
}
@SuppressWarnings("unchecked")
@Override
- public LazyOptional getDeviceInterface(final DeviceQuery query) {
+ public LazyOptional getDevice(final DeviceQuery query) {
if (!(query instanceof BlockDeviceQuery)) {
return LazyOptional.empty();
}
final BlockDeviceQuery blockQuery = (BlockDeviceQuery) query;
- final TileEntity tileEntity = blockQuery.getWorld().getTileEntity(blockQuery.getQueryPosition());
+ final TileEntity tileEntity = WorldUtils.getTileEntityIfChunkExists(blockQuery.getWorld(), blockQuery.getQueryPosition());
if (!tileEntityType.isInstance(tileEntity)) {
return LazyOptional.empty();
}
@@ -30,5 +31,5 @@ public abstract class AbstractTileEntityDeviceInterfaceProvider getDeviceInterface(final BlockDeviceQuery query, final T tileEntity);
+ protected abstract LazyOptional getDeviceInterface(final BlockDeviceQuery query, final T tileEntity);
}
diff --git a/src/main/java/li/cil/oc2/common/device/provider/BlockDeviceInterfaceProvider.java b/src/main/java/li/cil/oc2/common/device/provider/BlockDeviceProvider.java
similarity index 71%
rename from src/main/java/li/cil/oc2/common/device/provider/BlockDeviceInterfaceProvider.java
rename to src/main/java/li/cil/oc2/common/device/provider/BlockDeviceProvider.java
index 1db1aea5..72df3172 100644
--- a/src/main/java/li/cil/oc2/common/device/provider/BlockDeviceInterfaceProvider.java
+++ b/src/main/java/li/cil/oc2/common/device/provider/BlockDeviceProvider.java
@@ -1,19 +1,19 @@
package li.cil.oc2.common.device.provider;
-import li.cil.oc2.api.bus.device.DeviceInterface;
+import li.cil.oc2.api.bus.Device;
import li.cil.oc2.api.bus.device.object.Callbacks;
-import li.cil.oc2.api.bus.device.object.ObjectDeviceInterface;
+import li.cil.oc2.api.bus.device.object.ObjectDevice;
import li.cil.oc2.api.provider.BlockDeviceQuery;
-import li.cil.oc2.api.provider.DeviceInterfaceProvider;
+import li.cil.oc2.api.provider.DeviceProvider;
import li.cil.oc2.api.provider.DeviceQuery;
import li.cil.oc2.common.util.WorldUtils;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraftforge.common.util.LazyOptional;
-public class BlockDeviceInterfaceProvider implements DeviceInterfaceProvider {
+public class BlockDeviceProvider implements DeviceProvider {
@Override
- public LazyOptional getDeviceInterface(final DeviceQuery query) {
+ public LazyOptional getDevice(final DeviceQuery query) {
if (!(query instanceof BlockDeviceQuery)) {
return LazyOptional.empty();
}
@@ -30,6 +30,6 @@ public class BlockDeviceInterfaceProvider implements DeviceInterfaceProvider {
}
final String typeName = WorldUtils.getBlockName(blockQuery.getWorld(), blockQuery.getQueryPosition());
- return LazyOptional.of(() -> new ObjectDeviceInterface(block, typeName));
+ return LazyOptional.of(() -> new ObjectDevice(block, typeName));
}
}
diff --git a/src/main/java/li/cil/oc2/common/device/provider/EnergyStorageDeviceInterfaceProvider.java b/src/main/java/li/cil/oc2/common/device/provider/EnergyStorageDeviceProvider.java
similarity index 67%
rename from src/main/java/li/cil/oc2/common/device/provider/EnergyStorageDeviceInterfaceProvider.java
rename to src/main/java/li/cil/oc2/common/device/provider/EnergyStorageDeviceProvider.java
index 0b6e8580..17768c59 100644
--- a/src/main/java/li/cil/oc2/common/device/provider/EnergyStorageDeviceInterfaceProvider.java
+++ b/src/main/java/li/cil/oc2/common/device/provider/EnergyStorageDeviceProvider.java
@@ -1,23 +1,23 @@
package li.cil.oc2.common.device.provider;
-import li.cil.oc2.api.bus.device.DeviceInterface;
+import li.cil.oc2.api.bus.Device;
import li.cil.oc2.api.bus.device.object.Callback;
-import li.cil.oc2.api.bus.device.object.ObjectDeviceInterface;
+import li.cil.oc2.api.bus.device.object.ObjectDevice;
import li.cil.oc2.api.provider.BlockDeviceQuery;
import li.cil.oc2.common.capabilities.Capabilities;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.energy.IEnergyStorage;
-public class EnergyStorageDeviceInterfaceProvider extends AbstractCapabilityAnyTileEntityDeviceInterfaceProvider {
+public class EnergyStorageDeviceProvider extends AbstractCapabilityAnyTileEntityDeviceProvider {
private static final String ENERGY_STORAGE_TYPE_NAME = "energyStorage";
- public EnergyStorageDeviceInterfaceProvider() {
+ public EnergyStorageDeviceProvider() {
super(() -> Capabilities.ENERGY_STORAGE_CAPABILITY);
}
@Override
- protected LazyOptional getDeviceInterface(final BlockDeviceQuery query, final IEnergyStorage value) {
- return LazyOptional.of(() -> new ObjectDeviceInterface(new EnergyStorageDevice(value), ENERGY_STORAGE_TYPE_NAME));
+ protected LazyOptional getDeviceInterface(final BlockDeviceQuery query, final IEnergyStorage value) {
+ return LazyOptional.of(() -> new ObjectDevice(new EnergyStorageDevice(value), ENERGY_STORAGE_TYPE_NAME));
}
public static final class EnergyStorageDevice extends AbstractObjectProxy {
diff --git a/src/main/java/li/cil/oc2/common/device/provider/FluidHandlerDeviceInterfaceProvider.java b/src/main/java/li/cil/oc2/common/device/provider/FluidHandlerDeviceProvider.java
similarity index 67%
rename from src/main/java/li/cil/oc2/common/device/provider/FluidHandlerDeviceInterfaceProvider.java
rename to src/main/java/li/cil/oc2/common/device/provider/FluidHandlerDeviceProvider.java
index 146dd37d..e1213eeb 100644
--- a/src/main/java/li/cil/oc2/common/device/provider/FluidHandlerDeviceInterfaceProvider.java
+++ b/src/main/java/li/cil/oc2/common/device/provider/FluidHandlerDeviceProvider.java
@@ -1,24 +1,24 @@
package li.cil.oc2.common.device.provider;
-import li.cil.oc2.api.bus.device.DeviceInterface;
+import li.cil.oc2.api.bus.Device;
import li.cil.oc2.api.bus.device.object.Callback;
-import li.cil.oc2.api.bus.device.object.ObjectDeviceInterface;
+import li.cil.oc2.api.bus.device.object.ObjectDevice;
import li.cil.oc2.api.provider.BlockDeviceQuery;
import li.cil.oc2.common.capabilities.Capabilities;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.IFluidHandler;
-public class FluidHandlerDeviceInterfaceProvider extends AbstractCapabilityAnyTileEntityDeviceInterfaceProvider {
+public class FluidHandlerDeviceProvider extends AbstractCapabilityAnyTileEntityDeviceProvider {
private static final String FLUID_HANDLER_TYPE_NAME = "fluidHandler";
- public FluidHandlerDeviceInterfaceProvider() {
+ public FluidHandlerDeviceProvider() {
super(() -> Capabilities.FLUID_HANDLER_CAPABILITY);
}
@Override
- protected LazyOptional getDeviceInterface(final BlockDeviceQuery query, final IFluidHandler value) {
- return LazyOptional.of(() -> new ObjectDeviceInterface(new FluidHandlerDevice(value), FLUID_HANDLER_TYPE_NAME));
+ protected LazyOptional getDeviceInterface(final BlockDeviceQuery query, final IFluidHandler value) {
+ return LazyOptional.of(() -> new ObjectDevice(new FluidHandlerDevice(value), FLUID_HANDLER_TYPE_NAME));
}
public static final class FluidHandlerDevice extends AbstractObjectProxy {
diff --git a/src/main/java/li/cil/oc2/common/device/provider/ItemHandlerDeviceInterfaceProvider.java b/src/main/java/li/cil/oc2/common/device/provider/ItemHandlerDeviceProvider.java
similarity index 66%
rename from src/main/java/li/cil/oc2/common/device/provider/ItemHandlerDeviceInterfaceProvider.java
rename to src/main/java/li/cil/oc2/common/device/provider/ItemHandlerDeviceProvider.java
index 39094f0c..a6ec695a 100644
--- a/src/main/java/li/cil/oc2/common/device/provider/ItemHandlerDeviceInterfaceProvider.java
+++ b/src/main/java/li/cil/oc2/common/device/provider/ItemHandlerDeviceProvider.java
@@ -1,24 +1,24 @@
package li.cil.oc2.common.device.provider;
-import li.cil.oc2.api.bus.device.DeviceInterface;
+import li.cil.oc2.api.bus.Device;
import li.cil.oc2.api.bus.device.object.Callback;
-import li.cil.oc2.api.bus.device.object.ObjectDeviceInterface;
+import li.cil.oc2.api.bus.device.object.ObjectDevice;
import li.cil.oc2.api.provider.BlockDeviceQuery;
import li.cil.oc2.common.capabilities.Capabilities;
import net.minecraft.item.ItemStack;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.items.IItemHandler;
-public class ItemHandlerDeviceInterfaceProvider extends AbstractCapabilityAnyTileEntityDeviceInterfaceProvider {
+public class ItemHandlerDeviceProvider extends AbstractCapabilityAnyTileEntityDeviceProvider {
private static final String ITEM_HANDLER_TYPE_NAME = "itemHandler";
- public ItemHandlerDeviceInterfaceProvider() {
+ public ItemHandlerDeviceProvider() {
super(() -> Capabilities.ITEM_HANDLER_CAPABILITY);
}
@Override
- protected LazyOptional getDeviceInterface(final BlockDeviceQuery query, final IItemHandler value) {
- return LazyOptional.of(() -> new ObjectDeviceInterface(new ItemHandlerDevice(value), ITEM_HANDLER_TYPE_NAME));
+ protected LazyOptional getDeviceInterface(final BlockDeviceQuery query, final IItemHandler value) {
+ return LazyOptional.of(() -> new ObjectDevice(new ItemHandlerDevice(value), ITEM_HANDLER_TYPE_NAME));
}
public static final class ItemHandlerDevice extends AbstractObjectProxy {
diff --git a/src/main/java/li/cil/oc2/common/device/provider/Providers.java b/src/main/java/li/cil/oc2/common/device/provider/Providers.java
index 90119186..04430789 100644
--- a/src/main/java/li/cil/oc2/common/device/provider/Providers.java
+++ b/src/main/java/li/cil/oc2/common/device/provider/Providers.java
@@ -1,10 +1,9 @@
package li.cil.oc2.common.device.provider;
-import li.cil.oc2.api.bus.device.DeviceInterface;
-import li.cil.oc2.api.provider.DeviceInterfaceProvider;
+import li.cil.oc2.api.bus.Device;
+import li.cil.oc2.api.provider.DeviceProvider;
import li.cil.oc2.api.provider.DeviceQuery;
import li.cil.oc2.common.device.BlockDeviceQueryImpl;
-import li.cil.oc2.common.device.DeviceInterfaceCollection;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
@@ -12,56 +11,46 @@ import net.minecraft.world.World;
import net.minecraftforge.common.util.LazyOptional;
import java.util.ArrayList;
+import java.util.List;
public final class Providers {
- private static final ArrayList DEVICE_PROVIDERS = new ArrayList<>();
+ private static final ArrayList DEVICE_PROVIDERS = new ArrayList<>();
public static void initialize() {
- addProvider(new EnergyStorageDeviceInterfaceProvider());
- addProvider(new FluidHandlerDeviceInterfaceProvider());
- addProvider(new ItemHandlerDeviceInterfaceProvider());
- addProvider(new TileEntityDeviceInterfaceProvider());
- addProvider(new BlockDeviceInterfaceProvider());
+ addProvider(new EnergyStorageDeviceProvider());
+ addProvider(new FluidHandlerDeviceProvider());
+ addProvider(new ItemHandlerDeviceProvider());
+ addProvider(new TileEntityDeviceProvider());
+ addProvider(new BlockDeviceProvider());
}
- public static void addProvider(final DeviceInterfaceProvider provider) {
+ public static void addProvider(final DeviceProvider provider) {
if (!DEVICE_PROVIDERS.contains(provider)) {
DEVICE_PROVIDERS.add(provider);
}
}
- public static LazyOptional getDevice(final TileEntity tileEntity, final Direction side) {
+ public static List> getDevices(final TileEntity tileEntity, final Direction side) {
final World world = tileEntity.getWorld();
final BlockPos pos = tileEntity.getPos();
if (world == null) throw new IllegalArgumentException();
- return getDevice(world, pos, side);
+ return getDevices(world, pos, side);
}
- public static LazyOptional getDevice(final World world, final BlockPos pos, final Direction side) {
- return getDevice(new BlockDeviceQueryImpl(world, pos, side));
+ public static List> getDevices(final World world, final BlockPos pos, final Direction side) {
+ return getDevices(new BlockDeviceQueryImpl(world, pos, side));
}
- public static LazyOptional getDevice(final DeviceQuery query) {
- final ArrayList deviceInterfaces = new ArrayList<>();
- final ArrayList> optionals = new ArrayList<>();
- for (final DeviceInterfaceProvider provider : DEVICE_PROVIDERS) {
- final LazyOptional optional = provider.getDeviceInterface(query);
- optional.ifPresent((device) -> {
- deviceInterfaces.add(device);
- optionals.add(optional);
- });
- }
-
- if (deviceInterfaces.isEmpty()) {
- return LazyOptional.empty();
- } else {
- final LazyOptional compoundOptional = LazyOptional.of(() -> new DeviceInterfaceCollection(deviceInterfaces));
- for (final LazyOptional optional : optionals) {
- optional.addListener((ignored) -> compoundOptional.invalidate());
+ public static List> getDevices(final DeviceQuery query) {
+ final ArrayList> devices = new ArrayList<>();
+ for (final DeviceProvider provider : DEVICE_PROVIDERS) {
+ final LazyOptional device = provider.getDevice(query);
+ if (device.isPresent()) {
+ devices.add(device);
}
- return compoundOptional;
}
+ return devices;
}
}
diff --git a/src/main/java/li/cil/oc2/common/device/provider/TileEntityDeviceInterfaceProvider.java b/src/main/java/li/cil/oc2/common/device/provider/TileEntityDeviceProvider.java
similarity index 57%
rename from src/main/java/li/cil/oc2/common/device/provider/TileEntityDeviceInterfaceProvider.java
rename to src/main/java/li/cil/oc2/common/device/provider/TileEntityDeviceProvider.java
index ffd1432b..76bc773d 100644
--- a/src/main/java/li/cil/oc2/common/device/provider/TileEntityDeviceInterfaceProvider.java
+++ b/src/main/java/li/cil/oc2/common/device/provider/TileEntityDeviceProvider.java
@@ -1,24 +1,24 @@
package li.cil.oc2.common.device.provider;
-import li.cil.oc2.api.bus.device.DeviceInterface;
+import li.cil.oc2.api.bus.Device;
import li.cil.oc2.api.bus.device.object.Callbacks;
-import li.cil.oc2.api.bus.device.object.ObjectDeviceInterface;
+import li.cil.oc2.api.bus.device.object.ObjectDevice;
import li.cil.oc2.api.provider.BlockDeviceQuery;
import li.cil.oc2.common.util.WorldUtils;
import net.minecraft.tileentity.TileEntity;
import net.minecraftforge.common.util.LazyOptional;
-public final class TileEntityDeviceInterfaceProvider extends AbstractTileEntityDeviceInterfaceProvider {
- public TileEntityDeviceInterfaceProvider() {
+public final class TileEntityDeviceProvider extends AbstractTileEntityDeviceProvider {
+ public TileEntityDeviceProvider() {
super(TileEntity.class);
}
@Override
- public LazyOptional getDeviceInterface(final BlockDeviceQuery query, final TileEntity tileEntity) {
+ public LazyOptional getDeviceInterface(final BlockDeviceQuery query, final TileEntity tileEntity) {
if (Callbacks.hasMethods(tileEntity)) {
return LazyOptional.of(() -> {
final String typeName = WorldUtils.getBlockName(query.getWorld(), query.getQueryPosition());
- return new ObjectDeviceInterface(tileEntity, typeName);
+ return new ObjectDevice(tileEntity, typeName);
});
} else {
return LazyOptional.empty();
diff --git a/src/main/java/li/cil/oc2/common/tile/BusCableTileEntity.java b/src/main/java/li/cil/oc2/common/tile/BusCableTileEntity.java
index c3a55d89..f261ad89 100644
--- a/src/main/java/li/cil/oc2/common/tile/BusCableTileEntity.java
+++ b/src/main/java/li/cil/oc2/common/tile/BusCableTileEntity.java
@@ -16,7 +16,7 @@ public class BusCableTileEntity extends AbstractTileEntity {
super(OpenComputers.BUS_CABLE_TILE_ENTITY.get());
busElement = new TileEntityDeviceBusElement(this);
- setCapabilityIfAbsent(Capabilities.DEVICE_BUS_ELEMENT_CAPABILITY, busElement.getBusElement());
+ setCapabilityIfAbsent(Capabilities.DEVICE_BUS_ELEMENT_CAPABILITY, busElement);
}
public void handleNeighborChanged(final BlockPos pos) {
diff --git a/src/main/java/li/cil/oc2/common/tile/ComputerTileEntity.java b/src/main/java/li/cil/oc2/common/tile/ComputerTileEntity.java
index 930941f5..c8aa33f4 100644
--- a/src/main/java/li/cil/oc2/common/tile/ComputerTileEntity.java
+++ b/src/main/java/li/cil/oc2/common/tile/ComputerTileEntity.java
@@ -84,14 +84,14 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic
super(OpenComputers.COMPUTER_TILE_ENTITY.get());
busElement = new TileEntityDeviceBusElement(this);
- busController = new TileEntityDeviceBusController(this, this::joinVirtualMachine);
+ busController = new BusController();
busState = TileEntityDeviceBusController.State.SCAN_PENDING;
runState = RunState.STOPPED;
terminal = new Terminal();
virtualMachine = new VirtualMachine(busController);
- setCapabilityIfAbsent(Capabilities.DEVICE_BUS_ELEMENT_CAPABILITY, busElement.getBusElement());
+ setCapabilityIfAbsent(Capabilities.DEVICE_BUS_ELEMENT_CAPABILITY, busElement);
setCapabilityIfAbsent(Capabilities.DEVICE_BUS_CONTROLLER_CAPABILITY, busController);
}
@@ -412,6 +412,22 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic
}
}
+ private class BusController extends TileEntityDeviceBusController {
+ private BusController() {
+ super(ComputerTileEntity.this);
+ }
+
+ @Override
+ protected void onDevicesInvalid() {
+ virtualMachine.rpcAdapter.pause();
+ }
+
+ @Override
+ protected void onDevicesValid() {
+ virtualMachine.rpcAdapter.resume();
+ }
+ }
+
private final class ConsoleRunner extends VirtualMachineRunner {
// Thread-local buffers for lock-free read/writes in inner loop.
private final ByteArrayFIFOQueue outputBuffer = new ByteArrayFIFOQueue(1024);
diff --git a/src/main/java/li/cil/oc2/common/vm/InterruptAllocatorImpl.java b/src/main/java/li/cil/oc2/common/vm/InterruptAllocatorImpl.java
new file mode 100644
index 00000000..79a94e97
--- /dev/null
+++ b/src/main/java/li/cil/oc2/common/vm/InterruptAllocatorImpl.java
@@ -0,0 +1,52 @@
+package li.cil.oc2.common.vm;
+
+import li.cil.oc2.api.vm.InterruptAllocator;
+
+import java.util.BitSet;
+import java.util.OptionalInt;
+
+public final class InterruptAllocatorImpl implements InterruptAllocator {
+ private final BitSet interrupts;
+ private final int interruptCount;
+ private boolean isValid = true;
+
+ public InterruptAllocatorImpl(final BitSet interrupts, final int interruptCount) {
+ this.interrupts = new BitSet(interruptCount);
+ this.interrupts.or(interrupts);
+ this.interruptCount = interruptCount;
+ }
+
+ public BitSet complete() {
+ isValid = false;
+ return interrupts;
+ }
+
+ @Override
+ public OptionalInt claimInterrupt(final int interrupt) {
+ if (!isValid) {
+ return OptionalInt.empty();
+ }
+
+ if (interrupts.get(interrupt)) {
+ return claimInterrupt();
+ } else {
+ interrupts.set(interrupt);
+ return OptionalInt.of(interrupt);
+ }
+ }
+
+ @Override
+ public OptionalInt claimInterrupt() {
+ if (!isValid) {
+ return OptionalInt.empty();
+ }
+
+ final int interrupt = interrupts.nextClearBit(0);
+ if (interrupt >= interruptCount) {
+ return OptionalInt.empty();
+ }
+
+ interrupts.set(interrupt);
+ return OptionalInt.of(interrupt);
+ }
+}
diff --git a/src/main/java/li/cil/oc2/serialization/serializers/DeviceJsonSerializer.java b/src/main/java/li/cil/oc2/serialization/serializers/DeviceJsonSerializer.java
deleted file mode 100644
index b81f1a89..00000000
--- a/src/main/java/li/cil/oc2/serialization/serializers/DeviceJsonSerializer.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package li.cil.oc2.serialization.serializers;
-
-import com.google.gson.*;
-import li.cil.oc2.api.bus.device.Device;
-import li.cil.oc2.api.bus.device.DeviceMethod;
-
-import java.lang.reflect.Type;
-
-public final class DeviceJsonSerializer implements JsonSerializer {
- @Override
- public JsonElement serialize(final Device src, final Type typeOfSrc, final JsonSerializationContext context) {
- if (src == null) {
- return JsonNull.INSTANCE;
- }
-
- final JsonObject deviceJson = new JsonObject();
- deviceJson.add("deviceId", context.serialize(src.getUniqueIdentifier()));
- deviceJson.add("typeNames", context.serialize(src.getTypeNames()));
-
- final JsonArray methodsJson = new JsonArray();
- deviceJson.add("methods", methodsJson);
- for (final DeviceMethod method : src.getMethods()) {
- methodsJson.add(context.serialize(method, DeviceMethod.class));
- }
-
- return deviceJson;
- }
-}
diff --git a/src/main/java/li/cil/oc2/serialization/serializers/RPCDeviceWithIdentifierJsonSerializer.java b/src/main/java/li/cil/oc2/serialization/serializers/RPCDeviceWithIdentifierJsonSerializer.java
new file mode 100644
index 00000000..b5dbbfac
--- /dev/null
+++ b/src/main/java/li/cil/oc2/serialization/serializers/RPCDeviceWithIdentifierJsonSerializer.java
@@ -0,0 +1,28 @@
+package li.cil.oc2.serialization.serializers;
+
+import com.google.gson.*;
+import li.cil.oc2.api.bus.device.rpc.RPCMethod;
+import li.cil.oc2.common.bus.RPCAdapter;
+
+import java.lang.reflect.Type;
+
+public final class RPCDeviceWithIdentifierJsonSerializer implements JsonSerializer {
+ @Override
+ public JsonElement serialize(final RPCAdapter.RPCDeviceWithIdentifier src, final Type typeOfSrc, final JsonSerializationContext context) {
+ if (src == null) {
+ return JsonNull.INSTANCE;
+ }
+
+ final JsonObject deviceJson = new JsonObject();
+ deviceJson.add("deviceId", context.serialize(src.identifier));
+ deviceJson.add("typeNames", context.serialize(src.device.getTypeNames()));
+
+ final JsonArray methodsJson = new JsonArray();
+ deviceJson.add("methods", methodsJson);
+ for (final RPCMethod method : src.device.getMethods()) {
+ methodsJson.add(context.serialize(method, RPCMethod.class));
+ }
+
+ return deviceJson;
+ }
+}
diff --git a/src/main/java/li/cil/oc2/serialization/serializers/DeviceMethodJsonSerializer.java b/src/main/java/li/cil/oc2/serialization/serializers/RPCMethodJsonSerializer.java
similarity index 72%
rename from src/main/java/li/cil/oc2/serialization/serializers/DeviceMethodJsonSerializer.java
rename to src/main/java/li/cil/oc2/serialization/serializers/RPCMethodJsonSerializer.java
index f04cfdb6..6ae8e921 100644
--- a/src/main/java/li/cil/oc2/serialization/serializers/DeviceMethodJsonSerializer.java
+++ b/src/main/java/li/cil/oc2/serialization/serializers/RPCMethodJsonSerializer.java
@@ -1,14 +1,14 @@
package li.cil.oc2.serialization.serializers;
import com.google.gson.*;
-import li.cil.oc2.api.bus.device.DeviceMethod;
-import li.cil.oc2.api.bus.device.DeviceMethodParameter;
+import li.cil.oc2.api.bus.device.rpc.RPCMethod;
+import li.cil.oc2.api.bus.device.rpc.RPCParameter;
import java.lang.reflect.Type;
-public final class DeviceMethodJsonSerializer implements JsonSerializer {
+public final class RPCMethodJsonSerializer implements JsonSerializer {
@Override
- public JsonElement serialize(final DeviceMethod method, final Type typeOfMethod, final JsonSerializationContext context) {
+ public JsonElement serialize(final RPCMethod method, final Type typeOfMethod, final JsonSerializationContext context) {
if (method == null) {
return JsonNull.INSTANCE;
}
@@ -23,8 +23,8 @@ public final class DeviceMethodJsonSerializer implements JsonSerializer parameterJson.addProperty("name", s));
diff --git a/src/test/java/li/cil/oc2/bus/DeviceBusTests.java b/src/test/java/li/cil/oc2/bus/DeviceBusTests.java
index 005eb4e8..ca982cf4 100644
--- a/src/test/java/li/cil/oc2/bus/DeviceBusTests.java
+++ b/src/test/java/li/cil/oc2/bus/DeviceBusTests.java
@@ -1,7 +1,7 @@
package li.cil.oc2.bus;
import li.cil.oc2.api.bus.DeviceBusElement;
-import li.cil.oc2.api.bus.device.Device;
+import li.cil.oc2.api.bus.device.rpc.RPCDevice;
import li.cil.oc2.common.bus.TileEntityDeviceBusController;
import li.cil.oc2.common.capabilities.Capabilities;
import net.minecraft.tileentity.TileEntity;
@@ -16,7 +16,6 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.Collections;
-import java.util.UUID;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
@@ -50,7 +49,7 @@ public class DeviceBusTests {
.thenReturn(LazyOptional.of(() -> busControllerBusElement));
when(busControllerBusElement.getLocalDevices()).thenReturn(Collections.emptyList());
- busController = new TileEntityDeviceBusController(busControllerTileEntity);
+ busController = new TestBusController();
}
@Test
@@ -68,11 +67,9 @@ public class DeviceBusTests {
public void scanSuccessfulWithLocalElement() {
when(world.chunkExists(anyInt(), anyInt())).thenReturn(true);
- final Device device = mock(Device.class);
+ final RPCDevice device = mock(RPCDevice.class);
when(busControllerBusElement.getLocalDevices()).thenReturn(Collections.singletonList(device));
- when(device.getUniqueIdentifier()).thenReturn(UUID.randomUUID());
-
Assertions.assertEquals(TileEntityDeviceBusController.State.READY, busController.scan());
verify(busControllerBusElement).addController(busController);
@@ -103,4 +100,10 @@ public class DeviceBusTests {
return busElement;
}
+
+ private final class TestBusController extends TileEntityDeviceBusController {
+ public TestBusController() {
+ super(busControllerTileEntity);
+ }
+ }
}
diff --git a/src/test/java/li/cil/oc2/vm/AbstractTestMethod.java b/src/test/java/li/cil/oc2/vm/AbstractTestMethod.java
index 1d85e6cd..75e8fc06 100644
--- a/src/test/java/li/cil/oc2/vm/AbstractTestMethod.java
+++ b/src/test/java/li/cil/oc2/vm/AbstractTestMethod.java
@@ -1,18 +1,18 @@
package li.cil.oc2.vm;
-import li.cil.oc2.api.bus.device.DeviceMethod;
-import li.cil.oc2.api.bus.device.DeviceMethodParameter;
+import li.cil.oc2.api.bus.device.rpc.RPCMethod;
+import li.cil.oc2.api.bus.device.rpc.RPCParameter;
import javax.annotation.Nullable;
import java.util.Optional;
-abstract class AbstractTestMethod implements DeviceMethod {
+abstract class AbstractTestMethod implements RPCMethod {
private final Class> returnType;
- private final DeviceMethodParameter[] parameters;
+ private final RPCParameter[] parameters;
protected AbstractTestMethod(final Class> returnType, final Class>... parameterTypes) {
this.returnType = returnType;
- parameters = new DeviceMethodParameter[parameterTypes.length];
+ parameters = new RPCParameter[parameterTypes.length];
for (int i = 0; i < parameterTypes.length; i++) {
parameters[i] = new TestParameter("arg" + i, parameterTypes[i]);
}
@@ -34,11 +34,11 @@ abstract class AbstractTestMethod implements DeviceMethod {
}
@Override
- public DeviceMethodParameter[] getParameters() {
+ public RPCParameter[] getParameters() {
return parameters;
}
- private static final class TestParameter implements DeviceMethodParameter {
+ private static final class TestParameter implements RPCParameter {
@Nullable private final String name;
private final Class> type;
diff --git a/src/test/java/li/cil/oc2/vm/RPCAdapterTests.java b/src/test/java/li/cil/oc2/vm/RPCAdapterTests.java
index ff886f3d..eff1d349 100644
--- a/src/test/java/li/cil/oc2/vm/RPCAdapterTests.java
+++ b/src/test/java/li/cil/oc2/vm/RPCAdapterTests.java
@@ -3,15 +3,13 @@ package li.cil.oc2.vm;
import com.google.gson.*;
import it.unimi.dsi.fastutil.bytes.ByteArrayFIFOQueue;
import li.cil.oc2.api.bus.DeviceBusController;
-import li.cil.oc2.api.bus.device.Device;
-import li.cil.oc2.api.bus.device.DeviceMethod;
import li.cil.oc2.api.bus.device.object.Callback;
-import li.cil.oc2.api.bus.device.object.ObjectDeviceInterface;
+import li.cil.oc2.api.bus.device.object.ObjectDevice;
import li.cil.oc2.api.bus.device.object.Parameter;
+import li.cil.oc2.api.bus.device.rpc.RPCDevice;
+import li.cil.oc2.api.bus.device.rpc.RPCMethod;
import li.cil.oc2.common.bus.RPCAdapter;
-import li.cil.oc2.common.device.DeviceImpl;
import li.cil.sedna.api.device.serial.SerialDevice;
-import net.minecraftforge.common.util.LazyOptional;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -20,13 +18,14 @@ import javax.annotation.Nullable;
import java.io.ByteArrayOutputStream;
import java.util.Collections;
import java.util.List;
-import java.util.Optional;
import java.util.UUID;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class RPCAdapterTests {
+ private static final UUID DEVICE_UUID = java.util.UUID.randomUUID();
+
private TestSerialDevice serialDevice;
private DeviceBusController busController;
private RPCAdapter rpcAdapter;
@@ -41,8 +40,8 @@ public class RPCAdapterTests {
@Test
public void resetAndReadDescriptor() {
final VoidIntMethod method = new VoidIntMethod();
- final TestDeviceInterface device = new TestDeviceInterface(method);
- setDevice(device);
+ final TestRPCDeviceInterface device = new TestRPCDeviceInterface(method);
+ setDevice(device, DEVICE_UUID);
final JsonObject request = new JsonObject();
request.addProperty("type", "status");
@@ -66,10 +65,10 @@ public class RPCAdapterTests {
@Test
public void simpleMethod() {
final VoidIntMethod method = new VoidIntMethod();
- final TestDeviceInterface device = new TestDeviceInterface(method);
- setDevice(device);
+ final TestRPCDeviceInterface device = new TestRPCDeviceInterface(method);
+ setDevice(device, DEVICE_UUID);
- invokeMethod(device, method.getName(), 0xdeadbeef);
+ invokeMethod(DEVICE_UUID, method.getName(), 0xdeadbeef);
Assertions.assertEquals(0xdeadbeef, method.passedValue);
}
@@ -77,10 +76,10 @@ public class RPCAdapterTests {
@Test
public void returningMethod() {
final IntLongMethod method = new IntLongMethod();
- final TestDeviceInterface device = new TestDeviceInterface(method);
- setDevice(device);
+ final TestRPCDeviceInterface device = new TestRPCDeviceInterface(method);
+ setDevice(device, DEVICE_UUID);
- final JsonElement result = invokeMethod(device, method.getName(), 0xdeadbeefcafebabeL);
+ final JsonElement result = invokeMethod(DEVICE_UUID, method.getName(), 0xdeadbeefcafebabeL);
Assertions.assertNotNull(result);
Assertions.assertTrue(result.isJsonPrimitive());
Assertions.assertEquals(0xcafebabe, result.getAsInt());
@@ -89,23 +88,26 @@ public class RPCAdapterTests {
@Test
public void annotatedObject() {
final SimpleObject object = new SimpleObject();
- final ObjectDeviceInterface device = new ObjectDeviceInterface(object);
- final DeviceImpl identifiableDevice = new DeviceImpl(LazyOptional.of(() -> device), UUID.randomUUID());
- setDevice(identifiableDevice);
+ final ObjectDevice device = new ObjectDevice(object);
+ setDevice(device, DEVICE_UUID);
- Assertions.assertEquals(42 + 23, invokeMethod(identifiableDevice, "add", 42, 23).getAsInt());
+ Assertions.assertEquals(42 + 23, invokeMethod(DEVICE_UUID, "add", 42, 23).getAsInt());
}
- private void setDevice(final Device device) {
- when(busController.getDevices()).thenReturn(Collections.singletonList(device));
- when(busController.getDevice(device.getUniqueIdentifier())).thenReturn(Optional.of(device));
+ private void setDevice(final RPCDevice device, final UUID deviceId) {
+ when(busController.getDevices()).thenReturn(Collections.singleton(device));
+ when(busController.getDeviceIdentifiers(device)).thenReturn(Collections.singleton(deviceId));
+
+ // trigger device cache rebuild
+ rpcAdapter.pause();
+ rpcAdapter.resume();
}
- private JsonElement invokeMethod(final Device device, final String name, final Object... parameters) {
+ private JsonElement invokeMethod(final UUID deviceId, final String name, final Object... parameters) {
final JsonObject request = new JsonObject();
request.addProperty("type", "invoke");
final JsonObject methodInvocation = new JsonObject();
- methodInvocation.addProperty("deviceId", device.getUniqueIdentifier().toString());
+ methodInvocation.addProperty("deviceId", deviceId.toString());
methodInvocation.addProperty("name", name);
final JsonArray parametersJson = new JsonArray();
methodInvocation.add("parameters", parametersJson);
@@ -196,7 +198,7 @@ public class RPCAdapterTests {
}
if (bytes.size() > 0) {
- return new String(bytes.toByteArray());
+ return bytes.toString();
} else {
return null;
}
@@ -218,12 +220,10 @@ public class RPCAdapterTests {
}
}
- private static final class TestDeviceInterface implements Device {
- private static final UUID UUID = java.util.UUID.randomUUID();
+ private static final class TestRPCDeviceInterface implements RPCDevice {
+ private final RPCMethod method;
- private final DeviceMethod method;
-
- public TestDeviceInterface(final DeviceMethod method) {
+ public TestRPCDeviceInterface(final RPCMethod method) {
this.method = method;
}
@@ -233,13 +233,8 @@ public class RPCAdapterTests {
}
@Override
- public List getMethods() {
+ public List getMethods() {
return Collections.singletonList(method);
}
-
- @Override
- public UUID getUniqueIdentifier() {
- return UUID;
- }
}
}