diff --git a/src/main/java/li/cil/oc2/api/bus/device/data/FirmwareLoader.java b/src/main/java/li/cil/oc2/api/bus/device/data/FirmwareLoader.java
new file mode 100644
index 00000000..bc392dd3
--- /dev/null
+++ b/src/main/java/li/cil/oc2/api/bus/device/data/FirmwareLoader.java
@@ -0,0 +1,12 @@
+package li.cil.oc2.api.bus.device.data;
+
+import li.cil.oc2.api.bus.device.vm.VMDevice;
+
+/**
+ * This interface serves as a marker for devices that load firmware.
+ *
+ * It is used exclusively to check if some firmware will be loaded early in the
+ * startup process, to provide a useful error to the user if none is present.
+ */
+public interface FirmwareLoader extends VMDevice {
+}
diff --git a/src/main/java/li/cil/oc2/common/bus/device/item/ByteBufferFlashMemoryVMDevice.java b/src/main/java/li/cil/oc2/common/bus/device/item/ByteBufferFlashMemoryVMDevice.java
index a1e9b9fb..73f73c38 100644
--- a/src/main/java/li/cil/oc2/common/bus/device/item/ByteBufferFlashMemoryVMDevice.java
+++ b/src/main/java/li/cil/oc2/common/bus/device/item/ByteBufferFlashMemoryVMDevice.java
@@ -2,6 +2,7 @@ package li.cil.oc2.common.bus.device.item;
import com.google.common.eventbus.Subscribe;
import li.cil.oc2.api.bus.device.ItemDevice;
+import li.cil.oc2.api.bus.device.data.FirmwareLoader;
import li.cil.oc2.api.bus.device.vm.VMContext;
import li.cil.oc2.api.bus.device.vm.VMDevice;
import li.cil.oc2.api.bus.device.vm.VMDeviceLoadResult;
@@ -21,7 +22,7 @@ import java.nio.ByteBuffer;
import java.util.OptionalLong;
@SuppressWarnings("UnstableApiUsage")
-public final class ByteBufferFlashMemoryVMDevice extends IdentityProxy implements VMDevice, ItemDevice {
+public final class ByteBufferFlashMemoryVMDevice extends IdentityProxy implements VMDevice, ItemDevice, FirmwareLoader {
public static final String DATA_TAG_NAME = "data";
///////////////////////////////////////////////////////////////
diff --git a/src/main/java/li/cil/oc2/common/bus/device/item/FirmwareFlashMemoryVMDevice.java b/src/main/java/li/cil/oc2/common/bus/device/item/FirmwareFlashMemoryVMDevice.java
index 705691db..138655ee 100644
--- a/src/main/java/li/cil/oc2/common/bus/device/item/FirmwareFlashMemoryVMDevice.java
+++ b/src/main/java/li/cil/oc2/common/bus/device/item/FirmwareFlashMemoryVMDevice.java
@@ -3,6 +3,7 @@ package li.cil.oc2.common.bus.device.item;
import com.google.common.eventbus.Subscribe;
import li.cil.oc2.api.bus.device.ItemDevice;
import li.cil.oc2.api.bus.device.data.Firmware;
+import li.cil.oc2.api.bus.device.data.FirmwareLoader;
import li.cil.oc2.api.bus.device.vm.VMContext;
import li.cil.oc2.api.bus.device.vm.VMDevice;
import li.cil.oc2.api.bus.device.vm.VMDeviceLoadResult;
@@ -15,7 +16,7 @@ import net.minecraft.item.ItemStack;
import net.minecraft.util.text.TranslationTextComponent;
@SuppressWarnings("UnstableApiUsage")
-public final class FirmwareFlashMemoryVMDevice extends IdentityProxy implements VMDevice, ItemDevice {
+public final class FirmwareFlashMemoryVMDevice extends IdentityProxy implements VMDevice, ItemDevice, FirmwareLoader {
private final Firmware firmware;
private MemoryMap memoryMap;
diff --git a/src/main/java/li/cil/oc2/common/serialization/serializers/JsonArraySerializer.java b/src/main/java/li/cil/oc2/common/serialization/serializers/JsonArraySerializer.java
index 3332310e..96af5a72 100644
--- a/src/main/java/li/cil/oc2/common/serialization/serializers/JsonArraySerializer.java
+++ b/src/main/java/li/cil/oc2/common/serialization/serializers/JsonArraySerializer.java
@@ -16,6 +16,7 @@ public final class JsonArraySerializer implements Serializer {
visitor.putObject("value", String.class, jsonArray.toString());
}
+ @Nullable
@Override
public JsonArray deserialize(final DeserializationVisitor visitor, final Class type, @Nullable final Object value) throws SerializationException {
JsonArray array = (JsonArray) value;
diff --git a/src/main/java/li/cil/oc2/common/serialization/serializers/TextComponentSerializer.java b/src/main/java/li/cil/oc2/common/serialization/serializers/TextComponentSerializer.java
index 8e6a0a44..28bacf6a 100644
--- a/src/main/java/li/cil/oc2/common/serialization/serializers/TextComponentSerializer.java
+++ b/src/main/java/li/cil/oc2/common/serialization/serializers/TextComponentSerializer.java
@@ -15,6 +15,7 @@ public final class TextComponentSerializer implements Serializer
visitor.putObject("value", String.class, json);
}
+ @Nullable
@Override
public ITextComponent deserialize(final DeserializationVisitor visitor, final Class type, @Nullable final Object value) throws SerializationException {
if (!visitor.exists("value")) {
diff --git a/src/main/java/li/cil/oc2/common/vm/AbstractVirtualMachineState.java b/src/main/java/li/cil/oc2/common/vm/AbstractVirtualMachineState.java
index a2945e1f..1b9a6720 100644
--- a/src/main/java/li/cil/oc2/common/vm/AbstractVirtualMachineState.java
+++ b/src/main/java/li/cil/oc2/common/vm/AbstractVirtualMachineState.java
@@ -1,5 +1,6 @@
package li.cil.oc2.common.vm;
+import li.cil.oc2.api.bus.device.data.FirmwareLoader;
import li.cil.oc2.api.bus.device.vm.VMDeviceLoadResult;
import li.cil.oc2.api.bus.device.vm.event.VMPausingEvent;
import li.cil.oc2.common.Constants;
@@ -24,8 +25,6 @@ public abstract class AbstractVirtualMachineState device instanceof FirmwareLoader)) {
+ setBootError(new TranslationTextComponent(Constants.COMPUTER_ERROR_MISSING_FIRMWARE));
+ setRunState(RunState.STOPPED);
+ break;
+ }
// May have a valid runner after load. In which case we just had to wait for
// bus setup and devices to load. So we can keep using it.
@@ -205,12 +209,12 @@ public abstract class AbstractVirtualMachineState