Updated computer tile entity, made persistence work again, prep for item based devices.

This commit is contained in:
Florian Nücke
2020-12-02 20:16:02 +01:00
parent 18ad8d4670
commit 89d92f3afb
8 changed files with 398 additions and 217 deletions

View File

@@ -59,6 +59,12 @@ public final class RPCAdapter implements Steppable {
.create();
}
public void reset() {
transmitBuffer.clear();
receiveBuffer = null;
synchronizedInvocation = null;
}
public void tick() {
if (synchronizedInvocation != null) {
final MethodInvocation methodInvocation = synchronizedInvocation;

View File

@@ -1,5 +1,6 @@
package li.cil.oc2.common.bus;
import li.cil.ceres.api.Serialized;
import li.cil.oc2.api.bus.DeviceBus;
import li.cil.oc2.api.bus.DeviceBusElement;
import li.cil.oc2.common.ServerScheduler;
@@ -7,7 +8,6 @@ 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.NBTTagIds;
import li.cil.oc2.common.util.WorldUtils;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.ListNBT;
@@ -15,13 +15,12 @@ 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.INBTSerializable;
import net.minecraftforge.common.util.LazyOptional;
import java.util.Objects;
import java.util.UUID;
public final class TileEntityDeviceBusElement implements INBTSerializable<CompoundNBT> {
public final class TileEntityDeviceBusElement {
private static final String DEVICE_IDS_NBT_TAG_NAME = "deviceIds";
private static final String DEVICE_ID_NBT_TAG_NAME = "deviceId";
@@ -30,8 +29,8 @@ public final class TileEntityDeviceBusElement implements INBTSerializable<Compou
private final TileEntity tileEntity;
private final DeviceBusElement busElement = Objects.requireNonNull(Capabilities.DEVICE_BUS_ELEMENT_CAPABILITY.getDefaultInstance());
private final UUID[] deviceIds = new UUID[NEIGHBOR_COUNT];
private final DeviceImpl[] devices = new DeviceImpl[NEIGHBOR_COUNT];
@Serialized private UUID[] deviceIds = new UUID[NEIGHBOR_COUNT];
public TileEntityDeviceBusElement(final TileEntity tileEntity) {
this.tileEntity = tileEntity;
@@ -101,31 +100,6 @@ public final class TileEntityDeviceBusElement implements INBTSerializable<Compou
busElement.scheduleScan();
}
@Override
public CompoundNBT serializeNBT() {
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);
}
final CompoundNBT compound = new CompoundNBT();
compound.put(DEVICE_IDS_NBT_TAG_NAME, deviceIdsNbt);
return compound;
}
@Override
public void deserializeNBT(final CompoundNBT compound) {
final ListNBT deviceIdsNbt = compound.getList(DEVICE_IDS_NBT_TAG_NAME, NBTTagIds.TAG_COMPOUND);
for (int i = 0; i < Math.min(deviceIdsNbt.size(), NEIGHBOR_COUNT); i++) {
final CompoundNBT deviceIdNbt = deviceIdsNbt.getCompound(i);
if (deviceIdNbt.hasUniqueId(DEVICE_ID_NBT_TAG_NAME)) {
deviceIds[i] = deviceIdNbt.getUniqueId(DEVICE_ID_NBT_TAG_NAME);
}
}
}
public CompoundNBT write(final CompoundNBT compound) {
final ListNBT deviceIdsNbt = new ListNBT();
for (int i = 0; i < NEIGHBOR_COUNT; i++) {

View File

@@ -18,8 +18,8 @@ public abstract class AbstractTileEntity extends TileEntity {
super(tileEntityType);
}
protected <T> void addCapability(final Capability<T> capability, final T value) {
capabilities.put(capability, LazyOptional.of(() -> value));
protected <T> void setCapabilityIfAbsent(final Capability<T> capability, final T value) {
capabilities.putIfAbsent(capability, LazyOptional.of(() -> value));
}
protected void initialize() {

View File

@@ -3,6 +3,7 @@ package li.cil.oc2.common.tile;
import li.cil.oc2.OpenComputers;
import li.cil.oc2.common.bus.TileEntityDeviceBusElement;
import li.cil.oc2.common.capabilities.Capabilities;
import li.cil.oc2.serialization.NBTSerialization;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.math.BlockPos;
@@ -15,7 +16,7 @@ public class BusCableTileEntity extends AbstractTileEntity {
super(OpenComputers.BUS_CABLE_TILE_ENTITY.get());
busElement = new TileEntityDeviceBusElement(this);
addCapability(Capabilities.DEVICE_BUS_ELEMENT_CAPABILITY, busElement.getBusElement());
setCapabilityIfAbsent(Capabilities.DEVICE_BUS_ELEMENT_CAPABILITY, busElement.getBusElement());
}
public void handleNeighborChanged(final BlockPos pos) {
@@ -37,13 +38,13 @@ public class BusCableTileEntity extends AbstractTileEntity {
@Override
public CompoundNBT write(CompoundNBT compound) {
compound = super.write(compound);
compound.put(BUS_ELEMENT_NBT_TAG_NAME, busElement.serializeNBT());
compound.put(BUS_ELEMENT_NBT_TAG_NAME, NBTSerialization.serialize(busElement));
return compound;
}
@Override
public void read(final CompoundNBT compound) {
super.read(compound);
busElement.deserializeNBT(compound.getCompound(BUS_ELEMENT_NBT_TAG_NAME));
NBTSerialization.deserialize(compound.getCompound(BUS_ELEMENT_NBT_TAG_NAME), busElement);
}
}

View File

@@ -4,26 +4,32 @@ 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.ServerScheduler;
import li.cil.oc2.common.bus.TileEntityDeviceBusController;
import li.cil.oc2.common.bus.TileEntityDeviceBusElement;
import li.cil.oc2.common.capabilities.Capabilities;
import li.cil.oc2.common.network.Network;
import li.cil.oc2.common.network.TerminalBlockOutputMessage;
import li.cil.oc2.common.vm.Allocator;
import li.cil.oc2.common.tile.computer.VirtualMachine;
import li.cil.oc2.common.util.NBTTagIds;
import li.cil.oc2.common.util.NBTUtils;
import li.cil.oc2.common.vm.VirtualMachineRunner;
import li.cil.oc2.serialization.BlobStorage;
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.device.virtio.VirtIOFileSystemDevice;
import li.cil.sedna.fs.HostFileSystem;
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.nbt.ListNBT;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraftforge.fml.network.PacketDistributor;
import org.apache.logging.log4j.LogManager;
@@ -37,21 +43,56 @@ import java.nio.ByteBuffer;
import java.util.Objects;
import java.util.UUID;
public final class ComputerTileEntity extends TileEntity implements ITickableTileEntity {
public final class ComputerTileEntity extends AbstractTileEntity implements ITickableTileEntity {
private static final Logger LOGGER = LogManager.getLogger();
@Serialized private final Terminal terminal = new Terminal();
private VirtualMachine virtualMachine;
private VirtualMachineRunner runner;
private static final String DEVICES_NBT_TAG_NAME = "devices";
private static final String BUS_ELEMENT_NBT_TAG_NAME = "busElement";
private static final String BUS_STATE_NBT_TAG_NAME = "busState";
private static final String TERMINAL_NBT_TAG_NAME = "terminal";
private static final String VIRTUAL_MACHINE_NBT_TAG_NAME = "virtualMachine";
private static final String RUNNER_NBT_TAG_NAME = "runner";
private static final String RUN_STATE_NBT_TAG_NAME = "runState";
@Serialized private UUID firmwareBlobHandle;
@Serialized private UUID ramBlobHandle;
private BlobStorage.JobHandle blobStorageJobHandle;
private static final int DEVICE_LOAD_RETRY_INTERVAL = 10 * 20; // In ticks.
private enum RunState {
STOPPED,
LOADING_DEVICES,
RUNNING,
}
private Chunk chunk;
private final TileEntityDeviceBusController busController;
private TileEntityDeviceBusController.State busState;
private RunState runState;
private int loadDevicesDelay;
@Nullable private BlobStorage.JobHandle ramJobHandle;
// Serialized data
private final TileEntityDeviceBusElement busElement;
private final Terminal terminal;
private final VirtualMachine virtualMachine;
private ConsoleRunner runner;
private PhysicalMemory ram;
private UUID ramBlobHandle;
private VirtIOBlockDevice hdd;
public ComputerTileEntity() {
super(OpenComputers.COMPUTER_TILE_ENTITY.get());
busElement = new TileEntityDeviceBusElement(this);
busController = new TileEntityDeviceBusController(this, this::joinVirtualMachine);
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_CONTROLLER_CAPABILITY, busController);
}
public Terminal getTerminal() {
@@ -59,72 +100,149 @@ public final class ComputerTileEntity extends TileEntity implements ITickableTil
}
public void start() {
startVirtualMachine();
if (runState == RunState.RUNNING) {
return;
}
runState = RunState.LOADING_DEVICES;
loadDevicesDelay = 0;
}
public void stop() {
disposeVirtualMachine();
if (runState == RunState.STOPPED) {
return;
}
if (runState == RunState.LOADING_DEVICES) {
runState = RunState.STOPPED;
return;
}
stopRunnerAndUnloadDevices();
}
public boolean isRunning() {
return runner != null;
}
@Override
public void onLoad() {
terminal.setDisplayOnly(Objects.requireNonNull(getWorld()).isRemote());
}
@Override
public void tick() {
final World world = getWorld();
if (world == null || world.isRemote()) {
return;
}
if (chunk == null) {
chunk = Objects.requireNonNull(getWorld()).getChunkAt(getPos());
}
if (blobStorageJobHandle != null) {
blobStorageJobHandle.await();
blobStorageJobHandle = null;
}
if (virtualMachine != null && !virtualMachine.board.isRunning()) {
disposeVirtualMachine();
busState = busController.scan();
if (busState != TileEntityDeviceBusController.State.READY) {
return;
}
if (runner != null) {
runner.tick();
chunk.markDirty();
switch (runState) {
case STOPPED:
break;
case LOADING_DEVICES:
if (loadDevicesDelay > 0) {
loadDevicesDelay--;
break;
}
if (!loadDevices()) {
loadDevicesDelay = DEVICE_LOAD_RETRY_INTERVAL;
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.
if (runner == null) {
virtualMachine.board.reset();
virtualMachine.board.initialize();
virtualMachine.board.setRunning(true);
runner = new ConsoleRunner(virtualMachine);
}
runState = RunState.RUNNING;
// Only start running next tick. This gives loaded devices one tick to do async
// initialization. This is used by RAM to restore data from disk, for example.
break;
case RUNNING:
if (!virtualMachine.board.isRunning()) {
stopRunnerAndUnloadDevices();
break;
}
runner.tick();
chunk.markDirty();
break;
}
}
@Override
public void remove() {
super.remove();
disposeVirtualMachine();
protected void initializeClient() {
super.initializeClient();
terminal.setDisplayOnly(true);
}
@Override
public void onChunkUnloaded() {
super.onChunkUnloaded();
disposeVirtualMachine();
protected void initializeServer() {
super.initializeServer();
ServerScheduler.schedule(() -> chunk = Objects.requireNonNull(getWorld()).getChunkAt(getPos()));
}
@Override
protected void disposeServer() {
super.disposeServer();
busElement.dispose();
stopRunnerAndUnloadDevices();
}
@Override
public CompoundNBT getUpdateTag() {
final CompoundNBT result = super.getUpdateTag();
result.put("terminal", NBTSerialization.serialize(terminal));
result.put(TERMINAL_NBT_TAG_NAME, NBTSerialization.serialize(terminal));
result.putInt(BUS_STATE_NBT_TAG_NAME, busState.ordinal());
return result;
}
@Override
public void handleUpdateTag(final CompoundNBT tag) {
super.handleUpdateTag(tag);
NBTSerialization.deserialize(tag.getCompound("terminal"), terminal);
NBTSerialization.deserialize(tag.getCompound(TERMINAL_NBT_TAG_NAME), terminal);
busState = TileEntityDeviceBusController.State.values()[tag.getInt(BUS_STATE_NBT_TAG_NAME)];
}
@Override
public CompoundNBT write(CompoundNBT compound) {
compound = super.write(compound);
joinVirtualMachine();
if (ramJobHandle != null) {
ramJobHandle.await();
ramJobHandle = null;
}
compound.put(TERMINAL_NBT_TAG_NAME, NBTSerialization.serialize(terminal));
compound.put(BUS_ELEMENT_NBT_TAG_NAME, NBTSerialization.serialize(busElement));
compound.put(VIRTUAL_MACHINE_NBT_TAG_NAME, NBTSerialization.serialize(virtualMachine));
if (runner != null) {
compound.put(RUNNER_NBT_TAG_NAME, NBTSerialization.serialize(runner));
} else {
NBTUtils.putEnum(compound, RUN_STATE_NBT_TAG_NAME, runState);
}
final ListNBT devicesNbt = new ListNBT();
writeDevices(devicesNbt);
compound.put(DEVICES_NBT_TAG_NAME, devicesNbt);
return compound;
}
@Override
@@ -132,107 +250,144 @@ public final class ComputerTileEntity extends TileEntity implements ITickableTil
super.read(compound);
joinVirtualMachine();
if (blobStorageJobHandle != null) {
blobStorageJobHandle.await();
blobStorageJobHandle = null;
if (ramJobHandle != null) {
ramJobHandle.await();
ramJobHandle = null;
}
NBTSerialization.deserialize(compound, this);
NBTSerialization.deserialize(compound.getCompound(TERMINAL_NBT_TAG_NAME), terminal);
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();
if (blobStorageJobHandle != null) {
blobStorageJobHandle.await();
blobStorageJobHandle = null;
if (compound.contains(BUS_ELEMENT_NBT_TAG_NAME, NBTTagIds.TAG_COMPOUND)) {
NBTSerialization.deserialize(compound.getCompound(BUS_ELEMENT_NBT_TAG_NAME), busElement);
}
if (virtualMachine != null) {
compound.put("virtualMachine", NBTSerialization.serialize(virtualMachine));
if (compound.contains(VIRTUAL_MACHINE_NBT_TAG_NAME, NBTTagIds.TAG_COMPOUND)) {
NBTSerialization.deserialize(compound.getCompound(VIRTUAL_MACHINE_NBT_TAG_NAME), 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));
}
if (compound.contains(RUNNER_NBT_TAG_NAME, NBTTagIds.TAG_COMPOUND)) {
runner = new ConsoleRunner(virtualMachine);
NBTSerialization.deserialize(compound.getCompound(RUNNER_NBT_TAG_NAME), runner);
runState = RunState.LOADING_DEVICES;
} else {
compound.remove("virtualMachine");
runState = NBTUtils.getEnum(compound, RUN_STATE_NBT_TAG_NAME, RunState.class);
if (runState == RunState.RUNNING) {
runState = RunState.LOADING_DEVICES;
}
}
// Do this last so that blob handles have been updated.
NBTSerialization.serialize(compound, this);
// TODO Read item NBTs, generate item based devices.
return result;
if (compound.contains(DEVICES_NBT_TAG_NAME, NBTTagIds.TAG_LIST)) {
readDevices(compound.getList(DEVICES_NBT_TAG_NAME, NBTTagIds.TAG_COMPOUND));
}
}
private void startVirtualMachine() {
if (Objects.requireNonNull(getWorld()).isRemote()) {
return;
private void writeDevices(final ListNBT list) {
// TODO Apply to devices generated from items.
if (ram != null) {
final CompoundNBT ramNbt = new CompoundNBT();
ramBlobHandle = BlobStorage.validateHandle(ramBlobHandle);
ramNbt.putUniqueId("ram", ramBlobHandle);
list.add(ramNbt);
ramJobHandle = BlobStorage.JobHandle.combine(ramJobHandle,
BlobStorage.submitSave(ramBlobHandle, new PhysicalMemoryInputStream(ram)));
} else {
list.add(new CompoundNBT());
}
if (runner != null) {
return;
if (hdd != null) {
final CompoundNBT hddNbt = new CompoundNBT();
hddNbt.put("hdd", NBTSerialization.serialize(hdd));
list.add(hddNbt);
} else {
list.add(new CompoundNBT());
}
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();
virtualMachine.board.initialize();
virtualMachine.board.setRunning(true);
runner = new ConsoleRunner(virtualMachine);
}
private void disposeVirtualMachine() {
private void readDevices(final ListNBT list) {
// TODO Apply to devices generated from items.
final CompoundNBT ramNbt = list.getCompound(0);
if (ramNbt.hasUniqueId("ram")) {
ramBlobHandle = ramNbt.getUniqueId("ram");
}
final CompoundNBT hddNbt = list.getCompound(1);
if (hddNbt.contains("hdd", NBTTagIds.TAG_COMPOUND)) {
if (hdd == null) {
hdd = new VirtIOBlockDevice(virtualMachine.board.getMemoryMap());
hdd.getInterrupt().set(0x3, virtualMachine.board.getInterruptController());
virtualMachine.board.addDevice(hdd);
}
NBTSerialization.deserialize(hddNbt.getCompound("hdd"), hdd);
}
}
private boolean loadDevices() {
// TODO Load devices generated from items.
try {
if (ram == null) {
final int RAM_SIZE = 24 * 1024 * 1024;
ram = Memory.create(RAM_SIZE);
virtualMachine.board.addDevice(0x80000000L, ram);
}
if (ramBlobHandle != null) {
ramJobHandle = BlobStorage.JobHandle.combine(ramJobHandle,
BlobStorage.submitLoad(ramBlobHandle, new PhysicalMemoryOutputStream(ram)));
}
if (hdd == null) {
hdd = new VirtIOBlockDevice(virtualMachine.board.getMemoryMap());
hdd.getInterrupt().set(0x3, virtualMachine.board.getInterruptController());
virtualMachine.board.addDevice(hdd);
}
final ByteBufferBlockDevice blockDevice = ByteBufferBlockDevice.createFromStream(Buildroot.getRootFilesystem(), true);
hdd.setBlockDevice(blockDevice);
final VirtIOFileSystemDevice vfs = new VirtIOFileSystemDevice(virtualMachine.board.getMemoryMap(), "scripts", new HostFileSystem());
vfs.getInterrupt().set(0x4, virtualMachine.board.getInterruptController());
virtualMachine.board.addDevice(vfs);
} catch (final IOException e) {
LOGGER.error(e);
}
return true;
}
private void unloadDevices() {
// TODO Unload devices generated from items.
if (ram != null) {
virtualMachine.board.removeDevice(ram);
ram = null;
BlobStorage.freeHandle(ramBlobHandle);
}
if (hdd != null) {
try {
hdd.close();
} catch (final IOException e) {
LOGGER.error(e);
}
hdd = null;
}
}
private void stopRunnerAndUnloadDevices() {
joinVirtualMachine();
runner = null;
if (virtualMachine != null) {
virtualMachine.dispose();
virtualMachine = null;
}
runState = RunState.STOPPED;
BlobStorage.freeHandle(firmwareBlobHandle);
BlobStorage.freeHandle(ramBlobHandle);
unloadDevices();
virtualMachine.reset();
}
private void joinVirtualMachine() {
@@ -240,90 +395,56 @@ public final class ComputerTileEntity extends TileEntity implements ITickableTil
try {
runner.join();
} catch (final Throwable e) {
LOGGER.warn(e);
LOGGER.error(e);
runner = null;
}
}
}
private static void loadProgramFile(final PhysicalMemory memory, final InputStream stream) throws Throwable {
loadProgramFile(memory, stream, 0);
}
private static void loadProgramFile(final PhysicalMemory memory, final InputStream stream, final int offset) throws Throwable {
final BufferedInputStream bis = new BufferedInputStream(stream);
for (int address = 0, value = bis.read(); value != -1; value = bis.read(), address++) {
memory.store(address, (byte) value, Sizes.SIZE_8_LOG2);
memory.store(offset + address, (byte) value, Sizes.SIZE_8_LOG2);
}
}
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;
@Serialized private boolean didInitialization;
public ConsoleRunner(final VirtualMachine virtualMachine) {
super(virtualMachine.board);
}
@Override
public void tick() {
// TODO Tick devices that need it synchronously.
if (ramJobHandle != null) {
ramJobHandle.await();
ramJobHandle = null;
}
virtualMachine.rpcAdapter.tick();
super.tick();
}
@Override
protected void handleBeforeRun() {
if (!didLoadBinaries) {
didLoadBinaries = true;
if (!didInitialization) {
didInitialization = true;
try {
loadProgramFile(virtualMachine.firmware, Buildroot.getFirmware());
loadProgramFile(virtualMachine.ram, Buildroot.getLinuxImage());
// TODO Initialize devices that need it asynchronously.
loadProgramFile(ram, Buildroot.getFirmware());
loadProgramFile(ram, Buildroot.getLinuxImage(), 0x200000);
} catch (final Throwable e) {
LOGGER.error(e);
}
@@ -346,6 +467,8 @@ public final class ComputerTileEntity extends TileEntity implements ITickableTil
while ((value = virtualMachine.uart.read()) != -1) {
outputBuffer.enqueue((byte) value);
}
virtualMachine.rpcAdapter.step(cyclesPerStep);
}
@Override

View File

@@ -0,0 +1,43 @@
package li.cil.oc2.common.tile.computer;
import li.cil.ceres.api.Serialized;
import li.cil.oc2.api.bus.DeviceBusController;
import li.cil.oc2.common.bus.RPCAdapter;
import li.cil.sedna.device.rtc.GoldfishRTC;
import li.cil.sedna.device.rtc.SystemTimeRealTimeCounter;
import li.cil.sedna.device.serial.UART16550A;
import li.cil.sedna.device.virtio.VirtIOConsoleDevice;
import li.cil.sedna.riscv.R5Board;
public final class VirtualMachine {
@Serialized public R5Board board;
@Serialized public UART16550A uart;
@Serialized public VirtIOConsoleDevice deviceBusSerialDevice;
@Serialized public RPCAdapter rpcAdapter;
public VirtualMachine(final DeviceBusController busController) {
board = new R5Board();
uart = new UART16550A();
uart.getInterrupt().set(0x1, board.getInterruptController());
board.addDevice(uart);
final GoldfishRTC rtc = new GoldfishRTC(SystemTimeRealTimeCounter.get());
rtc.getInterrupt().set(0x2, board.getInterruptController());
board.addDevice(rtc);
deviceBusSerialDevice = new VirtIOConsoleDevice(board.getMemoryMap());
deviceBusSerialDevice.getInterrupt().set(0x3, board.getInterruptController());
board.addDevice(deviceBusSerialDevice);
rpcAdapter = new RPCAdapter(busController, deviceBusSerialDevice);
board.setBootArguments("root=/dev/vda ro");
board.setStandardOutputDevice(uart);
}
public void reset() {
board.reset();
rpcAdapter.reset();
}
}

View File

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

View File

@@ -0,0 +1,27 @@
package li.cil.oc2.common.util;
import net.minecraft.nbt.CompoundNBT;
import javax.annotation.Nullable;
public final class NBTUtils {
public static <T extends Enum<T>> void putEnum(final CompoundNBT compound, final String key, @Nullable final Enum<T> value) {
if (value != null) {
compound.putInt(key, value.ordinal());
}
}
@Nullable
public static <T extends Enum<T>> T getEnum(final CompoundNBT compound, final String key, final Class<T> enumType) {
if (!compound.contains(key, NBTTagIds.TAG_INT)) {
return null;
}
final int ordinal = compound.getInt(key);
try {
return enumType.getEnumConstants()[ordinal];
} catch (final IndexOutOfBoundsException ignored) {
return null;
}
}
}