diff --git a/src/main/java/li/cil/oc2/Constants.java b/src/main/java/li/cil/oc2/Constants.java index 7794891e..9af121ee 100644 --- a/src/main/java/li/cil/oc2/Constants.java +++ b/src/main/java/li/cil/oc2/Constants.java @@ -25,5 +25,13 @@ public final class Constants { /////////////////////////////////////////////////////////////////// - public static final String SUFFIX_FORMAT = "tooltip.oc2.suffix_format"; + public static final String TOOLTIP_SUFFIX_FORMAT = "tooltip.oc2.suffix_format"; + + /////////////////////////////////////////////////////////////////// + + public static final String COMPUTER_BOOT_ERROR_UNKNOWN = "gui.oc2.computer.boot_error.unknown"; + public static final String COMPUTER_BOOT_ERROR_NO_RAM = "gui.oc2.computer.boot_error.no_ram"; + public static final String COMPUTER_BUS_STATE_INCOMPLETE = "gui.oc2.computer.bus_state.incomplete"; + public static final String COMPUTER_BUS_STATE_TOO_COMPLEX = "gui.oc2.computer.bus_state.too_complex"; + public static final String COMPUTER_BUS_STATE_MULTIPLE_CONTROLLERS = "gui.oc2.computer.bus_state.multiple_controllers"; } diff --git a/src/main/java/li/cil/oc2/client/render/tile/ComputerTileEntityRenderer.java b/src/main/java/li/cil/oc2/client/render/tile/ComputerTileEntityRenderer.java index 98aee3a1..14432156 100644 --- a/src/main/java/li/cil/oc2/client/render/tile/ComputerTileEntityRenderer.java +++ b/src/main/java/li/cil/oc2/client/render/tile/ComputerTileEntityRenderer.java @@ -3,11 +3,13 @@ package li.cil.oc2.client.render.tile; import com.mojang.blaze3d.matrix.MatrixStack; import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.vertex.IVertexBuilder; +import li.cil.oc2.Constants; import li.cil.oc2.api.API; import li.cil.oc2.client.render.OpenComputersRenderType; import li.cil.oc2.common.block.ComputerBlock; import li.cil.oc2.common.block.entity.ComputerTileEntity; import li.cil.oc2.common.vm.Terminal; +import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.renderer.IRenderTypeBuffer; import net.minecraft.client.renderer.model.RenderMaterial; import net.minecraft.client.renderer.tileentity.TileEntityRenderer; @@ -19,11 +21,17 @@ import net.minecraft.util.math.vector.Matrix4f; import net.minecraft.util.math.vector.Quaternion; import net.minecraft.util.math.vector.Vector3d; import net.minecraft.util.math.vector.Vector3f; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.ITextProperties; +import net.minecraft.util.text.Style; +import net.minecraft.util.text.TranslationTextComponent; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.client.event.TextureStitchEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; +import java.util.List; + @Mod.EventBusSubscriber(value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD) public final class ComputerTileEntityRenderer extends TileEntityRenderer { private static final ResourceLocation OVERLAY_POWER_LOCATION = new ResourceLocation(API.MOD_ID, "blocks/computer/computer_overlay_power"); @@ -80,6 +88,8 @@ public final class ComputerTileEntityRenderer extends TileEntityRenderer wrappedText = renderDispatcher.getFontRenderer().getCharacterManager().func_238362_b_(text, maxWidth, Style.EMPTY); + if (wrappedText.size() == 1) { + final int textWidth = fontRenderer.getStringPropertyWidth(text); + fontRenderer.func_243248_b(stack, text, (maxWidth - textWidth) * 0.5f, 0, 0xEE3322); + } else { + for (int i = 0; i < wrappedText.size(); i++) { + fontRenderer.drawString(stack, wrappedText.get(i).getString(), 0, i * fontRenderer.FONT_HEIGHT, 0xEE3322); + } + } + + stack.pop(); + } + + private void renderStatus(final Matrix4f matrix, final IRenderTypeBuffer buffer) { + renderStatus(matrix, buffer, 0); + } + + private void renderStatus(final Matrix4f matrix, final IRenderTypeBuffer buffer, final int frequency) { if (frequency <= 0 || (((System.currentTimeMillis() + hashCode()) / frequency) % 2) == 1) { - drawQuad(matrix, TEXTURE_STATUS.getBuffer(buffer, OpenComputersRenderType::getUnlitBlock)); + renderQuad(matrix, TEXTURE_STATUS.getBuffer(buffer, OpenComputersRenderType::getUnlitBlock)); } } - private void drawPower(final Matrix4f matrix, final IRenderTypeBuffer buffer) { - drawQuad(matrix, TEXTURE_POWER.getBuffer(buffer, OpenComputersRenderType::getUnlitBlock)); + private void renderPower(final Matrix4f matrix, final IRenderTypeBuffer buffer) { + renderQuad(matrix, TEXTURE_POWER.getBuffer(buffer, OpenComputersRenderType::getUnlitBlock)); } - private static void drawQuad(final Matrix4f matrix, final IVertexBuilder buffer) { + private static void renderQuad(final Matrix4f matrix, final IVertexBuilder buffer) { // NB: We may get a SpriteAwareVertexBuilder here. Sadly, its chaining is broken, // because methods may return the underlying vertex builder, so e.g. calling // buffer.pos(...).tex(...) will not actually call SpriteAwareVertexBuilder.tex(...) 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 1941e73d..cb6790d2 100644 --- a/src/main/java/li/cil/oc2/common/block/ComputerBlock.java +++ b/src/main/java/li/cil/oc2/common/block/ComputerBlock.java @@ -71,7 +71,7 @@ public final class ComputerBlock extends HorizontalBlock { final TileEntity tileEntity = world.getTileEntity(pos); if (tileEntity instanceof ComputerTileEntity) { final ComputerTileEntity busCable = (ComputerTileEntity) tileEntity; - busCable.handleNeighborChanged(changedBlockPos); + busCable.handleNeighborChanged(); } } diff --git a/src/main/java/li/cil/oc2/common/block/entity/ComputerTileEntity.java b/src/main/java/li/cil/oc2/common/block/entity/ComputerTileEntity.java index cf6744bd..a7b8f012 100644 --- a/src/main/java/li/cil/oc2/common/block/entity/ComputerTileEntity.java +++ b/src/main/java/li/cil/oc2/common/block/entity/ComputerTileEntity.java @@ -18,6 +18,7 @@ import li.cil.oc2.common.capabilities.Capabilities; import li.cil.oc2.common.container.DeviceItemStackHandler; import li.cil.oc2.common.init.TileEntities; import li.cil.oc2.common.network.Network; +import li.cil.oc2.common.network.message.ComputerBootErrorMessage; import li.cil.oc2.common.network.message.ComputerBusStateMessage; import li.cil.oc2.common.network.message.ComputerRunStateMessage; import li.cil.oc2.common.network.message.TerminalBlockOutputMessage; @@ -30,6 +31,7 @@ import li.cil.oc2.common.vm.VirtualMachine; import li.cil.oc2.common.vm.VirtualMachineRunner; import li.cil.sedna.api.device.MemoryMappedDevice; import li.cil.sedna.api.device.PhysicalMemory; +import li.cil.sedna.api.memory.MemoryAccessException; import li.cil.sedna.buildroot.Buildroot; import li.cil.sedna.device.serial.UART16550A; import li.cil.sedna.device.virtio.VirtIOFileSystemDevice; @@ -39,7 +41,8 @@ import net.minecraft.block.BlockState; import net.minecraft.nbt.CompoundNBT; import net.minecraft.tileentity.ITickableTileEntity; import net.minecraft.util.Direction; -import net.minecraft.util.math.BlockPos; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TranslationTextComponent; import net.minecraft.world.World; import net.minecraft.world.chunk.Chunk; import net.minecraftforge.api.distmarker.Dist; @@ -62,12 +65,14 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic /////////////////////////////////////////////////////////////////// - private static final String BUS_ELEMENT_NBT_TAG_NAME = "busElement"; - private static final String BUS_STATE_NBT_TAG_NAME = "busState"; - private static final String TERMINAL_NBT_TAG_NAME = "terminal"; - private static final String VIRTUAL_MACHINE_NBT_TAG_NAME = "virtualMachine"; - private static final String RUNNER_NBT_TAG_NAME = "runner"; - private static final String RUN_STATE_NBT_TAG_NAME = "runState"; + private static final String BUS_ELEMENT_TAG_NAME = "busElement"; + private static final String TERMINAL_TAG_NAME = "terminal"; + private static final String VIRTUAL_MACHINE_TAG_NAME = "virtualMachine"; + private static final String RUNNER_TAG_NAME = "runner"; + + private static final String BUS_STATE_TAG_NAME = "busState"; + private static final String RUN_STATE_TAG_NAME = "runState"; + private static final String BOOT_ERROR_TAG_NAME = "bootError"; private static final int DEVICE_LOAD_RETRY_INTERVAL = 10 * 20; // In ticks. @@ -88,6 +93,7 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic private final AbstractDeviceBusController busController; private AbstractDeviceBusController.BusState busState; private RunState runState; + private ITextComponent bootError; private int loadDevicesDelay; /////////////////////////////////////////////////////////////////// @@ -127,6 +133,7 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic return; } + setBootError(null); setRunState(RunState.LOADING_DEVICES); loadDevicesDelay = 0; } @@ -157,7 +164,12 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic return runState; } - public void handleNeighborChanged(final BlockPos pos) { + @Nullable + public ITextComponent getBootError() { + return bootError; + } + + public void handleNeighborChanged() { busController.scheduleBusScan(); } @@ -177,6 +189,13 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic } } + public void setBootErrorClient(final ITextComponent value) { + final World world = getWorld(); + if (world != null && world.isRemote()) { + bootError = value; + } + } + @Override public @NotNull LazyOptional getCapability(final @NotNull Capability capability, @Nullable final Direction side) { if (isRemoved()) { @@ -240,18 +259,24 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic // May have a valid runner after load. In which case we just had to wait for // bus setup and devices to load. So we can keep using it. if (runner == null) { - virtualMachine.board.reset(); - try { + virtualMachine.board.reset(); virtualMachine.board.initialize(); - } catch (final Throwable e) { + } catch (final IllegalStateException e) { + // FDT did not fit into memory. Technically it's possible to run with + // a program that only uses registers. But not supporting that esoteric + // use-case loses out against avoiding people getting confused for having + // forgotten to add some RAM modules. + setBootError(new TranslationTextComponent(Constants.COMPUTER_BOOT_ERROR_NO_RAM)); + setRunState(RunState.STOPPED); + return; + } catch (final MemoryAccessException e) { LOGGER.error(e); + setBootError(new TranslationTextComponent(Constants.COMPUTER_BOOT_ERROR_UNKNOWN)); setRunState(RunState.STOPPED); return; } - virtualMachine.board.setRunning(true); - runner = new ComputerVirtualMachineRunner(virtualMachine); } @@ -285,9 +310,10 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic public CompoundNBT getUpdateTag() { final CompoundNBT result = super.getUpdateTag(); - result.put(TERMINAL_NBT_TAG_NAME, NBTSerialization.serialize(terminal)); - result.putInt(BUS_STATE_NBT_TAG_NAME, busState.ordinal()); - result.putInt(RUN_STATE_NBT_TAG_NAME, runState.ordinal()); + result.put(TERMINAL_TAG_NAME, NBTSerialization.serialize(terminal)); + result.putInt(BUS_STATE_TAG_NAME, busState.ordinal()); + result.putInt(RUN_STATE_TAG_NAME, runState.ordinal()); + result.putString(BOOT_ERROR_TAG_NAME, ITextComponent.Serializer.toJson(bootError)); return result; } @@ -296,9 +322,10 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic public void handleUpdateTag(final BlockState state, final CompoundNBT tag) { super.handleUpdateTag(state, tag); - NBTSerialization.deserialize(tag.getCompound(TERMINAL_NBT_TAG_NAME), terminal); - busState = AbstractDeviceBusController.BusState.values()[tag.getInt(BUS_STATE_NBT_TAG_NAME)]; - runState = RunState.values()[tag.getInt(RUN_STATE_NBT_TAG_NAME)]; + NBTSerialization.deserialize(tag.getCompound(TERMINAL_TAG_NAME), terminal); + busState = AbstractDeviceBusController.BusState.values()[tag.getInt(BUS_STATE_TAG_NAME)]; + runState = RunState.values()[tag.getInt(RUN_STATE_TAG_NAME)]; + bootError = ITextComponent.Serializer.getComponentFromJson(tag.getString(BOOT_ERROR_TAG_NAME)); } @Override @@ -307,15 +334,15 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic joinVirtualMachine(); - compound.put(TERMINAL_NBT_TAG_NAME, NBTSerialization.serialize(terminal)); + compound.put(TERMINAL_TAG_NAME, NBTSerialization.serialize(terminal)); - compound.put(BUS_ELEMENT_NBT_TAG_NAME, NBTSerialization.serialize(busElement)); - compound.put(VIRTUAL_MACHINE_NBT_TAG_NAME, NBTSerialization.serialize(virtualMachine)); + compound.put(BUS_ELEMENT_TAG_NAME, NBTSerialization.serialize(busElement)); + compound.put(VIRTUAL_MACHINE_TAG_NAME, NBTSerialization.serialize(virtualMachine)); if (runner != null) { - compound.put(RUNNER_NBT_TAG_NAME, NBTSerialization.serialize(runner)); + compound.put(RUNNER_TAG_NAME, NBTSerialization.serialize(runner)); } else { - NBTUtils.putEnum(compound, RUN_STATE_NBT_TAG_NAME, runState); + NBTUtils.putEnum(compound, RUN_STATE_TAG_NAME, runState); } compound.put(Constants.BLOCK_ENTITY_INVENTORY_TAG_NAME, itemHandler.serializeNBT()); @@ -329,22 +356,22 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic joinVirtualMachine(); - NBTSerialization.deserialize(compound.getCompound(TERMINAL_NBT_TAG_NAME), terminal); + NBTSerialization.deserialize(compound.getCompound(TERMINAL_TAG_NAME), terminal); - if (compound.contains(BUS_ELEMENT_NBT_TAG_NAME, NBTTagIds.TAG_COMPOUND)) { - NBTSerialization.deserialize(compound.getCompound(BUS_ELEMENT_NBT_TAG_NAME), busElement); + if (compound.contains(BUS_ELEMENT_TAG_NAME, NBTTagIds.TAG_COMPOUND)) { + NBTSerialization.deserialize(compound.getCompound(BUS_ELEMENT_TAG_NAME), busElement); } - if (compound.contains(VIRTUAL_MACHINE_NBT_TAG_NAME, NBTTagIds.TAG_COMPOUND)) { - NBTSerialization.deserialize(compound.getCompound(VIRTUAL_MACHINE_NBT_TAG_NAME), virtualMachine); + if (compound.contains(VIRTUAL_MACHINE_TAG_NAME, NBTTagIds.TAG_COMPOUND)) { + NBTSerialization.deserialize(compound.getCompound(VIRTUAL_MACHINE_TAG_NAME), virtualMachine); } - if (compound.contains(RUNNER_NBT_TAG_NAME, NBTTagIds.TAG_COMPOUND)) { + if (compound.contains(RUNNER_TAG_NAME, NBTTagIds.TAG_COMPOUND)) { runner = new ComputerVirtualMachineRunner(virtualMachine); - NBTSerialization.deserialize(compound.getCompound(RUNNER_NBT_TAG_NAME), runner); + NBTSerialization.deserialize(compound.getCompound(RUNNER_TAG_NAME), runner); runState = RunState.LOADING_DEVICES; } else { - runState = NBTUtils.getEnum(compound, RUN_STATE_NBT_TAG_NAME, RunState.class); + runState = NBTUtils.getEnum(compound, RUN_STATE_TAG_NAME, RunState.class); if (runState == null) { runState = RunState.STOPPED; } else if (runState == RunState.RUNNING) { @@ -413,6 +440,12 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic } } + private void setBootError(@Nullable final ITextComponent value) { + bootError = value; + final ComputerBootErrorMessage message = new ComputerBootErrorMessage(this); + Network.sendToClientsTrackingChunk(message, chunk); + } + private void stopRunnerAndResetVM() { joinVirtualMachine(); runner = null; diff --git a/src/main/java/li/cil/oc2/common/item/HddItem.java b/src/main/java/li/cil/oc2/common/item/HddItem.java index 82595720..db9c7f94 100644 --- a/src/main/java/li/cil/oc2/common/item/HddItem.java +++ b/src/main/java/li/cil/oc2/common/item/HddItem.java @@ -74,11 +74,11 @@ public class HddItem extends Item { if (baseBlockDevice != null) { return new StringTextComponent("") .append(super.getDisplayName(stack)) - .append(new TranslationTextComponent(Constants.SUFFIX_FORMAT, baseBlockDevice)); + .append(new TranslationTextComponent(Constants.TOOLTIP_SUFFIX_FORMAT, baseBlockDevice)); } else { return new StringTextComponent("") .append(super.getDisplayName(stack)) - .append(new TranslationTextComponent(Constants.SUFFIX_FORMAT, TextFormatUtils.formatSize(getCapacity(stack)))); + .append(new TranslationTextComponent(Constants.TOOLTIP_SUFFIX_FORMAT, TextFormatUtils.formatSize(getCapacity(stack)))); } } diff --git a/src/main/java/li/cil/oc2/common/item/RamItem.java b/src/main/java/li/cil/oc2/common/item/RamItem.java index 21e2a673..06b90205 100644 --- a/src/main/java/li/cil/oc2/common/item/RamItem.java +++ b/src/main/java/li/cil/oc2/common/item/RamItem.java @@ -44,7 +44,7 @@ public class RamItem extends Item { public ITextComponent getDisplayName(final ItemStack stack) { return new StringTextComponent("") .append(super.getDisplayName(stack)) - .append(new TranslationTextComponent(Constants.SUFFIX_FORMAT, TextFormatUtils.formatSize(getCapacity(stack)))); + .append(new TranslationTextComponent(Constants.TOOLTIP_SUFFIX_FORMAT, TextFormatUtils.formatSize(getCapacity(stack)))); } private static float getRamItemProperties(final ItemStack stack, final ClientWorld world, final LivingEntity entity) { 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 119a1d54..1cafeb33 100644 --- a/src/main/java/li/cil/oc2/common/network/Network.java +++ b/src/main/java/li/cil/oc2/common/network/Network.java @@ -1,10 +1,7 @@ package li.cil.oc2.common.network; import li.cil.oc2.api.API; -import li.cil.oc2.common.network.message.ComputerBusStateMessage; -import li.cil.oc2.common.network.message.ComputerRunStateMessage; -import li.cil.oc2.common.network.message.TerminalBlockInputMessage; -import li.cil.oc2.common.network.message.TerminalBlockOutputMessage; +import li.cil.oc2.common.network.message.*; import net.minecraft.util.ResourceLocation; import net.minecraft.world.chunk.Chunk; import net.minecraftforge.fml.network.NetworkDirection; @@ -52,6 +49,12 @@ public final class Network { .decoder(ComputerBusStateMessage::new) .consumer(ComputerBusStateMessage::handleMessage) .add(); + + INSTANCE.messageBuilder(ComputerBootErrorMessage.class, getNextPacketId(), NetworkDirection.PLAY_TO_CLIENT) + .encoder(ComputerBootErrorMessage::toBytes) + .decoder(ComputerBootErrorMessage::new) + .consumer(ComputerBootErrorMessage::handleMessage) + .add(); } public static void sendToClientsTrackingChunk(final T message, final Chunk chunk) { diff --git a/src/main/java/li/cil/oc2/common/network/message/ComputerBootErrorMessage.java b/src/main/java/li/cil/oc2/common/network/message/ComputerBootErrorMessage.java new file mode 100644 index 00000000..19583659 --- /dev/null +++ b/src/main/java/li/cil/oc2/common/network/message/ComputerBootErrorMessage.java @@ -0,0 +1,44 @@ +package li.cil.oc2.common.network.message; + +import li.cil.oc2.common.block.entity.ComputerTileEntity; +import li.cil.oc2.common.network.MessageUtils; +import net.minecraft.network.PacketBuffer; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.text.ITextComponent; +import net.minecraftforge.fml.network.NetworkEvent; + +import java.util.function.Supplier; + +public class ComputerBootErrorMessage { + private BlockPos pos; + private ITextComponent value; + + /////////////////////////////////////////////////////////////////// + + public ComputerBootErrorMessage(final ComputerTileEntity tileEntity) { + this.pos = tileEntity.getPos(); + this.value = tileEntity.getBootError(); + } + + public ComputerBootErrorMessage(final PacketBuffer buffer) { + fromBytes(buffer); + } + + /////////////////////////////////////////////////////////////////// + + public static boolean handleMessage(final ComputerBootErrorMessage message, final Supplier context) { + context.get().enqueueWork(() -> MessageUtils.withClientTileEntityAt(message.pos, ComputerTileEntity.class, + (tileEntity) -> tileEntity.setBootErrorClient(message.value))); + return true; + } + + public void fromBytes(final PacketBuffer buffer) { + pos = buffer.readBlockPos(); + value = buffer.readTextComponent(); + } + + public static void toBytes(final ComputerBootErrorMessage message, final PacketBuffer buffer) { + buffer.writeBlockPos(message.pos); + buffer.writeTextComponent(message.value); + } +} diff --git a/src/main/resources/assets/oc2/lang/en_us.json b/src/main/resources/assets/oc2/lang/en_us.json index 6a828814..c234c67c 100644 --- a/src/main/resources/assets/oc2/lang/en_us.json +++ b/src/main/resources/assets/oc2/lang/en_us.json @@ -11,5 +11,11 @@ "item.oc2.ram": "RAM", "item.oc2.hdd": "HDD", - "tooltip.oc2.suffix_format": " (%s)" + "tooltip.oc2.suffix_format": " (%s)", + + "gui.oc2.computer.boot_error.unknown": "Unknown Error", + "gui.oc2.computer.boot_error.no_ram": "Insufficient Memory", + "gui.oc2.computer.bus_state.incomplete": "Bus Incomplete", + "gui.oc2.computer.bus_state.too_complex": "Bus Too Complex", + "gui.oc2.computer.bus_state.multiple_controllers": "Multiple Bus Controllers" } \ No newline at end of file