Split out identifier for devices into separate sub-interface.

It's only needed at bus level so we can wrap at that point.
This commit is contained in:
Florian Nücke
2020-11-29 13:36:34 +01:00
parent 49160acf6d
commit fd70381f31
12 changed files with 138 additions and 47 deletions

View File

@@ -1,13 +1,13 @@
package li.cil.oc2.api.bus;
import li.cil.oc2.api.device.Device;
import li.cil.oc2.api.device.IdentifiableDevice;
import java.util.Collection;
/**
* A device bus provides the interface by which {@link Device} can be made available
* to a {@link DeviceBusController}, which is usually used by VMs to access devices.
* <p>
*/
public interface DeviceBus {
/**
@@ -22,24 +22,24 @@ public interface DeviceBus {
*
* @param device the device to add to the bus.
*/
void addDevice(Device device);
void addDevice(IdentifiableDevice device);
/**
* Removes a device from this device bus.
* <p>
* If the device has not been added with {@link #addDevice(Device)} before calling
* If the device has not been added with {@link #addDevice(IdentifiableDevice)} before calling
* this method, this method is a no-op.
*
* @param device the device to remove from the bus.
*/
void removeDevice(Device device);
void removeDevice(IdentifiableDevice device);
/**
* The list of all devices currently registered with this device bus.
*
* @return the list of all devices that are currently on this bus.
*/
Collection<Device> getDevices();
Collection<IdentifiableDevice> getDevices();
/**
* Schedules a rescan of the device bus.

View File

@@ -1,6 +1,7 @@
package li.cil.oc2.api.bus;
import li.cil.oc2.api.device.Device;
import li.cil.oc2.api.device.IdentifiableDevice;
import java.util.Collection;
@@ -11,7 +12,7 @@ import java.util.Collection;
* <p>
* This interface is usually provided by VM containers and used to collect connected
* {@link Device}s by aggregating the devices that were added to the device bus elements
* via {@link DeviceBusElement#addDevice(Device)}.
* via {@link DeviceBus#addDevice(IdentifiableDevice)}.
* <p>
* The only way for {@link DeviceBusElement}s to be added to a bus is for a
* {@link DeviceBusController} to detect them during a scan.
@@ -54,5 +55,5 @@ public interface DeviceBusController {
*
* @return the list of all devices on the bus managed by this controller.
*/
Collection<Device> getDevices();
Collection<IdentifiableDevice> getDevices();
}

View File

@@ -1,6 +1,6 @@
package li.cil.oc2.api.bus;
import li.cil.oc2.api.device.Device;
import li.cil.oc2.api.device.IdentifiableDevice;
import javax.annotation.Nullable;
import java.util.Collection;
@@ -48,5 +48,5 @@ public interface DeviceBusElement extends DeviceBus {
*
* @return the devices that have been added to this element.
*/
Collection<Device> getLocalDevices();
Collection<IdentifiableDevice> getLocalDevices();
}

View File

@@ -4,7 +4,6 @@ import li.cil.oc2.api.bus.DeviceBus;
import li.cil.oc2.api.device.object.ObjectDevice;
import java.util.List;
import java.util.UUID;
/**
* Defines a device that may be added to a {@link DeviceBus}.
@@ -15,21 +14,12 @@ import java.util.UUID;
* @see ObjectDevice
*/
public interface Device {
/**
* An id unique to this device.
* <p>
* This id must persist over save/load to prevent code in a running VM losing
* track of the device.
*/
UUID getUniqueId();
/**
* A list of device type names for this device.
* <p>
* Devices may be identified by multiple type names. Although every atomic
* implementation will usually only have one, when compounding such modular
* devices into a {@link CompoundDevice} all the underlying type names can
* thus be retained.
* devices all the underlying type names can thus be retained.
* <p>
* In a more general sense, these can be considered tags the device can be
* referenced by inside a VM.

View File

@@ -0,0 +1,23 @@
package li.cil.oc2.api.device;
import java.util.UUID;
/**
* Specialization of devices that allows referencing the device by a {@link UUID}.
* <p>
* This type is required when adding devices to a {@link li.cil.oc2.api.bus.DeviceBus}
* or referencing devices on a bus. Some {@link li.cil.oc2.api.bus.DeviceBusElement}s
* may take care of wrapping connected devices automatically.
* <p>
* Note that {@link li.cil.oc2.api.device.provider.DeviceProvider}s are <em>not</em>
* required to return identifiable devices.
*/
public interface IdentifiableDevice extends Device {
/**
* An id unique to this device.
* <p>
* This id must persist over save/load to prevent code in a running VM losing
* track of the device.
*/
UUID getUniqueId();
}

View File

@@ -1,4 +1,4 @@
package li.cil.oc2.common.vm;
package li.cil.oc2.common.bus;
import com.google.gson.*;
import li.cil.ceres.api.Serialized;
@@ -7,7 +7,8 @@ import li.cil.oc2.api.bus.DeviceBusElement;
import li.cil.oc2.api.device.Device;
import li.cil.oc2.api.device.DeviceMethod;
import li.cil.oc2.api.device.DeviceMethodParameter;
import li.cil.oc2.common.util.TileEntities;
import li.cil.oc2.api.device.IdentifiableDevice;
import li.cil.oc2.common.util.TileEntityUtils;
import li.cil.sedna.api.device.Steppable;
import li.cil.sedna.api.device.serial.SerialDevice;
import net.minecraft.tileentity.TileEntity;
@@ -50,7 +51,7 @@ public class DeviceBusControllerImpl implements DeviceBusController, Steppable {
private static final String MESSAGE_TYPE_INVOKE_METHOD = "invoke";
private final Set<DeviceBusElement> elements = new HashSet<>();
private final ConcurrentHashMap<UUID, Device> devices = new ConcurrentHashMap<>();
private final ConcurrentHashMap<UUID, IdentifiableDevice> devices = new ConcurrentHashMap<>();
private final SerialDevice serialDevice;
private final Gson gson;
@@ -92,7 +93,7 @@ public class DeviceBusControllerImpl implements DeviceBusController, Steppable {
public void scanDevices() {
devices.clear();
for (final DeviceBusElement element : elements) {
for (final Device device : element.getLocalDevices()) {
for (final IdentifiableDevice device : element.getLocalDevices()) {
final UUID uuid = device.getUniqueId();
devices.putIfAbsent(uuid, device);
}
@@ -100,7 +101,7 @@ public class DeviceBusControllerImpl implements DeviceBusController, Steppable {
}
@Override
public Collection<Device> getDevices() {
public Collection<IdentifiableDevice> getDevices() {
return devices.values();
}
@@ -148,7 +149,7 @@ public class DeviceBusControllerImpl implements DeviceBusController, Steppable {
continue;
}
final Optional<DeviceBusElement> capability = TileEntities.getInterfaceForSide(tileEntity, DeviceBusElement.class, edge.face);
final Optional<DeviceBusElement> capability = TileEntityUtils.getInterfaceForSide(tileEntity, DeviceBusElement.class, edge.face);
if (capability.isPresent()) {
if (busPositions.add(edge.position) && busPositions.size() > MAX_BUS_ELEMENT_COUNT) {
elements.clear();
@@ -159,7 +160,7 @@ public class DeviceBusControllerImpl implements DeviceBusController, Steppable {
elements.add(element);
for (final Direction face : faces) {
final Optional<DeviceBusElement> otherCapability = TileEntities.getInterfaceForSide(tileEntity, DeviceBusElement.class, face);
final Optional<DeviceBusElement> otherCapability = TileEntityUtils.getInterfaceForSide(tileEntity, DeviceBusElement.class, face);
otherCapability.ifPresent(otherElement -> {
final boolean isConnectedToIncomingEdge = otherElement == element;
if (!isConnectedToIncomingEdge) {
@@ -423,9 +424,9 @@ public class DeviceBusControllerImpl implements DeviceBusController, Steppable {
}
}
private static final class DeviceSerializer implements JsonSerializer<Device> {
private static final class DeviceSerializer implements JsonSerializer<IdentifiableDevice> {
@Override
public JsonElement serialize(final Device src, final Type typeOfSrc, final JsonSerializationContext context) {
public JsonElement serialize(final IdentifiableDevice src, final Type typeOfSrc, final JsonSerializationContext context) {
if (src == null) {
return JsonNull.INSTANCE;
}

View File

@@ -1,8 +1,8 @@
package li.cil.oc2.common.vm;
package li.cil.oc2.common.bus;
import li.cil.oc2.api.bus.DeviceBusController;
import li.cil.oc2.api.bus.DeviceBusElement;
import li.cil.oc2.api.device.Device;
import li.cil.oc2.api.device.IdentifiableDevice;
import javax.annotation.Nullable;
import java.util.ArrayList;
@@ -11,7 +11,7 @@ import java.util.List;
import java.util.Optional;
public class DeviceBusElementImpl implements DeviceBusElement {
private final List<Device> devices = new ArrayList<>();
private final List<IdentifiableDevice> devices = new ArrayList<>();
@Nullable private DeviceBusController controller;
@Override
@@ -24,12 +24,12 @@ public class DeviceBusElementImpl implements DeviceBusElement {
}
@Override
public Collection<Device> getLocalDevices() {
public Collection<IdentifiableDevice> getLocalDevices() {
return devices;
}
@Override
public void addDevice(final Device device) {
public void addDevice(final IdentifiableDevice device) {
devices.add(device);
if (controller != null) {
controller.scanDevices();
@@ -37,7 +37,7 @@ public class DeviceBusElementImpl implements DeviceBusElement {
}
@Override
public void removeDevice(final Device device) {
public void removeDevice(final IdentifiableDevice device) {
devices.remove(device);
if (controller != null) {
controller.scanDevices();
@@ -45,7 +45,7 @@ public class DeviceBusElementImpl implements DeviceBusElement {
}
@Override
public Collection<Device> getDevices() {
public Collection<IdentifiableDevice> getDevices() {
if (controller != null) {
return controller.getDevices();
} else {

View File

@@ -0,0 +1,7 @@
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
package li.cil.oc2.common.bus;
import mcp.MethodsReturnNonnullByDefault;
import javax.annotation.ParametersAreNonnullByDefault;

View File

@@ -0,0 +1,48 @@
package li.cil.oc2.common.device;
import li.cil.oc2.api.device.Device;
import li.cil.oc2.api.device.DeviceMethod;
import li.cil.oc2.api.device.IdentifiableDevice;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
public final class IdentifiableDeviceImpl implements IdentifiableDevice {
private final Device device;
private final UUID uuid;
public IdentifiableDeviceImpl(final Device device, final UUID uuid) {
this.device = device;
this.uuid = uuid;
}
@Override
public UUID getUniqueId() {
return uuid;
}
@Override
public List<String> getTypeNames() {
return device.getTypeNames();
}
@Override
public List<DeviceMethod> getMethods() {
return device.getMethods();
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final IdentifiableDeviceImpl that = (IdentifiableDeviceImpl) o;
return uuid.equals(that.uuid) &&
device.equals(that.device);
}
@Override
public int hashCode() {
return Objects.hash(uuid, device);
}
}

View File

@@ -0,0 +1,7 @@
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
package li.cil.oc2.common.device;
import mcp.MethodsReturnNonnullByDefault;
import javax.annotation.ParametersAreNonnullByDefault;

View File

@@ -1,9 +1,9 @@
package li.cil.oc2.bus;
import li.cil.oc2.api.bus.DeviceBusElement;
import li.cil.oc2.api.device.Device;
import li.cil.oc2.api.device.IdentifiableDevice;
import li.cil.oc2.common.bus.DeviceBusControllerImpl;
import li.cil.oc2.common.capabilities.Capabilities;
import li.cil.oc2.common.vm.DeviceBusControllerImpl;
import li.cil.sedna.api.device.serial.SerialDevice;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos;
@@ -62,7 +62,7 @@ public class DeviceBusTests {
final DeviceBusElement busElement = mock(DeviceBusElement.class);
when(tileEntity.getCapability(eq(busElementCapability), any())).thenReturn(LazyOptional.of(() -> busElement));
final Device device = mock(Device.class);
final IdentifiableDevice device = mock(IdentifiableDevice.class);
when(busElement.getLocalDevices()).thenReturn(Collections.singletonList(device));
when(device.getUniqueId()).thenReturn(UUID.randomUUID());

View File

@@ -3,15 +3,15 @@ package li.cil.oc2.vm;
import com.google.gson.*;
import it.unimi.dsi.fastutil.bytes.ByteArrayFIFOQueue;
import li.cil.oc2.api.bus.DeviceBusElement;
import li.cil.oc2.api.device.AbstractDevice;
import li.cil.oc2.api.device.Device;
import li.cil.oc2.api.device.DeviceMethod;
import li.cil.oc2.api.device.IdentifiableDevice;
import li.cil.oc2.api.device.object.Callback;
import li.cil.oc2.api.device.object.ObjectDevice;
import li.cil.oc2.api.device.object.Parameter;
import li.cil.oc2.common.bus.DeviceBusControllerImpl;
import li.cil.oc2.common.bus.DeviceBusElementImpl;
import li.cil.oc2.common.capabilities.Capabilities;
import li.cil.oc2.common.vm.DeviceBusControllerImpl;
import li.cil.oc2.common.vm.DeviceBusElementImpl;
import li.cil.oc2.common.device.IdentifiableDeviceImpl;
import li.cil.sedna.api.device.serial.SerialDevice;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos;
@@ -28,6 +28,7 @@ import javax.annotation.Nullable;
import java.io.ByteArrayOutputStream;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.mock;
@@ -117,14 +118,15 @@ public class ObjectDeviceProtocolTests {
public void annotatedObject() {
final SimpleObject object = new SimpleObject();
final ObjectDevice device = new ObjectDevice(object);
final IdentifiableDeviceImpl identifiableDevice = new IdentifiableDeviceImpl(device, UUID.randomUUID());
busElement.addDevice(device);
busElement.addDevice(identifiableDevice);
controller.scan(world, CONTROLLER_POS);
Assertions.assertEquals(42 + 23, invokeMethod(device, "add", 42, 23).getAsInt());
Assertions.assertEquals(42 + 23, invokeMethod(identifiableDevice, "add", 42, 23).getAsInt());
}
private JsonElement invokeMethod(final Device device, final String name, final Object... parameters) {
private JsonElement invokeMethod(final IdentifiableDevice device, final String name, final Object... parameters) {
final JsonObject request = new JsonObject();
request.addProperty("type", "invoke");
final JsonObject methodInvocation = new JsonObject();
@@ -241,16 +243,28 @@ public class ObjectDeviceProtocolTests {
}
}
private static final class TestDevice extends AbstractDevice {
private static final class TestDevice implements IdentifiableDevice {
private static final UUID UUID = java.util.UUID.randomUUID();
private final DeviceMethod method;
public TestDevice(final DeviceMethod method) {
this.method = method;
}
@Override
public List<String> getTypeNames() {
return Collections.singletonList(getClass().getSimpleName());
}
@Override
public List<DeviceMethod> getMethods() {
return Collections.singletonList(method);
}
@Override
public UUID getUniqueId() {
return UUID;
}
}
}