Started implementing a computer block as use/test-case for serialization.
This commit is contained in:
5
src/main/java/li/cil/oc2/Constants.java
Normal file
5
src/main/java/li/cil/oc2/Constants.java
Normal file
@@ -0,0 +1,5 @@
|
||||
package li.cil.oc2;
|
||||
|
||||
public final class Constants {
|
||||
public static final String COMPUTER_BLOCK_NAME = "computer";
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
12
src/main/java/li/cil/oc2/client/ClientSetup.java
Normal file
12
src/main/java/li/cil/oc2/client/ClientSetup.java
Normal 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);
|
||||
}
|
||||
}
|
||||
41
src/main/java/li/cil/oc2/client/gui/ComputerScreen.java
Normal file
41
src/main/java/li/cil/oc2/client/gui/ComputerScreen.java
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
@ParametersAreNonnullByDefault
|
||||
@MethodsReturnNonnullByDefault
|
||||
package li.cil.oc2.client.gui;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
@@ -1,4 +1,7 @@
|
||||
@ParametersAreNonnullByDefault
|
||||
@MethodsReturnNonnullByDefault
|
||||
package li.cil.oc2.client.gui.terminal;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
@@ -1,4 +1,7 @@
|
||||
@ParametersAreNonnullByDefault
|
||||
@MethodsReturnNonnullByDefault
|
||||
package li.cil.oc2.client.render.font;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
9
src/main/java/li/cil/oc2/common/CommonSetup.java
Normal file
9
src/main/java/li/cil/oc2/common/CommonSetup.java
Normal 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) {
|
||||
|
||||
}
|
||||
}
|
||||
71
src/main/java/li/cil/oc2/common/block/ComputerBlock.java
Normal file
71
src/main/java/li/cil/oc2/common/block/ComputerBlock.java
Normal 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;
|
||||
}
|
||||
}
|
||||
7
src/main/java/li/cil/oc2/common/block/package-info.java
Normal file
7
src/main/java/li/cil/oc2/common/block/package-info.java
Normal file
@@ -0,0 +1,7 @@
|
||||
@ParametersAreNonnullByDefault
|
||||
@MethodsReturnNonnullByDefault
|
||||
package li.cil.oc2.common.block;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
@ParametersAreNonnullByDefault
|
||||
@MethodsReturnNonnullByDefault
|
||||
package li.cil.oc2.common.container;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
@ParametersAreNonnullByDefault
|
||||
@MethodsReturnNonnullByDefault
|
||||
package li.cil.oc2.common.item;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
4
src/main/java/li/cil/oc2/common/network/Network.java
Normal file
4
src/main/java/li/cil/oc2/common/network/Network.java
Normal file
@@ -0,0 +1,4 @@
|
||||
package li.cil.oc2.common.network;
|
||||
|
||||
public final class Network {
|
||||
}
|
||||
180
src/main/java/li/cil/oc2/common/tile/ComputerTileEntity.java
Normal file
180
src/main/java/li/cil/oc2/common/tile/ComputerTileEntity.java
Normal 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
7
src/main/java/li/cil/oc2/common/tile/package-info.java
Normal file
7
src/main/java/li/cil/oc2/common/tile/package-info.java
Normal file
@@ -0,0 +1,7 @@
|
||||
@ParametersAreNonnullByDefault
|
||||
@MethodsReturnNonnullByDefault
|
||||
package li.cil.oc2.common.tile;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
@@ -1,4 +1,7 @@
|
||||
@ParametersAreNonnullByDefault
|
||||
@MethodsReturnNonnullByDefault
|
||||
package li.cil.oc2.common.vm;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
BIN
src/main/resources/assets/oc2/textures/gui/computer.png
Normal file
BIN
src/main/resources/assets/oc2/textures/gui/computer.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.6 KiB |
Reference in New Issue
Block a user