diff --git a/src/main/java/li/cil/oc2/client/ClientSetup.java b/src/main/java/li/cil/oc2/client/ClientSetup.java index 661f171f..5f10c33c 100644 --- a/src/main/java/li/cil/oc2/client/ClientSetup.java +++ b/src/main/java/li/cil/oc2/client/ClientSetup.java @@ -3,6 +3,7 @@ package li.cil.oc2.client; import li.cil.oc2.api.API; import li.cil.oc2.api.bus.device.DeviceType; import li.cil.oc2.client.gui.ComputerContainerScreen; +import li.cil.oc2.client.gui.RobotContainerScreen; import li.cil.oc2.client.item.CustomItemModelProperties; import li.cil.oc2.client.model.BusCableModelLoader; import li.cil.oc2.client.renderer.NetworkCableRenderer; @@ -32,6 +33,7 @@ public final class ClientSetup { CustomItemModelProperties.initialize(); ScreenManager.registerFactory(Containers.COMPUTER_CONTAINER.get(), ComputerContainerScreen::new); + ScreenManager.registerFactory(Containers.ROBOT_CONTAINER.get(), RobotContainerScreen::new); ClientRegistry.bindTileEntityRenderer(TileEntities.COMPUTER_TILE_ENTITY.get(), ComputerTileEntityRenderer::new); ClientRegistry.bindTileEntityRenderer(TileEntities.NETWORK_CONNECTOR_TILE_ENTITY.get(), NetworkConnectorTileEntityRenderer::new); diff --git a/src/main/java/li/cil/oc2/client/gui/TerminalScreen.java b/src/main/java/li/cil/oc2/client/gui/AbstractTerminalScreen.java similarity index 87% rename from src/main/java/li/cil/oc2/client/gui/TerminalScreen.java rename to src/main/java/li/cil/oc2/client/gui/AbstractTerminalScreen.java index 64613277..46f8efd4 100644 --- a/src/main/java/li/cil/oc2/client/gui/TerminalScreen.java +++ b/src/main/java/li/cil/oc2/client/gui/AbstractTerminalScreen.java @@ -7,13 +7,11 @@ import li.cil.oc2.client.gui.terminal.TerminalInput; import li.cil.oc2.client.gui.widget.Sprite; import li.cil.oc2.client.gui.widget.ToggleImageButton; import li.cil.oc2.common.Constants; -import li.cil.oc2.common.network.Network; -import li.cil.oc2.common.network.message.ComputerPowerMessage; -import li.cil.oc2.common.network.message.ComputerTerminalInputMessage; -import li.cil.oc2.common.tileentity.ComputerTileEntity; import li.cil.oc2.common.vm.Terminal; +import li.cil.oc2.common.vm.VirtualMachineState; import net.minecraft.client.entity.player.ClientPlayerEntity; import net.minecraft.client.gui.screen.Screen; +import net.minecraft.entity.player.PlayerEntity; import net.minecraft.util.ResourceLocation; import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.TranslationTextComponent; @@ -23,7 +21,7 @@ import java.nio.ByteBuffer; import static java.util.Objects.requireNonNull; -public final class TerminalScreen extends Screen { +public abstract class AbstractTerminalScreen extends Screen { private static final ResourceLocation BACKGROUND = new ResourceLocation(API.MOD_ID, "textures/gui/screen/terminal.png"); private static final ResourceLocation BACKGROUND_TERMINAL_FOCUSED = new ResourceLocation(API.MOD_ID, "textures/gui/screen/terminal_focused.png"); private static final int TEXTURE_SIZE = 512; @@ -50,7 +48,7 @@ public final class TerminalScreen extends Screen { /////////////////////////////////////////////////////////////////// - private final ComputerTileEntity tileEntity; + private final VirtualMachineState state; private final Terminal terminal; private final int windowWidth, windowHeight; private int windowLeft, windowTop; @@ -58,10 +56,10 @@ public final class TerminalScreen extends Screen { /////////////////////////////////////////////////////////////////// - public TerminalScreen(final ComputerTileEntity tileEntity, final ITextComponent title) { + public AbstractTerminalScreen(final VirtualMachineState state, final Terminal terminal, final ITextComponent title) { super(title); - this.tileEntity = tileEntity; - terminal = tileEntity.getTerminal(); + this.state = state; + this.terminal = terminal; windowWidth = SCREEN_WIDTH; windowHeight = SCREEN_HEIGHT; } @@ -84,13 +82,13 @@ public final class TerminalScreen extends Screen { super.render(matrixStack, mouseX, mouseY, partialTicks); - if (tileEntity.getState().isRunning()) { + if (state.isRunning()) { final MatrixStack stack = new MatrixStack(); stack.translate(windowLeft + TERMINAL_AREA_X, windowTop + TERMINAL_AREA_Y, this.itemRenderer.zLevel); stack.scale(TERMINAL_AREA_WIDTH / (float) terminal.getWidth(), TERMINAL_AREA_HEIGHT / (float) terminal.getHeight(), 1f); terminal.render(stack); } else { - final ITextComponent bootError = tileEntity.getState().getBootError(); + final ITextComponent bootError = state.getBootError(); if (bootError != null) { final int textWidth = font.getStringPropertyWidth(bootError); final int textOffsetX = (TERMINAL_AREA_WIDTH - textWidth) / 2; @@ -110,13 +108,13 @@ public final class TerminalScreen extends Screen { final ByteBuffer input = terminal.getInput(); if (input != null) { - Network.INSTANCE.sendToServer(new ComputerTerminalInputMessage(tileEntity, input)); + sendTerminalInputToServer(input); } assert minecraft != null; final ClientPlayerEntity player = minecraft.player; assert player != null; - if (!player.isAlive() || !tileEntity.getPos().withinDistance(player.getPositionVec(), 8)) { + if (!player.isAlive() || !canInteractWith(player)) { closeScreen(); } } @@ -166,6 +164,12 @@ public final class TerminalScreen extends Screen { /////////////////////////////////////////////////////////////////// + protected abstract void sendPowerStateToServer(boolean value); + + protected abstract void sendTerminalInputToServer(final ByteBuffer input); + + protected abstract boolean canInteractWith(PlayerEntity player); + protected void init() { super.init(); this.windowLeft = (this.width - this.windowWidth) / 2; @@ -185,13 +189,12 @@ public final class TerminalScreen extends Screen { @Override public void onPress() { super.onPress(); - final ComputerPowerMessage message = new ComputerPowerMessage(tileEntity, !tileEntity.getState().isRunning()); - Network.INSTANCE.sendToServer(message); + sendPowerStateToServer(!state.isRunning()); } @Override public boolean isToggled() { - return tileEntity.getState().isRunning(); + return state.isRunning(); } }); @@ -220,7 +223,7 @@ public final class TerminalScreen extends Screen { /////////////////////////////////////////////////////////////////// private boolean shouldCaptureInput() { - return isMouseOverTerminal && enableInputCapture && tileEntity.getState().isRunning(); + return isMouseOverTerminal && enableInputCapture && state.isRunning(); } private boolean isPointInRegion(final int x, final int y, final int width, final int height, double mouseX, double mouseY) { diff --git a/src/main/java/li/cil/oc2/client/gui/ComputerTerminalScreen.java b/src/main/java/li/cil/oc2/client/gui/ComputerTerminalScreen.java new file mode 100644 index 00000000..e6806cfd --- /dev/null +++ b/src/main/java/li/cil/oc2/client/gui/ComputerTerminalScreen.java @@ -0,0 +1,38 @@ +package li.cil.oc2.client.gui; + +import li.cil.oc2.common.network.Network; +import li.cil.oc2.common.network.message.ComputerPowerMessage; +import li.cil.oc2.common.network.message.ComputerTerminalInputMessage; +import li.cil.oc2.common.tileentity.ComputerTileEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.util.text.ITextComponent; + +import java.nio.ByteBuffer; + +public final class ComputerTerminalScreen extends AbstractTerminalScreen { + private final ComputerTileEntity computer; + + /////////////////////////////////////////////////////////////////// + + public ComputerTerminalScreen(final ComputerTileEntity computer, final ITextComponent title) { + super(computer.getState(), computer.getTerminal(), title); + this.computer = computer; + } + + /////////////////////////////////////////////////////////////////// + + @Override + protected void sendPowerStateToServer(final boolean value) { + Network.INSTANCE.sendToServer(new ComputerPowerMessage(computer, value)); + } + + @Override + protected void sendTerminalInputToServer(final ByteBuffer input) { + Network.INSTANCE.sendToServer(new ComputerTerminalInputMessage(computer, input)); + } + + @Override + protected boolean canInteractWith(final PlayerEntity player) { + return computer.getPos().withinDistance(player.getPositionVec(), 8); + } +} diff --git a/src/main/java/li/cil/oc2/client/gui/RobotContainerScreen.java b/src/main/java/li/cil/oc2/client/gui/RobotContainerScreen.java new file mode 100644 index 00000000..b9c49ed6 --- /dev/null +++ b/src/main/java/li/cil/oc2/client/gui/RobotContainerScreen.java @@ -0,0 +1,41 @@ +package li.cil.oc2.client.gui; + +import com.mojang.blaze3d.matrix.MatrixStack; +import com.mojang.blaze3d.systems.RenderSystem; +import li.cil.oc2.api.API; +import li.cil.oc2.common.container.RobotContainer; +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 static java.util.Objects.requireNonNull; + +public final class RobotContainerScreen extends ContainerScreen { + private static final ResourceLocation BACKGROUND = new ResourceLocation(API.MOD_ID, "textures/gui/container/computer.png"); + + /////////////////////////////////////////////////////////////////// + + public RobotContainerScreen(final RobotContainer container, final PlayerInventory inventory, final ITextComponent title) { + super(container, inventory, title); + xSize = 176; + ySize = 197; + playerInventoryTitleY = ySize - 94; + } + + @Override + public void render(final MatrixStack matrixStack, final int mouseX, final int mouseY, final float partialTicks) { + renderBackground(matrixStack); + super.render(matrixStack, mouseX, mouseY, partialTicks); + renderHoveredTooltip(matrixStack, mouseX, mouseY); + } + + /////////////////////////////////////////////////////////////////// + + @Override + protected void drawGuiContainerBackgroundLayer(final MatrixStack matrixStack, final float partialTicks, final int mouseX, final int mouseY) { + RenderSystem.color4f(1f, 1f, 1f, 1f); + requireNonNull(minecraft).getTextureManager().bindTexture(BACKGROUND); + blit(matrixStack, guiLeft, guiTop, 0, 0, xSize, ySize); + } +} diff --git a/src/main/java/li/cil/oc2/client/gui/RobotTerminalScreen.java b/src/main/java/li/cil/oc2/client/gui/RobotTerminalScreen.java new file mode 100644 index 00000000..869da5ce --- /dev/null +++ b/src/main/java/li/cil/oc2/client/gui/RobotTerminalScreen.java @@ -0,0 +1,38 @@ +package li.cil.oc2.client.gui; + +import li.cil.oc2.common.entity.RobotEntity; +import li.cil.oc2.common.network.Network; +import li.cil.oc2.common.network.message.RobotPowerMessage; +import li.cil.oc2.common.network.message.RobotTerminalInputMessage; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.util.text.ITextComponent; + +import java.nio.ByteBuffer; + +public final class RobotTerminalScreen extends AbstractTerminalScreen { + private final RobotEntity robot; + + /////////////////////////////////////////////////////////////////// + + public RobotTerminalScreen(final RobotEntity robot, final ITextComponent title) { + super(robot.getState(), robot.getTerminal(), title); + this.robot = robot; + } + + /////////////////////////////////////////////////////////////////// + + @Override + protected void sendPowerStateToServer(final boolean value) { + Network.INSTANCE.sendToServer(new RobotPowerMessage(robot, value)); + } + + @Override + protected void sendTerminalInputToServer(final ByteBuffer input) { + Network.INSTANCE.sendToServer(new RobotTerminalInputMessage(robot, input)); + } + + @Override + protected boolean canInteractWith(final PlayerEntity player) { + return robot.isEntityInRange(player, 8); + } +} diff --git a/src/main/java/li/cil/oc2/client/renderer/entity/RobotEntityRenderer.java b/src/main/java/li/cil/oc2/client/renderer/entity/RobotEntityRenderer.java index d7b76c86..50758beb 100644 --- a/src/main/java/li/cil/oc2/client/renderer/entity/RobotEntityRenderer.java +++ b/src/main/java/li/cil/oc2/client/renderer/entity/RobotEntityRenderer.java @@ -4,7 +4,6 @@ import com.mojang.blaze3d.matrix.MatrixStack; import com.mojang.blaze3d.vertex.IVertexBuilder; import li.cil.oc2.api.API; import li.cil.oc2.common.entity.RobotEntity; -import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.IRenderTypeBuffer; import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.entity.EntityRenderer; @@ -13,11 +12,8 @@ import net.minecraft.client.renderer.entity.model.EntityModel; import net.minecraft.client.renderer.model.ModelRenderer; import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.util.ResourceLocation; -import net.minecraft.util.math.EntityRayTraceResult; import net.minecraft.util.math.MathHelper; -import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.vector.Vector3f; -import net.minecraft.util.text.StringTextComponent; import net.minecraftforge.common.model.TransformationHelper; public final class RobotEntityRenderer extends EntityRenderer { @@ -57,10 +53,10 @@ public final class RobotEntityRenderer extends EntityRenderer { matrixStack.pop(); - final RayTraceResult hit = Minecraft.getInstance().objectMouseOver; - if (hit instanceof EntityRayTraceResult && entity == ((EntityRayTraceResult) hit).getEntity()) { - super.renderName(entity, new StringTextComponent("hi"), matrixStack, buffer, packedLight); - } +// final RayTraceResult hit = Minecraft.getInstance().objectMouseOver; +// if (hit instanceof EntityRayTraceResult && entity == ((EntityRayTraceResult) hit).getEntity()) { +// super.renderName(entity, new StringTextComponent("hi"), matrixStack, buffer, packedLight); +// } } /////////////////////////////////////////////////////////////////// diff --git a/src/main/java/li/cil/oc2/common/block/ComputerBlock.java b/src/main/java/li/cil/oc2/common/block/ComputerBlock.java index 52d03a80..2eb700c0 100644 --- a/src/main/java/li/cil/oc2/common/block/ComputerBlock.java +++ b/src/main/java/li/cil/oc2/common/block/ComputerBlock.java @@ -1,7 +1,7 @@ package li.cil.oc2.common.block; import li.cil.oc2.api.capabilities.RedstoneEmitter; -import li.cil.oc2.client.gui.TerminalScreen; +import li.cil.oc2.client.gui.ComputerTerminalScreen; import li.cil.oc2.common.capabilities.Capabilities; import li.cil.oc2.common.container.ComputerContainer; import li.cil.oc2.common.integration.Wrenches; @@ -9,7 +9,7 @@ import li.cil.oc2.common.item.Items; import li.cil.oc2.common.tileentity.ComputerTileEntity; import li.cil.oc2.common.tileentity.TileEntities; import li.cil.oc2.common.util.VoxelShapeUtils; -import li.cil.oc2.common.vm.CommonVirtualMachineItemStackHandlers; +import li.cil.oc2.common.vm.AbstractVirtualMachineItemStackHandlers; import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.block.HorizontalBlock; @@ -76,7 +76,7 @@ public final class ComputerBlock extends HorizontalBlock { @Override public void addInformation(final ItemStack stack, @Nullable final IBlockReader world, final List tooltip, final ITooltipFlag advanced) { super.addInformation(stack, world, tooltip, advanced); - CommonVirtualMachineItemStackHandlers.addInformation(stack, tooltip); + AbstractVirtualMachineItemStackHandlers.addInformation(stack, tooltip); } @Override @@ -178,8 +178,8 @@ public final class ComputerBlock extends HorizontalBlock { final TileEntity tileEntity = world.getTileEntity(pos); if (!world.isRemote() && tileEntity instanceof ComputerTileEntity) { final ComputerTileEntity computer = (ComputerTileEntity) tileEntity; - if (!computer.getItemHandlers().isEmpty()) { - computer.getItemHandlers().exportDeviceDataToItemStacks(); + if (!computer.getItemStackHandlers().isEmpty()) { + computer.getItemStackHandlers().exportDeviceDataToItemStacks(); if (player.isCreative()) { final ItemStack stack = new ItemStack(Items.COMPUTER_ITEM.get()); @@ -209,7 +209,7 @@ public final class ComputerBlock extends HorizontalBlock { @OnlyIn(Dist.CLIENT) private void openTerminalScreen(final ComputerTileEntity computer) { - Minecraft.getInstance().displayGuiScreen(new TerminalScreen(computer, getTranslatedName())); + Minecraft.getInstance().displayGuiScreen(new ComputerTerminalScreen(computer, getTranslatedName())); } private void openContainerScreen(final ComputerTileEntity tileEntity, final PlayerEntity player) { diff --git a/src/main/java/li/cil/oc2/common/container/ComputerContainer.java b/src/main/java/li/cil/oc2/common/container/ComputerContainer.java index 8452bf65..1d108e33 100644 --- a/src/main/java/li/cil/oc2/common/container/ComputerContainer.java +++ b/src/main/java/li/cil/oc2/common/container/ComputerContainer.java @@ -3,14 +3,13 @@ package li.cil.oc2.common.container; import li.cil.oc2.api.bus.device.DeviceTypes; import li.cil.oc2.common.block.Blocks; import li.cil.oc2.common.tileentity.ComputerTileEntity; -import li.cil.oc2.common.vm.CommonVirtualMachineItemStackHandlers; +import li.cil.oc2.common.vm.VirtualMachineItemStackHandlers; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.network.PacketBuffer; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.IWorldPosCallable; import net.minecraft.util.math.BlockPos; -import net.minecraft.world.World; import javax.annotation.Nullable; @@ -27,36 +26,35 @@ public final class ComputerContainer extends AbstractContainer { /////////////////////////////////////////////////////////////////// - private final World world; - private final BlockPos pos; + private final ComputerTileEntity computer; /////////////////////////////////////////////////////////////////// - public ComputerContainer(final int id, final ComputerTileEntity tileEntity, final PlayerInventory inventory) { + public ComputerContainer(final int id, final ComputerTileEntity computer, final PlayerInventory inventory) { super(Containers.COMPUTER_CONTAINER.get(), id); - this.world = inventory.player.getEntityWorld(); - this.pos = tileEntity.getPos(); + this.computer = computer; - final CommonVirtualMachineItemStackHandlers itemHandlers = tileEntity.getItemHandlers(); - itemHandlers.getItemHandler(DeviceTypes.FLASH_MEMORY).ifPresent(itemHandler -> { + final VirtualMachineItemStackHandlers handlers = computer.getItemStackHandlers(); + + handlers.getItemHandler(DeviceTypes.FLASH_MEMORY).ifPresent(itemHandler -> { if (itemHandler.getSlots() > 0) { this.addSlot(new TypedSlotItemHandler(itemHandler, DeviceTypes.FLASH_MEMORY, 0, 64, 78)); } }); - itemHandlers.getItemHandler(DeviceTypes.MEMORY).ifPresent(itemHandler -> { + handlers.getItemHandler(DeviceTypes.MEMORY).ifPresent(itemHandler -> { for (int slot = 0; slot < itemHandler.getSlots(); slot++) { this.addSlot(new TypedSlotItemHandler(itemHandler, DeviceTypes.MEMORY, slot, 64 + slot * SLOT_SIZE, 24)); } }); - itemHandlers.getItemHandler(DeviceTypes.HARD_DRIVE).ifPresent(itemHandler -> { + handlers.getItemHandler(DeviceTypes.HARD_DRIVE).ifPresent(itemHandler -> { for (int slot = 0; slot < itemHandler.getSlots(); slot++) { this.addSlot(new TypedSlotItemHandler(itemHandler, DeviceTypes.HARD_DRIVE, slot, 100 + (slot % 2) * SLOT_SIZE, 60 + (slot / 2) * SLOT_SIZE)); } }); - itemHandlers.getItemHandler(DeviceTypes.CARD).ifPresent(itemHandler -> { + handlers.getItemHandler(DeviceTypes.CARD).ifPresent(itemHandler -> { for (int slot = 0; slot < itemHandler.getSlots(); slot++) { this.addSlot(new TypedSlotItemHandler(itemHandler, DeviceTypes.CARD, slot, 38, 24 + slot * SLOT_SIZE)); } @@ -65,8 +63,10 @@ public final class ComputerContainer extends AbstractContainer { createPlayerInventoryAndHotbarSlots(inventory, 8, 115); } + /////////////////////////////////////////////////////////////////// + @Override public boolean canInteractWith(final PlayerEntity player) { - return isWithinUsableDistance(IWorldPosCallable.of(world, pos), player, Blocks.COMPUTER_BLOCK.get()); + return isWithinUsableDistance(IWorldPosCallable.of(computer.getWorld(), computer.getPos()), player, Blocks.COMPUTER_BLOCK.get()); } } diff --git a/src/main/java/li/cil/oc2/common/container/Containers.java b/src/main/java/li/cil/oc2/common/container/Containers.java index 264ef6e3..a052b719 100644 --- a/src/main/java/li/cil/oc2/common/container/Containers.java +++ b/src/main/java/li/cil/oc2/common/container/Containers.java @@ -15,6 +15,7 @@ public final class Containers { /////////////////////////////////////////////////////////////////// public static final RegistryObject> COMPUTER_CONTAINER = CONTAINERS.register(Constants.COMPUTER_BLOCK_NAME, () -> IForgeContainerType.create(ComputerContainer::create)); + public static final RegistryObject> ROBOT_CONTAINER = CONTAINERS.register(Constants.ROBOT_ENTITY_NAME, () -> IForgeContainerType.create(RobotContainer::create)); /////////////////////////////////////////////////////////////////// diff --git a/src/main/java/li/cil/oc2/common/container/RobotContainer.java b/src/main/java/li/cil/oc2/common/container/RobotContainer.java new file mode 100644 index 00000000..0895e23c --- /dev/null +++ b/src/main/java/li/cil/oc2/common/container/RobotContainer.java @@ -0,0 +1,69 @@ +package li.cil.oc2.common.container; + +import li.cil.oc2.api.bus.device.DeviceTypes; +import li.cil.oc2.common.entity.RobotEntity; +import li.cil.oc2.common.vm.VirtualMachineItemStackHandlers; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.network.PacketBuffer; + +import javax.annotation.Nullable; + +public final class RobotContainer extends AbstractContainer { + @Nullable + public static RobotContainer create(final int id, final PlayerInventory inventory, final PacketBuffer data) { + final int entityId = data.readVarInt(); + final Entity entity = inventory.player.getEntityWorld().getEntityByID(entityId); + if (!(entity instanceof RobotEntity)) { + return null; + } + return new RobotContainer(id, (RobotEntity) entity, inventory); + } + + /////////////////////////////////////////////////////////////////// + + private final RobotEntity robot; + + /////////////////////////////////////////////////////////////////// + + public RobotContainer(final int id, final RobotEntity robot, final PlayerInventory inventory) { + super(Containers.ROBOT_CONTAINER.get(), id); + this.robot = robot; + + final VirtualMachineItemStackHandlers handlers = robot.getItemStackHandlers(); + + handlers.getItemHandler(DeviceTypes.FLASH_MEMORY).ifPresent(itemHandler -> { + if (itemHandler.getSlots() > 0) { + this.addSlot(new TypedSlotItemHandler(itemHandler, DeviceTypes.FLASH_MEMORY, 0, 64, 78)); + } + }); + + handlers.getItemHandler(DeviceTypes.MEMORY).ifPresent(itemHandler -> { + for (int slot = 0; slot < itemHandler.getSlots(); slot++) { + this.addSlot(new TypedSlotItemHandler(itemHandler, DeviceTypes.MEMORY, slot, 64 + slot * SLOT_SIZE, 24)); + } + }); + + handlers.getItemHandler(DeviceTypes.HARD_DRIVE).ifPresent(itemHandler -> { + for (int slot = 0; slot < itemHandler.getSlots(); slot++) { + this.addSlot(new TypedSlotItemHandler(itemHandler, DeviceTypes.HARD_DRIVE, slot, 100 + (slot % 2) * SLOT_SIZE, 60 + (slot / 2) * SLOT_SIZE)); + } + }); + + handlers.getItemHandler(DeviceTypes.CARD).ifPresent(itemHandler -> { + for (int slot = 0; slot < itemHandler.getSlots(); slot++) { + this.addSlot(new TypedSlotItemHandler(itemHandler, DeviceTypes.CARD, slot, 38, 24 + slot * SLOT_SIZE)); + } + }); + + createPlayerInventoryAndHotbarSlots(inventory, 8, 115); + } + + /////////////////////////////////////////////////////////////////// + + @Override + public boolean canInteractWith(final PlayerEntity player) { + return robot.isEntityInRange(player, 8); + } +} diff --git a/src/main/java/li/cil/oc2/common/entity/RobotEntity.java b/src/main/java/li/cil/oc2/common/entity/RobotEntity.java index 09d6fadf..78fde973 100644 --- a/src/main/java/li/cil/oc2/common/entity/RobotEntity.java +++ b/src/main/java/li/cil/oc2/common/entity/RobotEntity.java @@ -2,23 +2,33 @@ package li.cil.oc2.common.entity; import li.cil.oc2.api.bus.DeviceBusElement; import li.cil.oc2.api.bus.device.Device; +import li.cil.oc2.api.bus.device.object.Callback; +import li.cil.oc2.api.bus.device.object.ObjectDevice; +import li.cil.oc2.api.bus.device.object.Parameter; +import li.cil.oc2.client.gui.RobotTerminalScreen; import li.cil.oc2.common.Constants; import li.cil.oc2.common.bus.AbstractDeviceBusController; +import li.cil.oc2.common.bus.AbstractDeviceBusElement; import li.cil.oc2.common.bus.device.util.Devices; import li.cil.oc2.common.bus.device.util.ItemDeviceInfo; +import li.cil.oc2.common.container.RobotContainer; import li.cil.oc2.common.entity.robot.*; import li.cil.oc2.common.integration.Wrenches; import li.cil.oc2.common.network.Network; -import li.cil.oc2.common.network.message.RobotBootErrorMessage; -import li.cil.oc2.common.network.message.RobotTerminalOutputMessage; +import li.cil.oc2.common.network.message.*; import li.cil.oc2.common.serialization.NBTSerialization; import li.cil.oc2.common.util.NBTTagIds; import li.cil.oc2.common.util.WorldUtils; import li.cil.oc2.common.vm.*; import net.minecraft.block.SoundType; +import net.minecraft.client.Minecraft; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; 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.item.ItemStack; import net.minecraft.nbt.CompoundNBT; import net.minecraft.nbt.ListNBT; @@ -27,25 +37,36 @@ import net.minecraft.network.datasync.DataParameter; import net.minecraft.network.datasync.DataSerializers; import net.minecraft.network.datasync.EntityDataManager; import net.minecraft.util.ActionResultType; +import net.minecraft.util.Direction; import net.minecraft.util.Hand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.ChunkPos; import net.minecraft.util.math.MathHelper; import net.minecraft.util.text.ITextComponent; import net.minecraft.world.World; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.common.util.LazyOptional; +import net.minecraftforge.event.world.ChunkEvent; +import net.minecraftforge.event.world.WorldEvent; import net.minecraftforge.fml.network.NetworkHooks; import javax.annotation.Nullable; import java.nio.ByteBuffer; import java.util.*; +import java.util.function.Consumer; public final class RobotEntity extends Entity { + public static final DataParameter TARGET_POSITION = EntityDataManager.createKey(RobotEntity.class, DataSerializers.BLOCK_POS); + public static final DataParameter TARGET_DIRECTION = EntityDataManager.createKey(RobotEntity.class, DataSerializers.DIRECTION); + private static final String TERMINAL_TAG_NAME = "terminal"; private static final String STATE_TAG_NAME = "state"; + private static final String BUS_ELEMENT_TAG_NAME = "bus_element"; private static final String COMMAND_PROCESSOR_TAG_NAME = "commands"; - private static final DataParameter IS_RUNNING = EntityDataManager.createKey(RobotEntity.class, DataSerializers.BOOLEAN); - private static final int MAX_QUEUED_ACTIONS = 16; + private static final int MAX_QUEUED_ACTIONS = 15; private static final int MEMORY_SLOTS = 4; private static final int HARD_DRIVE_SLOTS = 2; @@ -54,12 +75,15 @@ public final class RobotEntity extends Entity { /////////////////////////////////////////////////////////////////// + private final Consumer chunkUnloadListener = this::handleChunkUnload; + private final Consumer worldUnloadListener = this::handleWorldUnload; + private final AnimationState animationState = new AnimationState(); private final CommandProcessor commandProcessor = new CommandProcessor(); private final Terminal terminal = new Terminal(); - private final RobotVirtualMachineState state; private final RobotItemStackHandlers items = new RobotItemStackHandlers(); + private final RobotBusElement busElement = new RobotBusElement(); /////////////////////////////////////////////////////////////////// @@ -68,8 +92,11 @@ public final class RobotEntity extends Entity { this.preventEntitySpawning = true; setNoGravity(true); - final RobotBusController busController = new RobotBusController(items.busElement); + final RobotBusController busController = new RobotBusController(busElement); state = new RobotVirtualMachineState(busController, new CommonVirtualMachine(busController)); + state.virtualMachine.rtcMinecraft.setWorld(world); + + items.busElement.addDevice(new ObjectDevice(new RobotDevice(), "robot")); } /////////////////////////////////////////////////////////////////// @@ -87,14 +114,10 @@ public final class RobotEntity extends Entity { return state; } - public CommonVirtualMachineItemStackHandlers getItemHandlers() { + public VirtualMachineItemStackHandlers getItemStackHandlers() { return items; } - public boolean isRunning() { - return dataManager.get(IS_RUNNING); - } - public void start() { final World world = getEntityWorld(); if (world == null || world.isRemote()) { @@ -115,8 +138,24 @@ public final class RobotEntity extends Entity { @Override public void tick() { + if (firstUpdate) { + if (getEntityWorld().isRemote()) { + requestInitialState(); + } else { + registerListeners(); + RobotActions.initializeData(this); + if (commandProcessor.action != null) { + commandProcessor.action.initialize(this); + } + } + } + super.tick(); + if (!getEntityWorld().isRemote()) { + state.tick(); + } + commandProcessor.tick(); } @@ -124,25 +163,19 @@ public final class RobotEntity extends Entity { public ActionResultType processInitialInteract(final PlayerEntity player, final Hand hand) { final ItemStack stack = player.getHeldItem(hand); if (Wrenches.isWrench(stack)) { - if (!world.isRemote()) { + if (!world.isRemote() && player instanceof ServerPlayerEntity) { if (player.isSneaking()) { remove(); WorldUtils.playSound(world, getPosition(), SoundType.METAL, SoundType::getBreakSound); } else { - // todo open container + openContainerScreen(player); } } } else { if (player.isSneaking()) { start(); - } else { -// if (rand.nextBoolean()) { -// commandProcessor.move(MovementDirection.values()[rand.nextInt(MovementDirection.values().length)]); -// } else { -// commandProcessor.rotate(rand.nextBoolean() ? RotationDirection.LEFT : RotationDirection.RIGHT); - commandProcessor.rotate(RotationDirection.RIGHT); -// } - // TODO open terminal + inventory screen + } else if (world.isRemote()) { + openTerminalScreen(); } } @@ -154,6 +187,18 @@ public final class RobotEntity extends Entity { return NetworkHooks.getEntitySpawningPacket(this); } + @Override + public void remove(final boolean keepData) { + super.remove(keepData); + + handleUnload(); + + // Full unload to release out-of-nbt persisted runtime-only data such as ram. + state.virtualMachine.vmAdapter.unload(); + + // TODO drop self as item + } + @Override public boolean canBeCollidedWith() { return true; @@ -182,7 +227,6 @@ public final class RobotEntity extends Entity { @Override protected void registerData() { - getDataManager().register(IS_RUNNING, false); RobotActions.registerData(getDataManager()); } @@ -191,6 +235,7 @@ public final class RobotEntity extends Entity { tag.put(STATE_TAG_NAME, state.serialize()); tag.put(TERMINAL_TAG_NAME, NBTSerialization.serialize(terminal)); tag.put(COMMAND_PROCESSOR_TAG_NAME, commandProcessor.serialize()); + tag.put(BUS_ELEMENT_TAG_NAME, busElement.serialize()); tag.put(Constants.INVENTORY_TAG_NAME, items.serialize()); } @@ -199,6 +244,7 @@ public final class RobotEntity extends Entity { state.deserialize(tag.getCompound(STATE_TAG_NAME)); NBTSerialization.deserialize(tag.getCompound(TERMINAL_TAG_NAME), terminal); commandProcessor.deserialize(tag.getCompound(COMMAND_PROCESSOR_TAG_NAME)); + busElement.deserialize(tag.getCompound(BUS_ELEMENT_TAG_NAME)); if (tag.contains(Constants.INVENTORY_TAG_NAME, NBTTagIds.TAG_COMPOUND)) { items.deserialize(tag.getCompound(Constants.INVENTORY_TAG_NAME)); @@ -216,6 +262,69 @@ public final class RobotEntity extends Entity { /////////////////////////////////////////////////////////////////// + @OnlyIn(Dist.CLIENT) + private void requestInitialState() { + Network.INSTANCE.sendToServer(new RobotInitializationRequestMessage(this)); + } + + @OnlyIn(Dist.CLIENT) + private void openTerminalScreen() { + Minecraft.getInstance().displayGuiScreen(new RobotTerminalScreen(this, getName())); + } + + private void registerListeners() { + MinecraftForge.EVENT_BUS.addListener(chunkUnloadListener); + MinecraftForge.EVENT_BUS.addListener(worldUnloadListener); + } + + private void unregisterListeners() { + MinecraftForge.EVENT_BUS.unregister(chunkUnloadListener); + MinecraftForge.EVENT_BUS.unregister(worldUnloadListener); + } + + private void handleChunkUnload(final ChunkEvent.Unload event) { + if (event.getWorld() != getEntityWorld()) { + return; + } + + final ChunkPos chunkPos = new ChunkPos(getPosition()); + if (!Objects.equals(chunkPos, event.getChunk().getPos())) { + return; + } + + unregisterListeners(); + handleUnload(); + } + + private void handleWorldUnload(final WorldEvent.Unload event) { + if (event.getWorld() != getEntityWorld()) { + return; + } + + unregisterListeners(); + handleUnload(); + } + + private void handleUnload() { + state.joinVirtualMachine(); + state.virtualMachine.vmAdapter.suspend(); + state.busController.dispose(); + } + + private void openContainerScreen(final PlayerEntity player) { + NetworkHooks.openGui((ServerPlayerEntity) player, new INamedContainerProvider() { + @Override + public ITextComponent getDisplayName() { + return getName(); + } + + @Override + public Container createMenu(final int id, final PlayerInventory inventory, final PlayerEntity player) { + return new RobotContainer(id, RobotEntity.this, inventory); + } + }, b -> b.writeVarInt(getEntityId())); + } + private static float lerpClamped(final float from, final float to, final float delta) { if (from < to) { return Math.min(from + delta, to); @@ -255,7 +364,7 @@ public final class RobotEntity extends Entity { public float topRenderHover = -(hashCode() & 0xFFFF); // init to "random" to avoid synchronous hovering public void update(final float deltaTime, final Random random) { - if (isRunning() || commandProcessor.hasQueuedActions()) { + if (getState().isRunning() || commandProcessor.hasQueuedActions()) { topRenderHover = topRenderHover + deltaTime * HOVER_ANIMATION_SPEED; final float topOffsetY = MathHelper.sin(topRenderHover) / 32f; @@ -287,6 +396,10 @@ public final class RobotEntity extends Entity { return action != null || !queue.isEmpty(); } + public int getQueuedActionCount() { + return (action != null ? 1 : 0) + queue.size(); + } + public boolean move(final MovementDirection direction) { return addAction(new RobotMovementAction(direction)); } @@ -347,7 +460,6 @@ public final class RobotEntity extends Entity { if (tag.contains(ACTION_TAG_NAME, NBTTagIds.TAG_COMPOUND)) { action = RobotActions.deserialize(tag.getCompound(ACTION_TAG_NAME)); - action.initialize(RobotEntity.this); } } @@ -356,7 +468,7 @@ public final class RobotEntity extends Entity { return false; } - if (!isRunning()) { + if (!getState().isRunning()) { return false; } @@ -369,7 +481,7 @@ public final class RobotEntity extends Entity { } } - private final class RobotItemStackHandlers extends CommonVirtualMachineItemStackHandlers { + private final class RobotItemStackHandlers extends AbstractVirtualMachineItemStackHandlers { public RobotItemStackHandlers() { super(MEMORY_SLOTS, HARD_DRIVE_SLOTS, FLASH_MEMORY_SLOTS, CARD_SLOTS); } @@ -380,6 +492,43 @@ public final class RobotEntity extends Entity { } } + private final class RobotBusElement extends AbstractDeviceBusElement { + private static final String DEVICE_ID_TAG_NAME = "device_id"; + + private final Device device = new ObjectDevice(new RobotDevice(), "robot"); + private UUID deviceId = UUID.randomUUID(); + + @Override + public Optional>> getNeighbors() { + return Optional.of(Collections.singleton(LazyOptional.of(() -> items.busElement))); + } + + @Override + public Collection getLocalDevices() { + return Collections.singleton(device); + } + + @Override + public Optional getDeviceIdentifier(final Device device) { + if (device == this.device) { + return Optional.of(deviceId); + } + return super.getDeviceIdentifier(device); + } + + public CompoundNBT serialize() { + final CompoundNBT tag = new CompoundNBT(); + tag.putUniqueId(DEVICE_ID_TAG_NAME, deviceId); + return tag; + } + + public void deserialize(final CompoundNBT tag) { + if (tag.hasUniqueId(DEVICE_ID_TAG_NAME)) { + deviceId = tag.getUniqueId(DEVICE_ID_TAG_NAME); + } + } + } + private final class RobotBusController extends AbstractDeviceBusController { public RobotBusController(final DeviceBusElement root) { super(root); @@ -437,15 +586,41 @@ public final class RobotEntity extends Entity { commandProcessor.clear(); } + @Override + protected void handleBusStateChanged(final AbstractDeviceBusController.BusState value) { + Network.sendToClientsTrackingEntity(new RobotBusStateMessage(RobotEntity.this), RobotEntity.this); + } + @Override protected void handleRunStateChanged(final RunState value) { - dataManager.set(IS_RUNNING, isRunning()); + Network.sendToClientsTrackingEntity(new RobotRunStateMessage(RobotEntity.this), RobotEntity.this); } @Override protected void handleBootErrorChanged(@Nullable final ITextComponent value) { - final RobotBootErrorMessage message = new RobotBootErrorMessage(RobotEntity.this); - Network.sendToClientsTrackingEntity(message, RobotEntity.this); + Network.sendToClientsTrackingEntity(new RobotBootErrorMessage(RobotEntity.this), RobotEntity.this); + } + } + + public final class RobotDevice { + @Callback + public boolean move(@Parameter("direction") @Nullable final MovementDirection direction) { + if (direction == null) throw new IllegalArgumentException(); + return commandProcessor.move(direction); + } + + @Callback + public boolean turn(@Parameter("direction") @Nullable final RotationDirection direction) { + if (direction == null) throw new IllegalArgumentException(); + return commandProcessor.rotate(direction); + } + + @Callback + public int getQueuedActionCount() { + return commandProcessor.getQueuedActionCount(); + } + + private RobotDevice() { } } } diff --git a/src/main/java/li/cil/oc2/common/entity/robot/RobotMovementAction.java b/src/main/java/li/cil/oc2/common/entity/robot/RobotMovementAction.java index 5cb678fd..01b4d4bb 100644 --- a/src/main/java/li/cil/oc2/common/entity/robot/RobotMovementAction.java +++ b/src/main/java/li/cil/oc2/common/entity/robot/RobotMovementAction.java @@ -7,9 +7,6 @@ import li.cil.oc2.common.util.NBTTagIds; import li.cil.oc2.common.util.NBTUtils; import net.minecraft.entity.MoverType; import net.minecraft.nbt.CompoundNBT; -import net.minecraft.network.datasync.DataParameter; -import net.minecraft.network.datasync.DataSerializers; -import net.minecraft.network.datasync.EntityDataManager; import net.minecraft.util.Direction; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.vector.Vector3d; @@ -17,12 +14,11 @@ import net.minecraft.util.math.vector.Vector3d; import javax.annotation.Nullable; public final class RobotMovementAction extends AbstractRobotAction { - public static final DataParameter TARGET_POSITION = EntityDataManager.createKey(RobotEntity.class, DataSerializers.BLOCK_POS); public static final double TARGET_EPSILON = 0.0001; /////////////////////////////////////////////////////////////////// - private static final float MOVEMENT_SPEED = 0.5f / Constants.TICK_SECONDS; // In blocks per second. + private static final float MOVEMENT_SPEED = 1f / Constants.TICK_SECONDS; // In blocks per second. private static final String DIRECTION_TAG_NAME = "direction"; private static final String START_TAG_NAME = "start"; @@ -84,7 +80,7 @@ public final class RobotMovementAction extends AbstractRobotAction { target = getTargetPositionInBlock(targetPosition); } - robot.getDataManager().set(TARGET_POSITION, new BlockPos(target)); + robot.getDataManager().set(RobotEntity.TARGET_POSITION, new BlockPos(target)); } @Override @@ -99,7 +95,7 @@ public final class RobotMovementAction extends AbstractRobotAction { if (didCollide && !robot.getEntityWorld().isRemote()) { if (start != null) { target = getTargetPositionInBlock(start); - robot.getDataManager().set(TARGET_POSITION, start); + robot.getDataManager().set(RobotEntity.TARGET_POSITION, start); start = null; } else { diff --git a/src/main/java/li/cil/oc2/common/entity/robot/RobotMovementActionType.java b/src/main/java/li/cil/oc2/common/entity/robot/RobotMovementActionType.java index 268e95cf..986d8c8e 100644 --- a/src/main/java/li/cil/oc2/common/entity/robot/RobotMovementActionType.java +++ b/src/main/java/li/cil/oc2/common/entity/robot/RobotMovementActionType.java @@ -15,17 +15,17 @@ public final class RobotMovementActionType extends AbstractRobotActionType { @Override public void registerData(final EntityDataManager dataManager) { - dataManager.register(RobotMovementAction.TARGET_POSITION, BlockPos.ZERO); + dataManager.register(RobotEntity.TARGET_POSITION, BlockPos.ZERO); } @Override public void initializeData(final RobotEntity robot) { - robot.getDataManager().set(RobotMovementAction.TARGET_POSITION, robot.getPosition()); + robot.getDataManager().set(RobotEntity.TARGET_POSITION, robot.getPosition()); } @Override public void performClient(final RobotEntity robot) { - final Vector3d target = RobotMovementAction.getTargetPositionInBlock(robot.getDataManager().get(RobotMovementAction.TARGET_POSITION)); + final Vector3d target = RobotMovementAction.getTargetPositionInBlock(robot.getDataManager().get(RobotEntity.TARGET_POSITION)); if (robot.getPositionVec().squareDistanceTo(target) > RobotMovementAction.TARGET_EPSILON) { RobotMovementAction.moveTowards(robot, target); } diff --git a/src/main/java/li/cil/oc2/common/entity/robot/RobotRotationAction.java b/src/main/java/li/cil/oc2/common/entity/robot/RobotRotationAction.java index b106f36a..c02040ef 100644 --- a/src/main/java/li/cil/oc2/common/entity/robot/RobotRotationAction.java +++ b/src/main/java/li/cil/oc2/common/entity/robot/RobotRotationAction.java @@ -5,21 +5,17 @@ import li.cil.oc2.common.entity.RobotEntity; import li.cil.oc2.common.util.NBTTagIds; import li.cil.oc2.common.util.NBTUtils; import net.minecraft.nbt.CompoundNBT; -import net.minecraft.network.datasync.DataParameter; -import net.minecraft.network.datasync.DataSerializers; -import net.minecraft.network.datasync.EntityDataManager; import net.minecraft.util.Direction; import net.minecraft.util.math.MathHelper; import javax.annotation.Nullable; public final class RobotRotationAction extends AbstractRobotAction { - public static final DataParameter TARGET_DIRECTION = EntityDataManager.createKey(RobotEntity.class, DataSerializers.DIRECTION); public static final float TARGET_EPSILON = 0.0001f; /////////////////////////////////////////////////////////////////// - private static final float ROTATION_SPEED = 45f / Constants.TICK_SECONDS; // In degrees per second. + private static final float ROTATION_SPEED = 90f / Constants.TICK_SECONDS; // In degrees per second. private static final String DIRECTION_TAG_NAME = "direction"; private static final String TARGET_TAG_NAME = "start"; @@ -61,7 +57,7 @@ public final class RobotRotationAction extends AbstractRobotAction { } } - robot.getDataManager().set(TARGET_DIRECTION, target); + robot.getDataManager().set(RobotEntity.TARGET_DIRECTION, target); } @Override diff --git a/src/main/java/li/cil/oc2/common/entity/robot/RobotRotationActionType.java b/src/main/java/li/cil/oc2/common/entity/robot/RobotRotationActionType.java index 81a8f513..0488beea 100644 --- a/src/main/java/li/cil/oc2/common/entity/robot/RobotRotationActionType.java +++ b/src/main/java/li/cil/oc2/common/entity/robot/RobotRotationActionType.java @@ -15,17 +15,17 @@ public final class RobotRotationActionType extends AbstractRobotActionType { @Override public void registerData(final EntityDataManager dataManager) { - dataManager.register(RobotRotationAction.TARGET_DIRECTION, Direction.NORTH); + dataManager.register(RobotEntity.TARGET_DIRECTION, Direction.NORTH); } @Override public void initializeData(final RobotEntity robot) { - robot.getDataManager().set(RobotRotationAction.TARGET_DIRECTION, robot.getHorizontalFacing()); + robot.getDataManager().set(RobotEntity.TARGET_DIRECTION, robot.getHorizontalFacing()); } @Override public void performClient(final RobotEntity robot) { - final Direction target = robot.getDataManager().get(RobotRotationAction.TARGET_DIRECTION); + final Direction target = robot.getDataManager().get(RobotEntity.TARGET_DIRECTION); if (MathHelper.degreesDifferenceAbs(robot.rotationYaw, target.getHorizontalAngle()) > RobotRotationAction.TARGET_EPSILON) { RobotRotationAction.rotateTowards(robot, target); } diff --git a/src/main/java/li/cil/oc2/common/item/ItemGroup.java b/src/main/java/li/cil/oc2/common/item/ItemGroup.java index 775489bc..ef4a796b 100644 --- a/src/main/java/li/cil/oc2/common/item/ItemGroup.java +++ b/src/main/java/li/cil/oc2/common/item/ItemGroup.java @@ -5,7 +5,7 @@ import li.cil.oc2.common.Constants; import li.cil.oc2.common.bus.device.data.BaseBlockDevices; import li.cil.oc2.common.bus.device.data.Firmwares; import li.cil.oc2.common.util.ItemStackUtils; -import li.cil.oc2.common.vm.CommonVirtualMachineItemStackHandlers; +import li.cil.oc2.common.vm.AbstractVirtualMachineItemStackHandlers; import net.minecraft.item.ItemStack; import net.minecraft.nbt.CompoundNBT; import net.minecraft.util.NonNullList; @@ -46,18 +46,18 @@ public final class ItemGroup { final ItemStack computer = new ItemStack(Items.COMPUTER_ITEM.get()); final CompoundNBT computerItems = ItemStackUtils.getOrCreateTileEntityInventoryTag(computer); - computerItems.put(CommonVirtualMachineItemStackHandlers.MEMORY_TAG_NAME, makeInventoryTag( + computerItems.put(AbstractVirtualMachineItemStackHandlers.MEMORY_TAG_NAME, makeInventoryTag( MemoryItem.withCapacity(8 * Constants.MEGABYTE), MemoryItem.withCapacity(8 * Constants.MEGABYTE), MemoryItem.withCapacity(8 * Constants.MEGABYTE) )); - computerItems.put(CommonVirtualMachineItemStackHandlers.HARD_DRIVE_TAG_NAME, makeInventoryTag( + computerItems.put(AbstractVirtualMachineItemStackHandlers.HARD_DRIVE_TAG_NAME, makeInventoryTag( HardDriveItem.withBase(BaseBlockDevices.BUILDROOT.get()) )); - computerItems.put(CommonVirtualMachineItemStackHandlers.FLASH_MEMORY_TAG_NAME, makeInventoryTag( + computerItems.put(AbstractVirtualMachineItemStackHandlers.FLASH_MEMORY_TAG_NAME, makeInventoryTag( FlashMemoryItem.withFirmware(Firmwares.BUILDROOT.get()) )); - computerItems.put(CommonVirtualMachineItemStackHandlers.CARD_TAG_NAME, makeInventoryTag( + computerItems.put(AbstractVirtualMachineItemStackHandlers.CARD_TAG_NAME, makeInventoryTag( new ItemStack(Items.NETWORK_INTERFACE_CARD_ITEM.get()) )); diff --git a/src/main/java/li/cil/oc2/common/network/Network.java b/src/main/java/li/cil/oc2/common/network/Network.java index 56aef763..4b15aa8a 100644 --- a/src/main/java/li/cil/oc2/common/network/Network.java +++ b/src/main/java/li/cil/oc2/common/network/Network.java @@ -81,11 +81,41 @@ public final class Network { .consumer(RobotTerminalInputMessage::handleMessage) .add(); + INSTANCE.messageBuilder(RobotRunStateMessage.class, getNextPacketId(), NetworkDirection.PLAY_TO_CLIENT) + .encoder(RobotRunStateMessage::toBytes) + .decoder(RobotRunStateMessage::new) + .consumer(RobotRunStateMessage::handleMessage) + .add(); + + INSTANCE.messageBuilder(RobotBusStateMessage.class, getNextPacketId(), NetworkDirection.PLAY_TO_CLIENT) + .encoder(RobotBusStateMessage::toBytes) + .decoder(RobotBusStateMessage::new) + .consumer(RobotBusStateMessage::handleMessage) + .add(); + INSTANCE.messageBuilder(RobotBootErrorMessage.class, getNextPacketId(), NetworkDirection.PLAY_TO_CLIENT) .encoder(RobotBootErrorMessage::toBytes) .decoder(RobotBootErrorMessage::new) .consumer(RobotBootErrorMessage::handleMessage) .add(); + + INSTANCE.messageBuilder(RobotPowerMessage.class, getNextPacketId(), NetworkDirection.PLAY_TO_SERVER) + .encoder(RobotPowerMessage::toBytes) + .decoder(RobotPowerMessage::new) + .consumer(RobotPowerMessage::handleMessage) + .add(); + + INSTANCE.messageBuilder(RobotInitializationRequestMessage.class, getNextPacketId(), NetworkDirection.PLAY_TO_SERVER) + .encoder(RobotInitializationRequestMessage::toBytes) + .decoder(RobotInitializationRequestMessage::new) + .consumer(RobotInitializationRequestMessage::handleMessage) + .add(); + + INSTANCE.messageBuilder(RobotInitializationMessage.class, getNextPacketId(), NetworkDirection.PLAY_TO_CLIENT) + .encoder(RobotInitializationMessage::toBytes) + .decoder(RobotInitializationMessage::new) + .consumer(RobotInitializationMessage::handleMessage) + .add(); } public static void sendToClientsTrackingChunk(final T message, final Chunk chunk) { diff --git a/src/main/java/li/cil/oc2/common/network/message/ComputerBusStateMessage.java b/src/main/java/li/cil/oc2/common/network/message/ComputerBusStateMessage.java index 6a6b9c8c..0bdea435 100644 --- a/src/main/java/li/cil/oc2/common/network/message/ComputerBusStateMessage.java +++ b/src/main/java/li/cil/oc2/common/network/message/ComputerBusStateMessage.java @@ -11,13 +11,13 @@ import java.util.function.Supplier; public final class ComputerBusStateMessage { private BlockPos pos; - private AbstractDeviceBusController.BusState busState; + private AbstractDeviceBusController.BusState value; /////////////////////////////////////////////////////////////////// public ComputerBusStateMessage(final ComputerTileEntity tileEntity) { this.pos = tileEntity.getPos(); - this.busState = tileEntity.getState().getBusState(); + this.value = tileEntity.getState().getBusState(); } public ComputerBusStateMessage(final PacketBuffer buffer) { @@ -28,17 +28,17 @@ public final class ComputerBusStateMessage { public static boolean handleMessage(final ComputerBusStateMessage message, final Supplier context) { context.get().enqueueWork(() -> MessageUtils.withClientTileEntityAt(message.pos, ComputerTileEntity.class, - (tileEntity) -> tileEntity.getState().setBusStateClient(message.busState))); + (tileEntity) -> tileEntity.getState().setBusStateClient(message.value))); return true; } public void fromBytes(final PacketBuffer buffer) { pos = buffer.readBlockPos(); - busState = buffer.readEnumValue(AbstractDeviceBusController.BusState.class); + value = buffer.readEnumValue(AbstractDeviceBusController.BusState.class); } public static void toBytes(final ComputerBusStateMessage message, final PacketBuffer buffer) { buffer.writeBlockPos(message.pos); - buffer.writeEnumValue(message.busState); + buffer.writeEnumValue(message.value); } } diff --git a/src/main/java/li/cil/oc2/common/network/message/ComputerRunStateMessage.java b/src/main/java/li/cil/oc2/common/network/message/ComputerRunStateMessage.java index 5ca26e32..755217e2 100644 --- a/src/main/java/li/cil/oc2/common/network/message/ComputerRunStateMessage.java +++ b/src/main/java/li/cil/oc2/common/network/message/ComputerRunStateMessage.java @@ -11,13 +11,13 @@ import java.util.function.Supplier; public final class ComputerRunStateMessage { private BlockPos pos; - private VirtualMachineState.RunState runState; + private VirtualMachineState.RunState value; /////////////////////////////////////////////////////////////////// public ComputerRunStateMessage(final ComputerTileEntity tileEntity) { this.pos = tileEntity.getPos(); - this.runState = tileEntity.getState().getRunState(); + this.value = tileEntity.getState().getRunState(); } public ComputerRunStateMessage(final PacketBuffer buffer) { @@ -28,17 +28,17 @@ public final class ComputerRunStateMessage { public static boolean handleMessage(final ComputerRunStateMessage message, final Supplier context) { context.get().enqueueWork(() -> MessageUtils.withClientTileEntityAt(message.pos, ComputerTileEntity.class, - (tileEntity) -> tileEntity.getState().setRunStateClient(message.runState))); + (tileEntity) -> tileEntity.getState().setRunStateClient(message.value))); return true; } public void fromBytes(final PacketBuffer buffer) { pos = buffer.readBlockPos(); - runState = buffer.readEnumValue(VirtualMachineState.RunState.class); + value = buffer.readEnumValue(VirtualMachineState.RunState.class); } public static void toBytes(final ComputerRunStateMessage message, final PacketBuffer buffer) { buffer.writeBlockPos(message.pos); - buffer.writeEnumValue(message.runState); + buffer.writeEnumValue(message.value); } } diff --git a/src/main/java/li/cil/oc2/common/network/message/RobotBusStateMessage.java b/src/main/java/li/cil/oc2/common/network/message/RobotBusStateMessage.java new file mode 100644 index 00000000..d6cdf62f --- /dev/null +++ b/src/main/java/li/cil/oc2/common/network/message/RobotBusStateMessage.java @@ -0,0 +1,43 @@ +package li.cil.oc2.common.network.message; + +import li.cil.oc2.common.bus.AbstractDeviceBusController; +import li.cil.oc2.common.entity.RobotEntity; +import li.cil.oc2.common.network.MessageUtils; +import net.minecraft.network.PacketBuffer; +import net.minecraftforge.fml.network.NetworkEvent; + +import java.util.function.Supplier; + +public final class RobotBusStateMessage { + private int entityId; + private AbstractDeviceBusController.BusState value; + + /////////////////////////////////////////////////////////////////// + + public RobotBusStateMessage(final RobotEntity robot) { + this.entityId = robot.getEntityId(); + this.value = robot.getState().getBusState(); + } + + public RobotBusStateMessage(final PacketBuffer buffer) { + fromBytes(buffer); + } + + /////////////////////////////////////////////////////////////////// + + public static boolean handleMessage(final RobotBusStateMessage message, final Supplier context) { + context.get().enqueueWork(() -> MessageUtils.withClientEntity(message.entityId, RobotEntity.class, + (robot) -> robot.getState().setBusStateClient(message.value))); + return true; + } + + public void fromBytes(final PacketBuffer buffer) { + entityId = buffer.readVarInt(); + value = buffer.readEnumValue(AbstractDeviceBusController.BusState.class); + } + + public static void toBytes(final RobotBusStateMessage message, final PacketBuffer buffer) { + buffer.writeVarInt(message.entityId); + buffer.writeEnumValue(message.value); + } +} diff --git a/src/main/java/li/cil/oc2/common/network/message/RobotInitializationMessage.java b/src/main/java/li/cil/oc2/common/network/message/RobotInitializationMessage.java new file mode 100644 index 00000000..96c16700 --- /dev/null +++ b/src/main/java/li/cil/oc2/common/network/message/RobotInitializationMessage.java @@ -0,0 +1,64 @@ +package li.cil.oc2.common.network.message; + +import li.cil.oc2.common.bus.AbstractDeviceBusController; +import li.cil.oc2.common.entity.RobotEntity; +import li.cil.oc2.common.network.MessageUtils; +import li.cil.oc2.common.serialization.NBTSerialization; +import li.cil.oc2.common.vm.VirtualMachineState; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.network.PacketBuffer; +import net.minecraft.util.text.ITextComponent; +import net.minecraftforge.fml.network.NetworkEvent; + +import java.util.function.Supplier; + +public final class RobotInitializationMessage { + private int entityId; + private AbstractDeviceBusController.BusState busState; + private VirtualMachineState.RunState runState; + private ITextComponent bootError; + private CompoundNBT terminal; + + /////////////////////////////////////////////////////////////////// + + public RobotInitializationMessage(final RobotEntity robot) { + this.entityId = robot.getEntityId(); + this.busState = robot.getState().getBusState(); + this.runState = robot.getState().getRunState(); + this.bootError = robot.getState().getBootError(); + this.terminal = NBTSerialization.serialize(robot.getTerminal()); + } + + public RobotInitializationMessage(final PacketBuffer buffer) { + fromBytes(buffer); + } + + /////////////////////////////////////////////////////////////////// + + public static boolean handleMessage(final RobotInitializationMessage message, final Supplier context) { + context.get().enqueueWork(() -> MessageUtils.withClientEntity(message.entityId, RobotEntity.class, + (robot) -> { + robot.getState().setBusStateClient(message.busState); + robot.getState().setRunStateClient(message.runState); + robot.getState().setBootErrorClient(message.bootError); + NBTSerialization.deserialize(message.terminal, robot.getTerminal()); + })); + return true; + } + + public void fromBytes(final PacketBuffer buffer) { + entityId = buffer.readVarInt(); + busState = buffer.readEnumValue(AbstractDeviceBusController.BusState.class); + runState = buffer.readEnumValue(VirtualMachineState.RunState.class); + bootError = buffer.readTextComponent(); + terminal = buffer.readCompoundTag(); + } + + public static void toBytes(final RobotInitializationMessage message, final PacketBuffer buffer) { + buffer.writeVarInt(message.entityId); + buffer.writeEnumValue(message.busState); + buffer.writeEnumValue(message.runState); + buffer.writeTextComponent(message.bootError); + buffer.writeCompoundTag(message.terminal); + } +} diff --git a/src/main/java/li/cil/oc2/common/network/message/RobotInitializationRequestMessage.java b/src/main/java/li/cil/oc2/common/network/message/RobotInitializationRequestMessage.java new file mode 100644 index 00000000..0ecf1af5 --- /dev/null +++ b/src/main/java/li/cil/oc2/common/network/message/RobotInitializationRequestMessage.java @@ -0,0 +1,39 @@ +package li.cil.oc2.common.network.message; + +import li.cil.oc2.common.entity.RobotEntity; +import li.cil.oc2.common.network.MessageUtils; +import li.cil.oc2.common.network.Network; +import net.minecraft.network.PacketBuffer; +import net.minecraftforge.fml.network.NetworkEvent; + +import java.util.function.Supplier; + +public final class RobotInitializationRequestMessage { + private int entityId; + + /////////////////////////////////////////////////////////////////// + + public RobotInitializationRequestMessage(final RobotEntity robot) { + this.entityId = robot.getEntityId(); + } + + public RobotInitializationRequestMessage(final PacketBuffer buffer) { + fromBytes(buffer); + } + + /////////////////////////////////////////////////////////////////// + + public static boolean handleMessage(final RobotInitializationRequestMessage message, final Supplier context) { + context.get().enqueueWork(() -> MessageUtils.withServerEntity(context, message.entityId, RobotEntity.class, + (robot) -> Network.INSTANCE.reply(new RobotInitializationMessage(robot), context.get()))); + return true; + } + + public void fromBytes(final PacketBuffer buffer) { + entityId = buffer.readVarInt(); + } + + public static void toBytes(final RobotInitializationRequestMessage message, final PacketBuffer buffer) { + buffer.writeVarInt(message.entityId); + } +} diff --git a/src/main/java/li/cil/oc2/common/network/message/RobotPowerMessage.java b/src/main/java/li/cil/oc2/common/network/message/RobotPowerMessage.java new file mode 100644 index 00000000..497ec0dd --- /dev/null +++ b/src/main/java/li/cil/oc2/common/network/message/RobotPowerMessage.java @@ -0,0 +1,52 @@ +package li.cil.oc2.common.network.message; + +import li.cil.oc2.common.entity.RobotEntity; +import li.cil.oc2.common.network.MessageUtils; +import net.minecraft.entity.player.ServerPlayerEntity; +import net.minecraft.network.PacketBuffer; +import net.minecraftforge.fml.network.NetworkEvent; + +import java.util.function.Supplier; + +public final class RobotPowerMessage { + private int entityId; + private boolean power; + + /////////////////////////////////////////////////////////////////// + + public RobotPowerMessage(final RobotEntity robot, final boolean power) { + this.entityId = robot.getEntityId(); + this.power = power; + } + + public RobotPowerMessage(final PacketBuffer buffer) { + fromBytes(buffer); + } + + /////////////////////////////////////////////////////////////////// + + public static boolean handleMessage(final RobotPowerMessage message, final Supplier context) { + context.get().enqueueWork(() -> MessageUtils.withServerEntity(context, message.entityId, RobotEntity.class, + (robot) -> { + final ServerPlayerEntity player = context.get().getSender(); + if (player != null && robot.isEntityInRange(player, 8)) { + if (message.power) { + robot.start(); + } else { + robot.stop(); + } + } + })); + return true; + } + + public void fromBytes(final PacketBuffer buffer) { + entityId = buffer.readVarInt(); + power = buffer.readBoolean(); + } + + public static void toBytes(final RobotPowerMessage message, final PacketBuffer buffer) { + buffer.writeVarInt(message.entityId); + buffer.writeBoolean(message.power); + } +} diff --git a/src/main/java/li/cil/oc2/common/network/message/RobotRunStateMessage.java b/src/main/java/li/cil/oc2/common/network/message/RobotRunStateMessage.java new file mode 100644 index 00000000..d2ec7c02 --- /dev/null +++ b/src/main/java/li/cil/oc2/common/network/message/RobotRunStateMessage.java @@ -0,0 +1,43 @@ +package li.cil.oc2.common.network.message; + +import li.cil.oc2.common.entity.RobotEntity; +import li.cil.oc2.common.network.MessageUtils; +import li.cil.oc2.common.vm.VirtualMachineState; +import net.minecraft.network.PacketBuffer; +import net.minecraftforge.fml.network.NetworkEvent; + +import java.util.function.Supplier; + +public final class RobotRunStateMessage { + private int entityId; + private VirtualMachineState.RunState value; + + /////////////////////////////////////////////////////////////////// + + public RobotRunStateMessage(final RobotEntity robot) { + this.entityId = robot.getEntityId(); + this.value = robot.getState().getRunState(); + } + + public RobotRunStateMessage(final PacketBuffer buffer) { + fromBytes(buffer); + } + + /////////////////////////////////////////////////////////////////// + + public static boolean handleMessage(final RobotRunStateMessage message, final Supplier context) { + context.get().enqueueWork(() -> MessageUtils.withClientEntity(message.entityId, RobotEntity.class, + (robot) -> robot.getState().setRunStateClient(message.value))); + return true; + } + + public void fromBytes(final PacketBuffer buffer) { + entityId = buffer.readVarInt(); + value = buffer.readEnumValue(VirtualMachineState.RunState.class); + } + + public static void toBytes(final RobotRunStateMessage message, final PacketBuffer buffer) { + buffer.writeVarInt(message.entityId); + buffer.writeEnumValue(message.value); + } +} diff --git a/src/main/java/li/cil/oc2/common/tileentity/ComputerTileEntity.java b/src/main/java/li/cil/oc2/common/tileentity/ComputerTileEntity.java index f510a9d7..65f9916a 100644 --- a/src/main/java/li/cil/oc2/common/tileentity/ComputerTileEntity.java +++ b/src/main/java/li/cil/oc2/common/tileentity/ComputerTileEntity.java @@ -83,7 +83,7 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic return state; } - public CommonVirtualMachineItemStackHandlers getItemHandlers() { + public VirtualMachineItemStackHandlers getItemStackHandlers() { return items; } @@ -162,7 +162,8 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic super.remove(); // Unload only suspends, but we want to do a full clean-up when we get - // destroyed, so stuff inside us can delete out-of-nbt persisted data. + // destroyed, so stuff inside us can delete out-of-nbt persisted runtime- + // only data such as ram. state.virtualMachine.vmAdapter.unload(); } @@ -259,7 +260,7 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic /////////////////////////////////////////////////////////////////// - private final class ComputerItemStackHandlers extends CommonVirtualMachineItemStackHandlers { + private final class ComputerItemStackHandlers extends AbstractVirtualMachineItemStackHandlers { public ComputerItemStackHandlers() { super(MEMORY_SLOTS, HARD_DRIVE_SLOTS, FLASH_MEMORY_SLOTS, CARD_SLOTS); } @@ -364,8 +365,7 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic @Override protected void handleBusStateChanged(final AbstractDeviceBusController.BusState value) { - final ComputerBusStateMessage message = new ComputerBusStateMessage(ComputerTileEntity.this); - Network.sendToClientsTrackingChunk(message, chunk); + Network.sendToClientsTrackingChunk(new ComputerBusStateMessage(ComputerTileEntity.this), chunk); if (value == AbstractDeviceBusController.BusState.READY) { // Bus just became ready, meaning new devices may be available, meaning new @@ -379,15 +379,13 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic // This method can be called from disposal logic, so if we are disposed quickly enough // chunk may not be initialized yet. Avoid resulting NRE in network logic. if (chunk != null) { - final ComputerRunStateMessage message = new ComputerRunStateMessage(ComputerTileEntity.this); - Network.sendToClientsTrackingChunk(message, chunk); + Network.sendToClientsTrackingChunk(new ComputerRunStateMessage(ComputerTileEntity.this), chunk); } } @Override protected void handleBootErrorChanged(@Nullable final ITextComponent value) { - final ComputerBootErrorMessage message = new ComputerBootErrorMessage(ComputerTileEntity.this); - Network.sendToClientsTrackingChunk(message, chunk); + Network.sendToClientsTrackingChunk(new ComputerBootErrorMessage(ComputerTileEntity.this), chunk); } } } diff --git a/src/main/java/li/cil/oc2/common/vm/CommonVirtualMachineItemStackHandlers.java b/src/main/java/li/cil/oc2/common/vm/AbstractVirtualMachineItemStackHandlers.java similarity index 93% rename from src/main/java/li/cil/oc2/common/vm/CommonVirtualMachineItemStackHandlers.java rename to src/main/java/li/cil/oc2/common/vm/AbstractVirtualMachineItemStackHandlers.java index a2c3eb39..c5fde87d 100644 --- a/src/main/java/li/cil/oc2/common/vm/CommonVirtualMachineItemStackHandlers.java +++ b/src/main/java/li/cil/oc2/common/vm/AbstractVirtualMachineItemStackHandlers.java @@ -20,7 +20,7 @@ import net.minecraftforge.items.wrapper.CombinedInvWrapper; import java.util.*; import java.util.function.Function; -public abstract class CommonVirtualMachineItemStackHandlers { +public abstract class AbstractVirtualMachineItemStackHandlers implements VirtualMachineItemStackHandlers { private static final long ITEM_DEVICE_BASE_ADDRESS = 0x40000000L; private static final int ITEM_DEVICE_STRIDE = 0x1000; @@ -52,10 +52,10 @@ public abstract class CommonVirtualMachineItemStackHandlers { /////////////////////////////////////////////////////////////////// - public CommonVirtualMachineItemStackHandlers(final int memorySlots, - final int hardDriveSlots, - final int flashMemorySlots, - final int cardSlots) { + public AbstractVirtualMachineItemStackHandlers(final int memorySlots, + final int hardDriveSlots, + final int flashMemorySlots, + final int cardSlots) { memoryItemHandler = new ItemHandler(memorySlots, this::getDevices, DeviceTypes.MEMORY); hardDriveItemHandler = new ItemHandler(hardDriveSlots, this::getDevices, DeviceTypes.HARD_DRIVE); flashMemoryItemHandler = new ItemHandler(flashMemorySlots, this::getDevices, DeviceTypes.FLASH_MEMORY); @@ -66,6 +66,7 @@ public abstract class CommonVirtualMachineItemStackHandlers { /////////////////////////////////////////////////////////////////// + @Override public Optional getItemHandler(final DeviceType deviceType) { if (deviceType == DeviceTypes.MEMORY) { return Optional.of(memoryItemHandler); @@ -79,6 +80,7 @@ public abstract class CommonVirtualMachineItemStackHandlers { return Optional.empty(); } + @Override public boolean isEmpty() { for (int slot = 0; slot < itemHandlers.getSlots(); slot++) { if (!itemHandlers.getStackInSlot(slot).isEmpty()) { @@ -116,6 +118,7 @@ public abstract class CommonVirtualMachineItemStackHandlers { return OptionalLong.empty(); } + @Override public void exportDeviceDataToItemStacks() { memoryItemHandler.exportDeviceDataToItemStacks(); hardDriveItemHandler.exportDeviceDataToItemStacks(); @@ -166,7 +169,7 @@ public abstract class CommonVirtualMachineItemStackHandlers { @Override protected void onContentsChanged(final int slot) { super.onContentsChanged(slot); - CommonVirtualMachineItemStackHandlers.this.onContentsChanged(this, slot); + AbstractVirtualMachineItemStackHandlers.this.onContentsChanged(this, slot); } } diff --git a/src/main/java/li/cil/oc2/common/vm/VirtualMachineItemStackHandlers.java b/src/main/java/li/cil/oc2/common/vm/VirtualMachineItemStackHandlers.java new file mode 100644 index 00000000..28832870 --- /dev/null +++ b/src/main/java/li/cil/oc2/common/vm/VirtualMachineItemStackHandlers.java @@ -0,0 +1,14 @@ +package li.cil.oc2.common.vm; + +import li.cil.oc2.api.bus.device.DeviceType; +import net.minecraftforge.items.IItemHandler; + +import java.util.Optional; + +public interface VirtualMachineItemStackHandlers { + Optional getItemHandler(DeviceType deviceType); + + boolean isEmpty(); + + void exportDeviceDataToItemStacks(); +} diff --git a/src/main/resources/assets/oc2/lang/en_us.json b/src/main/resources/assets/oc2/lang/en_us.json index 3f98655e..316a6585 100644 --- a/src/main/resources/assets/oc2/lang/en_us.json +++ b/src/main/resources/assets/oc2/lang/en_us.json @@ -16,6 +16,8 @@ "item.oc2.redstone_interface_card": "Redstone Interface Card", "item.oc2.network_interface_card": "Network Interface Card", + "entity.oc2.robot": "Robot", + "config.oc2.maxAllocatedMemory": "Maximum allocated memory", "config.oc2.maxMemorySize:": "Maximum memory device size", "config.oc2.maxHardDriveSize": "Maximum hard drive device size", diff --git a/src/main/resources/assets/oc2/textures/entity/robot/robot.png b/src/main/resources/assets/oc2/textures/entity/robot/robot.png index 1071b5f2..8aeb30b5 100644 Binary files a/src/main/resources/assets/oc2/textures/entity/robot/robot.png and b/src/main/resources/assets/oc2/textures/entity/robot/robot.png differ