Started implementing a computer block as use/test-case for serialization.

This commit is contained in:
Florian Nücke
2020-09-29 13:01:32 +02:00
parent 8a7aabfdad
commit cd1d06842d
19 changed files with 428 additions and 2 deletions

View File

@@ -0,0 +1,5 @@
package li.cil.oc2;
public final class Constants {
public static final String COMPUTER_BLOCK_NAME = "computer";
}

View File

@@ -1,8 +1,22 @@
package li.cil.oc2;
import li.cil.oc2.api.API;
import li.cil.oc2.client.ClientSetup;
import li.cil.oc2.common.CommonSetup;
import li.cil.oc2.common.block.ComputerBlock;
import li.cil.oc2.common.container.ComputerContainer;
import li.cil.oc2.common.item.RISCVTesterItem;
import li.cil.oc2.common.tile.ComputerTileEntity;
import net.minecraft.block.Block;
import net.minecraft.inventory.container.ContainerType;
import net.minecraft.item.BlockItem;
import net.minecraft.item.Item;
import net.minecraft.item.ItemGroup;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.math.BlockPos;
import net.minecraftforge.common.extensions.IForgeContainerType;
import net.minecraftforge.fml.RegistryObject;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
@@ -11,10 +25,37 @@ import net.minecraftforge.registries.ForgeRegistries;
@Mod(API.MOD_ID)
public final class OpenComputers {
public static final ItemGroup ITEM_GROUP = new ItemGroup(API.MOD_ID) {
@Override
public ItemStack createIcon() {
return new ItemStack(COMPUTER_ITEM.get());
}
};
public static final DeferredRegister<Block> BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, API.MOD_ID);
public static final RegistryObject<Block> COMPUTER_BLOCK = BLOCKS.register(Constants.COMPUTER_BLOCK_NAME, ComputerBlock::new);
public static final DeferredRegister<Item> ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, API.MOD_ID);
public static final RegistryObject<Item> RISCV_TESTER = ITEMS.register("riscv_tester", RISCVTesterItem::new);
public static final RegistryObject<Item> COMPUTER_ITEM = ITEMS.register(Constants.COMPUTER_BLOCK_NAME, () -> new BlockItem(COMPUTER_BLOCK.get(), new Item.Properties().group(ITEM_GROUP)));
public static final DeferredRegister<TileEntityType<?>> TILES = DeferredRegister.create(ForgeRegistries.TILE_ENTITIES, API.MOD_ID);
public static final RegistryObject<TileEntityType<ComputerTileEntity>> COMPUTER_TILE_ENTITY = TILES.register(Constants.COMPUTER_BLOCK_NAME, () -> TileEntityType.Builder.create(ComputerTileEntity::new, COMPUTER_BLOCK.get()).build(null));
public static final DeferredRegister<ContainerType<?>> CONTAINERS = DeferredRegister.create(ForgeRegistries.CONTAINERS, API.MOD_ID);
public static final RegistryObject<ContainerType<ComputerContainer>> COMPUTER_CONTAINER = CONTAINERS.register(Constants.COMPUTER_BLOCK_NAME, () -> IForgeContainerType.create((id, inventory, data) -> {
final BlockPos pos = data.readBlockPos();
final TileEntity tileEntity = inventory.player.getEntityWorld().getTileEntity(pos);
return new ComputerContainer(id, tileEntity);
}));
public OpenComputers() {
ITEMS.register(FMLJavaModLoadingContext.get().getModEventBus());
BLOCKS.register(FMLJavaModLoadingContext.get().getModEventBus());
TILES.register(FMLJavaModLoadingContext.get().getModEventBus());
CONTAINERS.register(FMLJavaModLoadingContext.get().getModEventBus());
FMLJavaModLoadingContext.get().getModEventBus().addListener(CommonSetup::run);
FMLJavaModLoadingContext.get().getModEventBus().addListener(ClientSetup::run);
}
}

View File

@@ -0,0 +1,12 @@
package li.cil.oc2.client;
import li.cil.oc2.OpenComputers;
import li.cil.oc2.client.gui.ComputerScreen;
import net.minecraft.client.gui.ScreenManager;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
public final class ClientSetup {
public static void run(final FMLClientSetupEvent event) {
ScreenManager.registerFactory(OpenComputers.COMPUTER_CONTAINER.get(), ComputerScreen::new);
}
}

View File

@@ -0,0 +1,41 @@
package li.cil.oc2.client.gui;
import com.mojang.blaze3d.systems.RenderSystem;
import li.cil.oc2.api.API;
import li.cil.oc2.common.container.ComputerContainer;
import net.minecraft.client.gui.screen.inventory.ContainerScreen;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.text.ITextComponent;
import java.util.Objects;
public final class ComputerScreen extends ContainerScreen<ComputerContainer> {
private static final ResourceLocation BACKGROUND = new ResourceLocation(API.MOD_ID, "textures/gui/container/computer.png");
public ComputerScreen(final ComputerContainer container, final PlayerInventory inventory, final ITextComponent title) {
super(container, inventory, title);
xSize = 196;
ySize = 197;
}
@Override
public void render(final int mouseX, final int mouseY, final float partialTicks) {
renderBackground();
super.render(mouseX, mouseY, partialTicks);
RenderSystem.disableBlend();
// TODO Render terminal text.
renderHoveredToolTip(mouseX, mouseY);
}
@Override
protected void drawGuiContainerBackgroundLayer(final float partialTicks, final int mouseX, final int mouseY) {
RenderSystem.color4f(1f, 1f, 1f, 1f);
Objects.requireNonNull(minecraft).getTextureManager().bindTexture(BACKGROUND);
final int x = (width - xSize) / 2;
final int y = (height - ySize) / 2;
blit(x, y, 0, 0, xSize, ySize);
}
}

View File

@@ -1,4 +1,7 @@
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
package li.cil.oc2.client.gui;
import mcp.MethodsReturnNonnullByDefault;
import javax.annotation.ParametersAreNonnullByDefault;

View File

@@ -1,4 +1,7 @@
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
package li.cil.oc2.client.gui.terminal;
import mcp.MethodsReturnNonnullByDefault;
import javax.annotation.ParametersAreNonnullByDefault;

View File

@@ -1,4 +1,7 @@
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
package li.cil.oc2.client.render.font;
import mcp.MethodsReturnNonnullByDefault;
import javax.annotation.ParametersAreNonnullByDefault;

View File

@@ -0,0 +1,9 @@
package li.cil.oc2.common;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
public final class CommonSetup {
public static void run(final FMLCommonSetupEvent event) {
}
}

View File

@@ -0,0 +1,71 @@
package li.cil.oc2.common.block;
import li.cil.oc2.OpenComputers;
import li.cil.oc2.common.container.ComputerContainer;
import li.cil.oc2.common.tile.ComputerTileEntity;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.SoundType;
import net.minecraft.block.material.Material;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.inventory.container.Container;
import net.minecraft.inventory.container.INamedContainerProvider;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TranslationTextComponent;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
import net.minecraftforge.fml.network.NetworkHooks;
import javax.annotation.Nullable;
public final class ComputerBlock extends Block {
public ComputerBlock() {
super(Properties.create(Material.IRON).sound(SoundType.METAL));
}
@Override
public boolean hasTileEntity(final BlockState state) {
return true;
}
@Nullable
@Override
public TileEntity createTileEntity(final BlockState state, final IBlockReader world) {
return OpenComputers.COMPUTER_TILE_ENTITY.get().create();
}
@SuppressWarnings("deprecation")
@Override
public ActionResultType onBlockActivated(final BlockState state, final World world, final BlockPos pos, final PlayerEntity player, final Hand hand, final BlockRayTraceResult hit) {
if (!world.isRemote()) {
if (!(player instanceof ServerPlayerEntity)) {
throw new IllegalArgumentException();
}
final TileEntity tileEntity = world.getTileEntity(pos);
if (!(tileEntity instanceof ComputerTileEntity)) {
throw new IllegalStateException();
}
NetworkHooks.openGui((ServerPlayerEntity) player, new INamedContainerProvider() {
@Override
public ITextComponent getDisplayName() {
return new TranslationTextComponent("blah");
}
@Override
public Container createMenu(final int id, final PlayerInventory inventory, final PlayerEntity player) {
return new ComputerContainer(id, tileEntity);
}
}, tileEntity.getPos());
}
return ActionResultType.SUCCESS;
}
}

View File

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

View File

@@ -0,0 +1,27 @@
package li.cil.oc2.common.container;
import li.cil.oc2.OpenComputers;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.inventory.container.Container;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.IWorldPosCallable;
import net.minecraft.world.World;
import javax.annotation.Nullable;
public final class ComputerContainer extends Container {
private final TileEntity tileEntity;
public ComputerContainer(final int id, @Nullable final TileEntity tileEntity) {
super(OpenComputers.COMPUTER_CONTAINER.get(), id);
this.tileEntity = tileEntity;
}
@Override
public boolean canInteractWith(final PlayerEntity player) {
if (tileEntity == null) return false;
final World world = tileEntity.getWorld();
if (world == null) return false;
return isWithinUsableDistance(IWorldPosCallable.of(world, tileEntity.getPos()), player, OpenComputers.COMPUTER_BLOCK.get());
}
}

View File

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

View File

@@ -1,11 +1,11 @@
package li.cil.oc2.common.item;
import li.cil.oc2.OpenComputers;
import li.cil.oc2.client.gui.RISCVTestScreen;
import net.minecraft.client.Minecraft;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemGroup;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ItemUseContext;
import net.minecraft.util.ActionResult;
@@ -15,7 +15,7 @@ import net.minecraft.world.World;
public final class RISCVTesterItem extends Item {
public RISCVTesterItem() {
super(new Properties().group(ItemGroup.MISC).maxStackSize(1));
super(new Properties().group(OpenComputers.ITEM_GROUP).maxStackSize(1));
}
@Override

View File

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

View File

@@ -0,0 +1,4 @@
package li.cil.oc2.common.network;
public final class Network {
}

View File

@@ -0,0 +1,180 @@
package li.cil.oc2.common.tile;
import it.unimi.dsi.fastutil.bytes.ByteArrayFIFOQueue;
import li.cil.oc2.OpenComputers;
import li.cil.oc2.client.gui.terminal.Terminal;
import li.cil.oc2.common.vm.VirtualMachineRunner;
import li.cil.sedna.api.Sizes;
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.riscv.R5Board;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntity;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.BufferedInputStream;
import java.io.InputStream;
public final class ComputerTileEntity extends TileEntity implements ITickableTileEntity {
private static final Logger LOGGER = LogManager.getLogger();
private final Terminal terminal = new Terminal();
private VirtualMachineRunner runner;
private R5Board board;
private PhysicalMemory rom;
private PhysicalMemory ram;
private UART16550A uart;
private VirtIOBlockDevice hdd;
public ComputerTileEntity() {
super(OpenComputers.COMPUTER_TILE_ENTITY.get());
}
public void start() {
startVirtualMachine();
}
public void stop() {
stopVirtualMachine();
}
@Override
public void tick() {
if (world == null || world.isRemote()) {
return;
}
if (runner != null) {
runner.tick();
}
}
@Override
public void remove() {
super.remove();
stopVirtualMachine();
}
@Override
public void onChunkUnloaded() {
stopVirtualMachine();
}
@Override
public void read(final CompoundNBT compound) {
super.read(compound);
joinVirtualMachine();
// TODO deserialize VM
}
@Override
public CompoundNBT write(final CompoundNBT compound) {
final CompoundNBT result = super.write(compound);
joinVirtualMachine();
// todo serialize VM
return result;
}
private void startVirtualMachine() {
if (runner == null) {
try {
createVirtualMachine();
} catch (final Throwable e) {
LOGGER.warn(e);
}
}
}
private void stopVirtualMachine() {
joinVirtualMachine();
runner = null;
}
private void joinVirtualMachine() {
if (runner != null) {
try {
runner.join();
} catch (final Throwable e) {
LOGGER.warn(e);
runner = null;
}
}
}
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++) {
memory.store(address, (byte) value, Sizes.SIZE_8_LOG2);
}
}
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);
public ConsoleRunner(final R5Board board) {
super(board);
}
@Override
protected void handleBeforeRun() {
int value;
while ((value = terminal.readInput()) != -1) {
inputBuffer.enqueue((byte) value);
}
}
@Override
protected void step() {
while (!inputBuffer.isEmpty() && uart.canPutByte()) {
uart.putByte(inputBuffer.dequeueByte());
}
int value;
while ((value = uart.read()) != -1) {
outputBuffer.enqueue((byte) value);
}
}
@Override
protected void handleAfterRun() {
while (!outputBuffer.isEmpty()) {
terminal.putOutput(outputBuffer.dequeueByte());
}
}
}
}

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB