diff --git a/index.js b/index.js index 1e7a243..6c9017f 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,26 @@ 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 - else if (this.translate !== undefined) { + 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(/\0/g, '').slice(0, MAX_CHAT_LENGTH) } valueOf () { diff --git a/test/basic.test.js b/test/basic.test.js index af2ef74..166b166 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('') + }) +})