From 8e131c359ebd5509136fd849a82cc59cd0dc1e58 Mon Sep 17 00:00:00 2001 From: extremeheat Date: Mon, 6 Jan 2025 13:47:44 -0500 Subject: [PATCH] Fix server_data payload for 1.19+, fix kicks messages on 1.20.3+ (#1364) * Fix server_data payload sent to clients for versions 1.19+ Fixes #1362 Add missing fields to `server_data` payload for versions 1.19+. * Add `motd` and `icon` fields to `server_data` payload for version 1.19. * Add `motd`, `icon`, and `enforcesSecureChat` fields to `server_data` payload for version 1.19.2. * Add `motd`, `icon`, and `enforcesSecureChat` fields to `server_data` payload for version 1.19.3. * Add `motd`, `iconBytes`, and `enforcesSecureChat` fields to `server_data` payload for versions 1.19.4, 1.20, and 1.20.2. * Add `motd`, `iconBytes`, and `enforcesSecureChat` fields to `server_data` payload for version 1.20.3. * Add `motd` and `iconBytes` fields to `server_data` payload for version 1.20.5+. * Use NBT components for `motd` if `chatPacketsUseNbtComponents` feature is supported. * Convert `favicon` to buffer for `iconBytes` field if available. --- For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/PrismarineJS/node-minecraft-protocol/issues/1362?shareId=XXXX-XXXX-XXXX-XXXX). * add --retries 2 * lint, debug close event emit twice * bail tests * fix * flaky test fix * remove debug * Update serverTest.js * Fix NBT chat not being used for 1.20.3+ kicks * Update createClient.js * fix client.._supportFeature not being defined * only nbt on the play state disconnect --- src/client.js | 1 + src/createServer.js | 1 + src/server.js | 8 ++++++-- src/server/login.js | 6 ++++++ test/serverTest.js | 15 +++++++++++++-- 5 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/client.js b/src/client.js index 5b63c2950..74749698f 100644 --- a/src/client.js +++ b/src/client.js @@ -30,6 +30,7 @@ class Client extends EventEmitter { this.hideErrors = hideErrors this.closeTimer = null const mcData = require('minecraft-data')(version) + this._supportFeature = mcData.supportFeature this.state = states.HANDSHAKING this._hasBundlePacket = mcData.supportFeature('hasBundlePacket') } diff --git a/src/createServer.js b/src/createServer.js index 4a56c0ad9..50ae0a38c 100644 --- a/src/createServer.js +++ b/src/createServer.js @@ -46,6 +46,7 @@ function createServer (options = {}) { server.onlineModeExceptions = Object.create(null) server.favicon = favicon server.options = options + server._supportFeature = mcData.supportFeature options.registryCodec = options.registryCodec || mcData.registryCodec || mcData.loginPacket?.dimensionCodec // The RSA keypair can take some time to generate diff --git a/src/server.js b/src/server.js index 99cfcbac2..46df10422 100644 --- a/src/server.js +++ b/src/server.js @@ -4,6 +4,7 @@ const net = require('net') const EventEmitter = require('events').EventEmitter const Client = require('./client') const states = require('./states') +const nbt = require('prismarine-nbt') const { createSerializer } = require('./transforms/serializer') class Server extends EventEmitter { @@ -26,11 +27,14 @@ class Server extends EventEmitter { self.socketServer.on('connection', socket => { const client = new Client(true, this.version, this.customPackets, this.hideErrors) client._end = client.end - client.end = function end (endReason, fullReason = JSON.stringify({ text: endReason })) { + client.end = function end (endReason, fullReason) { if (client.state === states.PLAY) { + fullReason ||= this._supportFeature('chatPacketsUseNbtComponents') + ? nbt.comp({ text: nbt.string(endReason) }) + : JSON.stringify({ text: endReason }) client.write('kick_disconnect', { reason: fullReason }) } else if (client.state === states.LOGIN) { - client.write('disconnect', { reason: fullReason }) + client.write('disconnect', { reason: fullReason || endReason }) } client._end(endReason) } diff --git a/src/server/login.js b/src/server/login.js index 858b952ca..bc5785114 100644 --- a/src/server/login.js +++ b/src/server/login.js @@ -7,6 +7,7 @@ const chatPlugin = require('./chat') const { concat } = require('../transforms/binaryStream') const { mojangPublicKeyPem } = require('./constants') const debug = require('debug')('minecraft-protocol') +const nbt = require('prismarine-nbt') /** * @param {import('../index').Client} client @@ -196,7 +197,12 @@ module.exports = function (client, server, options) { client.settings = {} if (client.supportFeature('chainedChatWithHashing')) { // 1.19.1+ + const jsonMotd = JSON.stringify(server.motdMsg ?? { text: server.motd }) + const nbtMotd = nbt.comp({ text: nbt.string(server.motd) }) client.write('server_data', { + motd: client.supportFeature('chatPacketsUseNbtComponents') ? nbtMotd : jsonMotd, + icon: server.favicon, // b64 + iconBytes: server.favicon ? Buffer.from(server.favicon, 'base64') : undefined, previewsChat: options.enableChatPreview, // Note: in 1.20.5+ user must send this with `login` enforcesSecureChat: options.enforceSecureProfile diff --git a/test/serverTest.js b/test/serverTest.js index 5837e7656..197603422 100644 --- a/test/serverTest.js +++ b/test/serverTest.js @@ -102,6 +102,7 @@ for (const supportedVersion of mc.supportedVersions) { describe('mc-server ' + supportedVersion + 'v', function () { this.timeout(5000) this.beforeEach(async function () { + console.log('🔻 Starting test', this.currentTest.title) PORT = await getPort() console.log(`Using port for tests: ${PORT}`) }) @@ -411,9 +412,12 @@ for (const supportedVersion of mc.supportedVersions) { }) }) function checkFinish () { - if (serverPlayerDisconnected && clientClosed && serverClosed) done() + if (serverPlayerDisconnected && clientClosed && serverClosed) { + console.log('Kick test is done') + callOnce(done) + } } - }) + }).retries(2) it('gives correct reason for kicking clients when shutting down', function (done) { const server = mc.createServer({ @@ -532,3 +536,10 @@ for (const supportedVersion of mc.supportedVersions) { }) }) } + +function callOnce (fn, ...args) { + console.log('Call Fn', fn.called) + if (fn.called) return + fn(...args) + fn.called = true +}