From 468f942c4984578a03e472c49646198680489723 Mon Sep 17 00:00:00 2001 From: LabyStudio Date: Fri, 26 Jan 2024 13:25:53 +0100 Subject: [PATCH] improve performance of chat rendering --- src/js/net/minecraft/client/GameWindow.js | 8 +++ src/js/net/minecraft/client/Minecraft.js | 6 +-- .../client/command/command/TeleportCommand.js | 2 +- .../client/gui/overlay/ChatOverlay.js | 25 ++++++++- .../client/gui/overlay/IngameOverlay.js | 11 +++- .../minecraft/client/gui/screens/GuiChat.js | 9 +++- .../client/network/NetworkManager.js | 2 +- .../play/server/ServerJoinGamePacket.js | 2 +- .../client/render/gui/FontRenderer.js | 51 ++++++++++++++++++- 9 files changed, 103 insertions(+), 13 deletions(-) diff --git a/src/js/net/minecraft/client/GameWindow.js b/src/js/net/minecraft/client/GameWindow.js index 748d083..30731c5 100644 --- a/src/js/net/minecraft/client/GameWindow.js +++ b/src/js/net/minecraft/client/GameWindow.js @@ -52,6 +52,7 @@ export default class GameWindow { // Create render layers this.canvasWorld = document.createElement('canvas'); this.canvasDebug = document.createElement('canvas'); + this.canvasChat = document.createElement('canvas'); this.canvasPlayerList = document.createElement('canvas'); this.canvasItems = document.createElement('canvas'); @@ -381,6 +382,11 @@ export default class GameWindow { this.canvasDebug.height = this.canvas.height; } + if (this.canvasChat.width !== this.canvas.width || this.canvasChat.height !== this.canvas.height) { + this.canvasChat.width = this.canvas.width; + this.canvasChat.height = this.canvas.height; + } + if (this.canvasPlayerList.width !== this.canvas.width || this.canvasPlayerList.height !== this.canvas.height) { this.canvasPlayerList.width = this.canvas.width; this.canvasPlayerList.height = this.canvas.height; @@ -394,6 +400,8 @@ export default class GameWindow { this.minecraft.currentScreen.setup(this.minecraft, this.width, this.height); } + this.minecraft.ingameOverlay.chatOverlay.setDirty(); + // Render first frame if (this.minecraft.isInGame()) { this.minecraft.worldRenderer.render(0); diff --git a/src/js/net/minecraft/client/Minecraft.js b/src/js/net/minecraft/client/Minecraft.js index 74578d2..930731a 100644 --- a/src/js/net/minecraft/client/Minecraft.js +++ b/src/js/net/minecraft/client/Minecraft.js @@ -32,8 +32,7 @@ export default class Minecraft { // TODO Add to settings static PROXY = { - "address": "localhost", - "port": 30023 + "url": "wss://socket.labystudio.de/minecraft/" }; /** @@ -350,7 +349,8 @@ export default class Minecraft { // Open chat if (button === this.settings.keyOpenChat) { - this.displayScreen(new GuiChat()); + this.displayScreen(new GuiChat(this)); + this.ingameOverlay.chatOverlay.setDirty(); } // Toggle debug overlay diff --git a/src/js/net/minecraft/client/command/command/TeleportCommand.js b/src/js/net/minecraft/client/command/command/TeleportCommand.js index c284a16..2258331 100644 --- a/src/js/net/minecraft/client/command/command/TeleportCommand.js +++ b/src/js/net/minecraft/client/command/command/TeleportCommand.js @@ -3,7 +3,7 @@ import Command from "../Command.js"; export default class TeleportCommand extends Command { constructor() { - super("tp", " ", "Teleport to a position") + super("tp", " ", "Teleport to a position") } execute(minecraft, args) { diff --git a/src/js/net/minecraft/client/gui/overlay/ChatOverlay.js b/src/js/net/minecraft/client/gui/overlay/ChatOverlay.js index 2d2f903..8625384 100644 --- a/src/js/net/minecraft/client/gui/overlay/ChatOverlay.js +++ b/src/js/net/minecraft/client/gui/overlay/ChatOverlay.js @@ -8,8 +8,11 @@ export default class ChatOverlay extends Gui { constructor(minecraft) { super(minecraft); + this.chatWidth = 320; + this.messages = []; this.sentHistory = []; + this.dirty = true; } render(stack, mouseX, mouseY, partialTicks) { @@ -30,25 +33,43 @@ export default class ChatOverlay extends Gui { if (alpha > 0) { let y = this.minecraft.window.height - 40 - i * 9; - this.drawRect(stack, 2, y - 1, 2 + 320, y + 8, '#000000', alpha / 2 / 255); + this.drawRect(stack, 2, y - 1, 2 + this.chatWidth, y + 8, '#000000', alpha / 2 / 255); this.drawString(stack, message.message, 2, y, 0xffffff + (alpha << 24)); } } + this.dirty = false; } onTick() { for (let i = 0; i < this.messages.length; i++) { let message = this.messages[i]; message.updateCounter++; + + // Fade out animation + if (message.updateCounter >= 180 && message.updateCounter <= 200) { + this.dirty = true; + } } } addMessage(message) { - this.messages.splice(0, 0, new ChatLine(message)); + for (let line of this.minecraft.fontRenderer.listFormattedStringToWidth(message, this.chatWidth)) { + this.messages.splice(0, 0, new ChatLine(line)); + } + this.dirty = true; } addMessageToSentHistory(message) { this.sentHistory.splice(0, 0, message); + this.dirty = true; + } + + isDirty() { + return this.dirty; + } + + setDirty() { + this.dirty = true; } } \ No newline at end of file diff --git a/src/js/net/minecraft/client/gui/overlay/IngameOverlay.js b/src/js/net/minecraft/client/gui/overlay/IngameOverlay.js index a4b4eb3..53fbb88 100644 --- a/src/js/net/minecraft/client/gui/overlay/IngameOverlay.js +++ b/src/js/net/minecraft/client/gui/overlay/IngameOverlay.js @@ -34,8 +34,8 @@ export default class IngameOverlay extends Gui { // Render hotbar this.renderHotbar(stack, this.window.width / 2 - 91, this.window.height - 22); - // Render chat - this.chatOverlay.render(stack, mouseX, mouseY, partialTicks); + // Render chat canvas + stack.drawImage(this.window.canvasChat, 0, 0); // Render debug canvas on stack if (this.minecraft.settings.debugOverlay) { @@ -70,6 +70,13 @@ export default class IngameOverlay extends Gui { this.ticksRendered++; } + + // Render chat on tick if dirty + if (this.chatOverlay.isDirty()) { + let stack = this.window.canvasChat.getContext('2d'); + stack.clearRect(0, 0, this.window.width, this.window.height); + this.chatOverlay.render(stack, 0, 0, 0); + } } renderCrosshair(stack, x, y) { diff --git a/src/js/net/minecraft/client/gui/screens/GuiChat.js b/src/js/net/minecraft/client/gui/screens/GuiChat.js index ed44790..d71c2c9 100644 --- a/src/js/net/minecraft/client/gui/screens/GuiChat.js +++ b/src/js/net/minecraft/client/gui/screens/GuiChat.js @@ -3,9 +3,11 @@ import GuiTextField from "../widgets/GuiTextField.js"; export default class GuiChat extends GuiScreen { - constructor() { + constructor(minecraft) { super(); + this.minecraft = minecraft; + this.inputField = new GuiTextField(0, 0, 0, 0); this.inputField.renderBackground = false; @@ -24,6 +26,11 @@ export default class GuiChat extends GuiScreen { this.buttonList.push(this.inputField); } + onClose() { + super.onClose(); + this.minecraft.ingameOverlay.chatOverlay.setDirty(); + } + drawScreen(stack, mouseX, mouseY, partialTicks) { this.drawRect(stack, 2, this.height - 14, this.width - 2, this.height - 2, '#000000', 0.5); diff --git a/src/js/net/minecraft/client/network/NetworkManager.js b/src/js/net/minecraft/client/network/NetworkManager.js index edd5a79..43637ac 100644 --- a/src/js/net/minecraft/client/network/NetworkManager.js +++ b/src/js/net/minecraft/client/network/NetworkManager.js @@ -31,7 +31,7 @@ export default class NetworkManager { } connect(address, port, proxy) { - this.socket = new WebSocket("ws://" + proxy.address + ":" + proxy.port); + this.socket = new WebSocket(proxy.url); this.socket.binaryType = "arraybuffer"; this.socket.onopen = e => this._onOpen(e); diff --git a/src/js/net/minecraft/client/network/packet/play/server/ServerJoinGamePacket.js b/src/js/net/minecraft/client/network/packet/play/server/ServerJoinGamePacket.js index e04bd4b..32a480f 100644 --- a/src/js/net/minecraft/client/network/packet/play/server/ServerJoinGamePacket.js +++ b/src/js/net/minecraft/client/network/packet/play/server/ServerJoinGamePacket.js @@ -20,7 +20,7 @@ export default class ServerJoinGamePacket extends Packet { } read(buffer) { - this.entityId = buffer.readVarInt(); + this.entityId = buffer.readInt(); let bits = buffer.readByte(); this.hardcoreMode = (bits & 8) === 8; this.gameType = bits & -9; diff --git a/src/js/net/minecraft/client/render/gui/FontRenderer.js b/src/js/net/minecraft/client/render/gui/FontRenderer.js index ce8c538..7fa6afe 100644 --- a/src/js/net/minecraft/client/render/gui/FontRenderer.js +++ b/src/js/net/minecraft/client/render/gui/FontRenderer.js @@ -140,7 +140,6 @@ export default class FontRenderer { return length; } - createBitMap(img) { let canvas = document.createElement('canvas'); canvas.width = img.width; @@ -178,6 +177,54 @@ export default class FontRenderer { } listFormattedStringToWidth(text, wrapWidth) { - return text.split("\n"); // TODO Implement wrap logic + let lines = []; + let currentColorCharacter = "r"; + for (let line of text.split('\n')) { + let currentLine = ''; + let currentLineWidth = 0; + + // Split the text into words + for (let word of line.split(' ')) { + const wordWidth = this.getStringWidth(word + " "); + + // If adding the word exceeds the wrap width, start a new line + if (currentLineWidth + wordWidth > wrapWidth) { + lines.push(FontRenderer.COLOR_PREFIX + currentColorCharacter + currentLine.trim()); + currentColorCharacter = this.getLastColorCharacterOfText(currentLine); + + currentLine = ''; + currentLineWidth = 0; + } + + // Add the word to the current line + currentLine += word + ' '; + currentLineWidth += wordWidth; + } + + // Push the last line + if (currentLine.length > 0) { + lines.push(FontRenderer.COLOR_PREFIX + currentColorCharacter + currentLine.trim()); + currentColorCharacter = this.getLastColorCharacterOfText(currentLine); + } + } + return lines; + } + + getLastColorCharacterOfText(text) { + let character = "r"; + let isColorCode = false; + + // For each character + for (let i = 0; i < text.length; i++) { + if (text[i] === FontRenderer.COLOR_PREFIX) { + isColorCode = true; + } else { + if (isColorCode) { + character = text[i]; + isColorCode = false; + } + } + } + return character; } } \ No newline at end of file