Made computer block serializable.
This commit is contained in:
@@ -1,10 +1,27 @@
|
||||
package li.cil.oc2.common;
|
||||
|
||||
import li.cil.oc2.common.blobs.BlobStorage;
|
||||
import li.cil.oc2.common.network.Network;
|
||||
import li.cil.oc2.common.sandbox.Allocator;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
|
||||
import net.minecraftforge.fml.event.server.FMLServerAboutToStartEvent;
|
||||
import net.minecraftforge.fml.event.server.FMLServerStoppedEvent;
|
||||
|
||||
public final class CommonSetup {
|
||||
public static void run(final FMLCommonSetupEvent event) {
|
||||
Network.setup();
|
||||
|
||||
MinecraftForge.EVENT_BUS.addListener(CommonSetup::handleServerAboutToStart);
|
||||
MinecraftForge.EVENT_BUS.addListener(CommonSetup::handleServerStoppedEvent);
|
||||
}
|
||||
|
||||
public static void handleServerAboutToStart(final FMLServerAboutToStartEvent event) {
|
||||
BlobStorage.setServer(event.getServer());
|
||||
}
|
||||
|
||||
public static void handleServerStoppedEvent(final FMLServerStoppedEvent event) {
|
||||
BlobStorage.synchronize();
|
||||
Allocator.resetAndCheckLeaks();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,25 @@
|
||||
package li.cil.oc2.common.tile;
|
||||
|
||||
import it.unimi.dsi.fastutil.bytes.ByteArrayFIFOQueue;
|
||||
import li.cil.ceres.api.Serialized;
|
||||
import li.cil.oc2.OpenComputers;
|
||||
import li.cil.oc2.client.gui.terminal.Terminal;
|
||||
import li.cil.oc2.common.blobs.BlobStorage;
|
||||
import li.cil.oc2.common.network.Network;
|
||||
import li.cil.oc2.common.network.TerminalBlockOutputMessage;
|
||||
import li.cil.oc2.common.sandbox.Allocator;
|
||||
import li.cil.oc2.common.vm.VirtualMachineRunner;
|
||||
import li.cil.oc2.serialization.NBTSerialization;
|
||||
import li.cil.sedna.api.Sizes;
|
||||
import li.cil.sedna.api.device.BlockDevice;
|
||||
import li.cil.sedna.api.device.PhysicalMemory;
|
||||
import li.cil.sedna.buildroot.Buildroot;
|
||||
import li.cil.sedna.device.block.ByteBufferBlockDevice;
|
||||
import li.cil.sedna.device.memory.Memory;
|
||||
import li.cil.sedna.device.serial.UART16550A;
|
||||
import li.cil.sedna.device.virtio.VirtIOBlockDevice;
|
||||
import li.cil.sedna.memory.PhysicalMemoryInputStream;
|
||||
import li.cil.sedna.memory.PhysicalMemoryOutputStream;
|
||||
import li.cil.sedna.riscv.R5Board;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.tileentity.ITickableTileEntity;
|
||||
@@ -23,23 +29,26 @@ import net.minecraftforge.fml.network.PacketDistributor;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
public final class ComputerTileEntity extends TileEntity implements ITickableTileEntity {
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
|
||||
private final Terminal terminal = new Terminal();
|
||||
private Chunk chunk;
|
||||
|
||||
@Serialized private final Terminal terminal = new Terminal();
|
||||
private VirtualMachine virtualMachine;
|
||||
private VirtualMachineRunner runner;
|
||||
private R5Board board;
|
||||
private PhysicalMemory rom;
|
||||
private PhysicalMemory ram;
|
||||
private UART16550A uart;
|
||||
private VirtIOBlockDevice hdd;
|
||||
|
||||
@Serialized private UUID firmwareBlobHandle;
|
||||
@Serialized private UUID ramBlobHandle;
|
||||
private BlobStorage.JobHandle blobStorageJobHandle;
|
||||
|
||||
private Chunk chunk;
|
||||
|
||||
public ComputerTileEntity() {
|
||||
super(OpenComputers.COMPUTER_TILE_ENTITY.get());
|
||||
@@ -54,7 +63,7 @@ public final class ComputerTileEntity extends TileEntity implements ITickableTil
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
stopVirtualMachine();
|
||||
disposeVirtualMachine();
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
@@ -71,6 +80,11 @@ public final class ComputerTileEntity extends TileEntity implements ITickableTil
|
||||
chunk = Objects.requireNonNull(getWorld()).getChunkAt(getPos());
|
||||
}
|
||||
|
||||
if (blobStorageJobHandle != null) {
|
||||
blobStorageJobHandle.await();
|
||||
blobStorageJobHandle = null;
|
||||
}
|
||||
|
||||
if (runner != null) {
|
||||
runner.tick();
|
||||
}
|
||||
@@ -79,13 +93,13 @@ public final class ComputerTileEntity extends TileEntity implements ITickableTil
|
||||
@Override
|
||||
public void remove() {
|
||||
super.remove();
|
||||
stopVirtualMachine();
|
||||
disposeVirtualMachine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChunkUnloaded() {
|
||||
super.onChunkUnloaded();
|
||||
stopVirtualMachine();
|
||||
disposeVirtualMachine();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -105,33 +119,104 @@ public final class ComputerTileEntity extends TileEntity implements ITickableTil
|
||||
@Override
|
||||
public void read(final CompoundNBT compound) {
|
||||
super.read(compound);
|
||||
|
||||
joinVirtualMachine();
|
||||
NBTSerialization.deserialize(compound.getCompound("terminal"), terminal);
|
||||
// TODO deserialize VM
|
||||
|
||||
if (blobStorageJobHandle != null) {
|
||||
blobStorageJobHandle.await();
|
||||
blobStorageJobHandle = null;
|
||||
}
|
||||
|
||||
NBTSerialization.deserialize(compound, this);
|
||||
|
||||
if (compound.contains("virtualMachine")) {
|
||||
virtualMachine = VirtualMachine.allocate();
|
||||
|
||||
if (virtualMachine != null) {
|
||||
NBTSerialization.deserialize(compound.getCompound("virtualMachine"), virtualMachine);
|
||||
|
||||
firmwareBlobHandle = BlobStorage.validateHandle(firmwareBlobHandle);
|
||||
ramBlobHandle = BlobStorage.validateHandle(ramBlobHandle);
|
||||
|
||||
blobStorageJobHandle = BlobStorage.JobHandle.combine(blobStorageJobHandle,
|
||||
BlobStorage.submitLoad(firmwareBlobHandle, new PhysicalMemoryOutputStream(virtualMachine.firmware)));
|
||||
blobStorageJobHandle = BlobStorage.JobHandle.combine(blobStorageJobHandle,
|
||||
BlobStorage.submitLoad(ramBlobHandle, new PhysicalMemoryOutputStream(virtualMachine.ram)));
|
||||
|
||||
if (compound.contains("runner")) {
|
||||
runner = new ConsoleRunner(virtualMachine);
|
||||
NBTSerialization.deserialize(compound.getCompound("runner"), runner);
|
||||
}
|
||||
} else {
|
||||
// TODO Let user know VM could not be created because of memory constraints.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(final CompoundNBT compound) {
|
||||
final CompoundNBT result = super.write(compound);
|
||||
|
||||
joinVirtualMachine();
|
||||
compound.put("terminal", NBTSerialization.serialize(terminal));
|
||||
// TODO serialize VM
|
||||
|
||||
if (blobStorageJobHandle != null) {
|
||||
blobStorageJobHandle.await();
|
||||
blobStorageJobHandle = null;
|
||||
}
|
||||
|
||||
if (virtualMachine != null) {
|
||||
compound.put("virtualMachine", NBTSerialization.serialize(virtualMachine));
|
||||
|
||||
firmwareBlobHandle = BlobStorage.validateHandle(firmwareBlobHandle);
|
||||
ramBlobHandle = BlobStorage.validateHandle(ramBlobHandle);
|
||||
|
||||
blobStorageJobHandle = BlobStorage.JobHandle.combine(blobStorageJobHandle,
|
||||
BlobStorage.submitSave(firmwareBlobHandle, new PhysicalMemoryInputStream(virtualMachine.firmware)));
|
||||
blobStorageJobHandle = BlobStorage.JobHandle.combine(blobStorageJobHandle,
|
||||
BlobStorage.submitSave(ramBlobHandle, new PhysicalMemoryInputStream(virtualMachine.ram)));
|
||||
|
||||
if (runner != null) {
|
||||
compound.put("runner", NBTSerialization.serialize(runner));
|
||||
}
|
||||
} else {
|
||||
compound.remove("virtualMachine");
|
||||
}
|
||||
|
||||
// Do this last so that blob handles have been updated.
|
||||
NBTSerialization.serialize(compound, this);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void startVirtualMachine() {
|
||||
if (runner == null) {
|
||||
try {
|
||||
createVirtualMachine();
|
||||
} catch (final Throwable e) {
|
||||
LOGGER.warn(e);
|
||||
}
|
||||
if (Objects.requireNonNull(getWorld()).isRemote()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (runner != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (virtualMachine == null) {
|
||||
virtualMachine = VirtualMachine.allocate();
|
||||
}
|
||||
|
||||
if (virtualMachine == null) {
|
||||
// TODO Let user know VM could not be created because of memory constraints.
|
||||
return;
|
||||
}
|
||||
|
||||
virtualMachine.board.reset();
|
||||
runner = new ConsoleRunner(virtualMachine);
|
||||
}
|
||||
|
||||
private void stopVirtualMachine() {
|
||||
private void disposeVirtualMachine() {
|
||||
joinVirtualMachine();
|
||||
runner = null;
|
||||
if (virtualMachine != null) {
|
||||
virtualMachine.dispose();
|
||||
virtualMachine = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void joinVirtualMachine() {
|
||||
@@ -145,32 +230,6 @@ public final class ComputerTileEntity extends TileEntity implements ITickableTil
|
||||
}
|
||||
}
|
||||
|
||||
private void createVirtualMachine() throws Throwable {
|
||||
board = new R5Board();
|
||||
rom = Memory.create(128 * 1024);
|
||||
ram = Memory.create(32 * 1024 * 1204);
|
||||
hdd = new VirtIOBlockDevice(board.getMemoryMap(),
|
||||
ByteBufferBlockDevice.createFromStream(Buildroot.getRootFilesystem(), true));
|
||||
uart = new UART16550A();
|
||||
|
||||
hdd.getInterrupt().set(0x1, board.getInterruptController());
|
||||
uart.getInterrupt().set(0x2, board.getInterruptController());
|
||||
|
||||
board.addDevice(uart);
|
||||
board.addDevice(hdd);
|
||||
board.addDevice(0x80000000, rom);
|
||||
board.addDevice(0x80000000 + 0x400000, ram);
|
||||
|
||||
board.setBootargs("console=ttyS0 root=/dev/vda ro");
|
||||
|
||||
board.reset();
|
||||
|
||||
loadProgramFile(rom, Buildroot.getFirmware());
|
||||
loadProgramFile(ram, Buildroot.getLinuxImage());
|
||||
|
||||
runner = new ConsoleRunner(board);
|
||||
}
|
||||
|
||||
private static void loadProgramFile(final PhysicalMemory memory, final InputStream stream) throws Throwable {
|
||||
final BufferedInputStream bis = new BufferedInputStream(stream);
|
||||
for (int address = 0, value = bis.read(); value != -1; value = bis.read(), address++) {
|
||||
@@ -178,17 +237,82 @@ public final class ComputerTileEntity extends TileEntity implements ITickableTil
|
||||
}
|
||||
}
|
||||
|
||||
private static final class VirtualMachine {
|
||||
@Serialized R5Board board;
|
||||
@Serialized UART16550A uart;
|
||||
@Serialized VirtIOBlockDevice hdd;
|
||||
PhysicalMemory firmware;
|
||||
PhysicalMemory ram;
|
||||
|
||||
UUID ramHandle;
|
||||
|
||||
VirtualMachine(final UUID ramHandle, final BlockDevice blockDevice) {
|
||||
this.ramHandle = ramHandle;
|
||||
|
||||
board = new R5Board();
|
||||
|
||||
firmware = Memory.create(128 * 1024);
|
||||
ram = Memory.create(32 * 1024 * 1024);
|
||||
|
||||
board.addDevice(0x80000000, firmware);
|
||||
board.addDevice(0x80000000 + 0x400000, ram);
|
||||
|
||||
uart = new UART16550A();
|
||||
hdd = new VirtIOBlockDevice(board.getMemoryMap(), blockDevice);
|
||||
|
||||
hdd.getInterrupt().set(0x1, board.getInterruptController());
|
||||
uart.getInterrupt().set(0x2, board.getInterruptController());
|
||||
|
||||
board.addDevice(uart);
|
||||
board.addDevice(hdd);
|
||||
|
||||
board.setBootargs("console=ttyS0 root=/dev/vda ro");
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
ram = null;
|
||||
Allocator.freeMemory(ramHandle);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static VirtualMachine allocate() {
|
||||
final UUID ramHandle = Allocator.createHandle();
|
||||
if (Allocator.claimMemory(ramHandle, 32 * 1024 * 1024)) {
|
||||
try {
|
||||
return new VirtualMachine(ramHandle, ByteBufferBlockDevice.createFromStream(Buildroot.getRootFilesystem(), true));
|
||||
} catch (final IOException e) {
|
||||
LOGGER.error(e);
|
||||
Allocator.freeMemory(ramHandle);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Serialized
|
||||
private final class ConsoleRunner extends VirtualMachineRunner {
|
||||
// Thread-local buffers for lock-free read/writes in inner loop.
|
||||
private final ByteArrayFIFOQueue outputBuffer = new ByteArrayFIFOQueue(1024);
|
||||
private final ByteArrayFIFOQueue inputBuffer = new ByteArrayFIFOQueue(32);
|
||||
private boolean didLoadBinaries;
|
||||
|
||||
public ConsoleRunner(final R5Board board) {
|
||||
super(board);
|
||||
public ConsoleRunner(final VirtualMachine virtualMachine) {
|
||||
super(virtualMachine.board);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleBeforeRun() {
|
||||
if (!didLoadBinaries) {
|
||||
didLoadBinaries = true;
|
||||
try {
|
||||
loadProgramFile(virtualMachine.firmware, Buildroot.getFirmware());
|
||||
loadProgramFile(virtualMachine.ram, Buildroot.getLinuxImage());
|
||||
} catch (final Throwable e) {
|
||||
LOGGER.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
int value;
|
||||
while ((value = terminal.readInput()) != -1) {
|
||||
inputBuffer.enqueue((byte) value);
|
||||
@@ -197,12 +321,12 @@ public final class ComputerTileEntity extends TileEntity implements ITickableTil
|
||||
|
||||
@Override
|
||||
protected void step() {
|
||||
while (!inputBuffer.isEmpty() && uart.canPutByte()) {
|
||||
uart.putByte(inputBuffer.dequeueByte());
|
||||
while (!inputBuffer.isEmpty() && virtualMachine.uart.canPutByte()) {
|
||||
virtualMachine.uart.putByte(inputBuffer.dequeueByte());
|
||||
}
|
||||
|
||||
int value;
|
||||
while ((value = uart.read()) != -1) {
|
||||
while ((value = virtualMachine.uart.read()) != -1) {
|
||||
outputBuffer.enqueue((byte) value);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user