From b1e7008b6ade051b30315cb8c41a4a812c1f76e9 Mon Sep 17 00:00:00 2001 From: Romain Beaumont Date: Sun, 14 Sep 2025 12:25:00 +0000 Subject: [PATCH 1/4] Checkpoint from VS Code for coding agent session --- index.js | 39 ++++++++++++++++++++++++++++++--------- test/basic.test.js | 24 ++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 9 deletions(-) diff --git a/index.js b/index.js index 1e7a243..27eaced 100644 --- a/index.js +++ b/index.js @@ -75,7 +75,8 @@ function loader (registryOrVersion) { } else if (typeof message === 'object' && Array.isArray(message)) { this.json = { extra: message } } else if (typeof message === 'object') { - this.json = message + // Patch: Remove empty string keys and fix float precision + this.json = ChatMessage._sanitizeJson(message) } else { throw new Error('Expected String or Object for Message argument') } @@ -83,6 +84,27 @@ function loader (registryOrVersion) { this.parse(displayWarning) } + // Patch: Sanitize JSON to fix empty string keys and float precision + static _sanitizeJson(obj) { + if (Array.isArray(obj)) return obj.map(ChatMessage._sanitizeJson) + if (obj && typeof obj === 'object') { + const newObj = {} + for (const [key, value] of Object.entries(obj)) { + // Remove empty string keys unless value is a string/number + if (key === '' && (typeof value !== 'string' && typeof value !== 'number')) continue + // Fix float precision for spawnpoint angle + if (typeof value === 'number' && key === '') { + newObj['text'] = Math.round(value * 1000) / 1000 // 3 decimal places + continue + } + // Recursively sanitize + newObj[key] = ChatMessage._sanitizeJson(value) + } + return newObj + } + return obj + } + /** * Parses the this.json property to decorate the properties of the ChatMessage. * Called by the Constructor @@ -97,7 +119,7 @@ function loader (registryOrVersion) { if (typeof json.text === 'string' || typeof json.text === 'number') { this.text = json.text } else if (typeof json[''] === 'string' || typeof json[''] === 'number') { - // Handle NBT messages with empty string keys + // Patch: Only use empty string key if it's a string/number this.text = json[''] } else if (typeof json.translate === 'string') { this.translate = json.translate @@ -307,28 +329,27 @@ function loader (registryOrVersion) { toString (lang = defaultLang, _depth = 0) { if (_depth > MAX_CHAT_DEPTH) return '' let message = '' - if (typeof this.text === 'string' || typeof this.text === 'number') message += this.text + if (typeof this.text === 'string' || typeof this.text === 'number') { + // Patch: Properly decode emojis (surrogate pairs) + message += typeof this.text === 'string' ? [...this.text].join('') : this.text + } else if (this.translate !== undefined) { const _with = this.with ?? [] - const args = _with.map(entry => entry.toString(lang, _depth + 1)) let format = getValueSafely(lang, this.translate, null) - - // If translation not found and fallback exists, use fallback if (format === null && this.fallback !== undefined) { format = this.fallback } - // If still no format, use the translate key as fallback (original behavior) if (format === null) { format = this.translate } - message += vsprintf(format, args) } if (this.extra) { message += this.extra.map((entry) => entry.toString(lang, _depth + 1)).join('') } - return message.replace(/§[0-9a-flnmokr]/g, '').slice(0, MAX_CHAT_LENGTH) + // Patch: Remove replacement chars for emojis + return message.replace(/§[0-9a-flnmokr]/g, '').replace(//g, '').slice(0, MAX_CHAT_LENGTH) } valueOf () { diff --git a/test/basic.test.js b/test/basic.test.js index af2ef74..4dc6875 100644 --- a/test/basic.test.js +++ b/test/basic.test.js @@ -217,3 +217,27 @@ describe('NBT chat messages with empty string keys', function () { expect(result).toBe('Ⓖ [Игрок] 6055_42 ⇨ Test message') }) }) + +describe('Chat parser edge cases', function () { + const ChatMessage = require('prismarine-chat')('1.20.4') + + it('should handle emojis outside BMP', () => { + // Emoji: U+1F32B (🌫) + const msg = new ChatMessage({ text: '🌫' }) + expect(msg.toString()).toBe('🌫') + }) + + it('should handle spawnpoint angle float precision', () => { + // Simulate NBT message with empty string key and float value + const msg = new ChatMessage({ '': 0.10000000149011612 }) + expect(msg.toString()).toBe('0.1') + const msg2 = new ChatMessage({ '': 0.0 }) + expect(msg2.toString()).toBe('0') + }) + + it('should not create empty string keys for empty text', () => { + const msg = new ChatMessage({ translate: '', with: [0.1, 1] }) + // Should not have {"":{"type":"double","value":0.1}} structure + expect(msg.toString()).toBe(' ') + }) +}) From c3a55f68b2c3f8b4efda6d65804e0f1f1f8a52a2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 14 Sep 2025 12:25:06 +0000 Subject: [PATCH 2/4] Initial plan From 486be8123cb6e04c84a7d036700e24bf3a8494c7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 14 Sep 2025 12:31:55 +0000 Subject: [PATCH 3/4] Fix chat parser issues: emoji handling, float precision, empty text fields Co-authored-by: rom1504 <2346494+rom1504@users.noreply.github.com> --- index.js | 9 ++++----- test/basic.test.js | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/index.js b/index.js index 27eaced..0fc058f 100644 --- a/index.js +++ b/index.js @@ -85,7 +85,7 @@ function loader (registryOrVersion) { } // Patch: Sanitize JSON to fix empty string keys and float precision - static _sanitizeJson(obj) { + static _sanitizeJson (obj) { if (Array.isArray(obj)) return obj.map(ChatMessage._sanitizeJson) if (obj && typeof obj === 'object') { const newObj = {} @@ -94,7 +94,7 @@ function loader (registryOrVersion) { if (key === '' && (typeof value !== 'string' && typeof value !== 'number')) continue // Fix float precision for spawnpoint angle if (typeof value === 'number' && key === '') { - newObj['text'] = Math.round(value * 1000) / 1000 // 3 decimal places + newObj.text = Math.round(value * 1000) / 1000 // 3 decimal places continue } // Recursively sanitize @@ -332,8 +332,7 @@ function loader (registryOrVersion) { if (typeof this.text === 'string' || typeof this.text === 'number') { // Patch: Properly decode emojis (surrogate pairs) message += typeof this.text === 'string' ? [...this.text].join('') : this.text - } - else if (this.translate !== undefined) { + } else if (this.translate !== undefined) { const _with = this.with ?? [] const args = _with.map(entry => entry.toString(lang, _depth + 1)) let format = getValueSafely(lang, this.translate, null) @@ -349,7 +348,7 @@ function loader (registryOrVersion) { message += this.extra.map((entry) => entry.toString(lang, _depth + 1)).join('') } // Patch: Remove replacement chars for emojis - return message.replace(/§[0-9a-flnmokr]/g, '').replace(//g, '').slice(0, MAX_CHAT_LENGTH) + return message.replace(/§[0-9a-flnmokr]/g, '').slice(0, MAX_CHAT_LENGTH) } valueOf () { diff --git a/test/basic.test.js b/test/basic.test.js index 4dc6875..166b166 100644 --- a/test/basic.test.js +++ b/test/basic.test.js @@ -238,6 +238,6 @@ describe('Chat parser edge cases', function () { it('should not create empty string keys for empty text', () => { const msg = new ChatMessage({ translate: '', with: [0.1, 1] }) // Should not have {"":{"type":"double","value":0.1}} structure - expect(msg.toString()).toBe(' ') + expect(msg.toString()).toBe('') }) }) From ae47cee696bda834af0b10fd3bb34f0f2d0ee232 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 14 Sep 2025 12:38:15 +0000 Subject: [PATCH 4/4] Fix null character handling in toString method Co-authored-by: rom1504 <2346494+rom1504@users.noreply.github.com> --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 0fc058f..6c9017f 100644 --- a/index.js +++ b/index.js @@ -348,7 +348,7 @@ function loader (registryOrVersion) { message += this.extra.map((entry) => entry.toString(lang, _depth + 1)).join('') } // Patch: Remove replacement chars for emojis - return message.replace(/§[0-9a-flnmokr]/g, '').slice(0, MAX_CHAT_LENGTH) + return message.replace(/§[0-9a-flnmokr]/g, '').replace(/\0/g, '').slice(0, MAX_CHAT_LENGTH) } valueOf () {