From 2718bc64c0c6a4a0b106a88a5b60d0ce46f3e8ff Mon Sep 17 00:00:00 2001 From: extremeheat Date: Sat, 3 Jun 2023 15:54:31 -0400 Subject: [PATCH] 1.19.4 (#1226) * Rename 'session' (packet) to chat_session_update to fix auth event conflict * impl packet "bundle" grouping, add client.writeBundle(packets) * fix handling, test * test 1.19.4 * 1.19.4 test ci * test ci against mcdata fork * lint * fix delim * fix 1.19.3 being skipped * Update ci.yml * Update package.json --------- Co-authored-by: Romain Beaumont --- .github/workflows/ci.yml | 2 +- .gitpod.yml | 2 +- package.json | 2 +- src/client.js | 34 ++++++++++++++++++----- src/client/play.js | 2 +- src/server/chat.js | 2 +- src/version.js | 4 +-- test/packetTest.js | 14 ++++++++-- test/serverTest.js | 60 ++++++++++++++++++++++++++++++++-------- 9 files changed, 94 insertions(+), 28 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4de44f8..97bc3e6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - mcVersion: ['1.7', '1.8', '1.9', '1.10', '1.11.2', '1.12.2', '1.13.2', '1.14.4', '1.15.2', '1.16.5', '1.17', '1.17.1', '1.18.2', '1.19', '1.19.2', '1.19.3'] + mcVersion: ['1.7', '1.8', '1.9', '1.10', '1.11.2', '1.12.2', '1.13.2', '1.14.4', '1.15.2', '1.16.5', '1.17', '1.17.1', '1.18.2', '1.19', '1.19.2', '1.19.3', '1.19.4'] fail-fast: false steps: diff --git a/.gitpod.yml b/.gitpod.yml index 38fc373..13f366c 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -1,2 +1,2 @@ tasks: -- command: npm install +- command: npm install && sdk install java diff --git a/package.json b/package.json index 7f0d7a5..4dfd986 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "endian-toggle": "^0.0.0", "lodash.get": "^4.1.2", "lodash.merge": "^4.3.0", - "minecraft-data": "^3.21.0", + "minecraft-data": "^3.34.0", "minecraft-folder-path": "^1.2.0", "node-fetch": "^2.6.1", "node-rsa": "^0.4.2", diff --git a/src/client.js b/src/client.js index 4146d6b..d716be1 100644 --- a/src/client.js +++ b/src/client.js @@ -1,5 +1,4 @@ 'use strict' - const EventEmitter = require('events').EventEmitter const debug = require('debug')('minecraft-protocol') const compression = require('./transforms/compression') @@ -30,8 +29,9 @@ class Client extends EventEmitter { this.latency = 0 this.hideErrors = hideErrors this.closeTimer = null - + const mcData = require('minecraft-data')(version) this.state = states.HANDSHAKING + this._hasBundlePacket = mcData.supportFeature('hasBundlePacket') } get state () { @@ -77,7 +77,13 @@ class Client extends EventEmitter { if (!this.compressor) { this.splitter.pipe(this.deserializer) } else { this.decompressor.pipe(this.deserializer) } this.emit('error', e) }) - + this._mcBundle = [] + const emitPacket = (parsed) => { + this.emit('packet', parsed.data, parsed.metadata, parsed.buffer, parsed.fullBuffer) + this.emit(parsed.metadata.name, parsed.data, parsed.metadata) + this.emit('raw.' + parsed.metadata.name, parsed.buffer, parsed.metadata) + this.emit('raw', parsed.buffer, parsed.metadata) + } this.deserializer.on('data', (parsed) => { parsed.metadata.name = parsed.data.name parsed.data = parsed.data.params @@ -87,10 +93,18 @@ class Client extends EventEmitter { const s = JSON.stringify(parsed.data, null, 2) debug(s && s.length > 10000 ? parsed.data : s) } - this.emit('packet', parsed.data, parsed.metadata, parsed.buffer, parsed.fullBuffer) - this.emit(parsed.metadata.name, parsed.data, parsed.metadata) - this.emit('raw.' + parsed.metadata.name, parsed.buffer, parsed.metadata) - this.emit('raw', parsed.buffer, parsed.metadata) + if (parsed.metadata.name === 'bundle_delimiter') { + if (this._mcBundle.length) { + this._mcBundle.forEach(emitPacket) + this._mcBundle = [] + } else { // Start bundle + this._mcBundle.push(parsed) + } + } else if (this._mcBundle.length) { + this._mcBundle.push(parsed) + } else { + emitPacket(parsed) + } }) } @@ -221,6 +235,12 @@ class Client extends EventEmitter { this.serializer.write({ name, params }) } + writeBundle (packets) { + if (this._hasBundlePacket) this.write('bundle_delimiter', {}) + for (const [name, params] of packets) this.write(name, params) + if (this._hasBundlePacket) this.write('bundle_delimiter', {}) + } + writeRaw (buffer) { const stream = this.compressor === null ? this.framer : this.compressor if (!stream.writable) { return } diff --git a/src/client/play.js b/src/client/play.js index 21d31a9..344ad9d 100644 --- a/src/client/play.js +++ b/src/client/play.js @@ -19,7 +19,7 @@ module.exports = function (client, options) { uuid: uuid.v4fast() } - client.write('session', { + client.write('chat_session_update', { sessionUUID: client._session.uuid, expireTime: client.profileKeys ? BigInt(client.profileKeys.expiresOn.getTime()) : undefined, publicKey: client.profileKeys ? client.profileKeys.public.export({ type: 'spki', format: 'der' }) : undefined, diff --git a/src/server/chat.js b/src/server/chat.js index a7b4daa..dc9dc3b 100644 --- a/src/server/chat.js +++ b/src/server/chat.js @@ -76,7 +76,7 @@ module.exports = function (client, server, options) { } } - client.on('session', (packet) => { + client.on('chat_session_update', (packet) => { client._session = { index: 0, uuid: packet.sessionUuid diff --git a/src/version.js b/src/version.js index 75cf507..d1c5ed8 100644 --- a/src/version.js +++ b/src/version.js @@ -1,6 +1,6 @@ 'use strict' module.exports = { - defaultVersion: '1.19.3', - supportedVersions: ['1.7', '1.8', '1.9', '1.10', '1.11.2', '1.12.2', '1.13.2', '1.14.4', '1.15.2', '1.16.5', '1.17.1', '1.18.2', '1.19', '1.19.2', '1.19.3'] + defaultVersion: '1.19.4', + supportedVersions: ['1.7', '1.8', '1.9', '1.10', '1.11.2', '1.12.2', '1.13.2', '1.14.4', '1.15.2', '1.16.5', '1.17.1', '1.18.2', '1.19', '1.19.2', '1.19.3', '1.19.4'] } diff --git a/test/packetTest.js b/test/packetTest.js index ae4ac5e..c4b4ba5 100644 --- a/test/packetTest.js +++ b/test/packetTest.js @@ -82,7 +82,7 @@ const values = { } Object.keys(typeArgs).forEach(function (index) { const v = typeArgs[index].name === 'type' && typeArgs[index].type === 'string' && typeArgs[2] !== undefined && - typeArgs[2].type !== undefined + typeArgs[2].type !== undefined ? (typeArgs[2].type[1].fields['minecraft:crafting_shapeless'] === undefined ? 'crafting_shapeless' : 'minecraft:crafting_shapeless') : getValue(typeArgs[index].type, results) if (typeArgs[index].anon) { @@ -96,6 +96,15 @@ const values = { delete results['..'] return results }, + vec3f: { + x: 0, y: 0, z: 0 + }, + vec3f64: { + x: 0, y: 0, z: 0 + }, + vec4f: { + x: 0, y: 0, z: 0, w: 0 + }, count: 1, // TODO : might want to set this to a correct value bool: true, f64: 99999.2222, @@ -266,8 +275,7 @@ for (const supportedVersion of mc.supportedVersions) { Object.keys(packets[state]).forEach(function (direction) { Object.keys(packets[state][direction].types) .filter(function (packetName) { - return packetName !== 'packet' && - packetName.startsWith('packet_') + return packetName !== 'packet' && packetName.startsWith('packet_') }) .forEach(function (packetName) { packetInfo = packets[state][direction].types[packetName] diff --git a/test/serverTest.js b/test/serverTest.js index 3683572..7a8a514 100644 --- a/test/serverTest.js +++ b/test/serverTest.js @@ -62,23 +62,23 @@ for (const supportedVersion of mc.supportedVersions) { // removed `dimension` // removed `dimensionCodec` registryCodec: { - "type": "compound", - "name": "", - "value": {} + type: 'compound', + name: '', + value: {} }, - worldType: "minecraft:overworld", + worldType: 'minecraft:overworld', death: undefined // more to be added } } - function sendBroadcastMessage(server, clients, message, sender) { + function sendBroadcastMessage (server, clients, message, sender) { if (mcData.supportFeature('signedChat')) { server.writeToClients(clients, 'player_chat', { plainMessage: message, signedChatContent: '', unsignedChatContent: JSON.stringify({ text: message }), - type: 0, + type: 0, senderUuid: 'd3527a0b-bc03-45d5-a878-2aafdd8c8a43', // random senderName: JSON.stringify({ text: sender }), senderTeam: undefined, @@ -96,7 +96,7 @@ for (const supportedVersion of mc.supportedVersions) { describe('mc-server ' + version.minecraftVersion, function () { this.timeout(5000) - this.beforeAll(async function() { + this.beforeAll(async function () { PORT = await getPort() console.log(`Using port for tests: ${PORT}`) }) @@ -214,7 +214,7 @@ for (const supportedVersion of mc.supportedVersions) { sample: [] }, description: { - extra: [ { color: 'red', text: 'Red text' } ], + extra: [{ color: 'red', text: 'Red text' }], bold: true, text: 'Example chat mesasge' } @@ -278,7 +278,7 @@ for (const supportedVersion of mc.supportedVersions) { version: version.minecraftVersion, port: PORT }) - client.on('packet', (data, {name})=>{ + client.on('packet', (data, { name }) => { if (name === 'success') { assert.strictEqual(data.uuid, notchUUID, 'UUID') server.close() @@ -333,7 +333,7 @@ for (const supportedVersion of mc.supportedVersions) { })) const p1Join = await player1.nextMessage('player2') - + assert.strictEqual(p1Join, '{"text":"player2 joined the game."}') player2.chat('hi') @@ -441,7 +441,7 @@ for (const supportedVersion of mc.supportedVersions) { sendBroadcastMessage(server, Object.values(server.clients), 'A message from the server.') - let results = await Promise.all([player1.nextMessage(), player2.nextMessage()]) + const results = await Promise.all([player1.nextMessage(), player2.nextMessage()]) for (const msg of results) { assert.strictEqual(msg, '{"text":"A message from the server."}') } @@ -452,5 +452,43 @@ for (const supportedVersion of mc.supportedVersions) { server.close() }) }) + + it('supports bundle packet', function (done) { + const server = mc.createServer({ + 'online-mode': false, + version: version.minecraftVersion, + port: PORT + }) + server.on('login', function (client) { + client.on('end', function (reason) { + assert.strictEqual(reason, 'ServerShutdown') + }) + client.write('login', loginPacket(client, server)) + client.writeBundle([ + ['update_time', { age: 1, time: 2 }], + ['close_window', { windowId: 0 }] + ]) + }) + server.on('close', done) + server.on('listening', function () { + const client = mc.createClient({ + username: 'lalalal', + host: '127.0.0.1', + version: version.minecraftVersion, + port: PORT + }) + client.on('update_time', function () { + // Below handler synchronously defined should be guaranteed to be called after the above one + const d1 = Date.now() + client.on('close_window', function () { + server.close() + const d2 = Date.now() + if (mcData.supportFeature('hasBundlePacket') && (d2 - d1) > 1) { + throw new Error(`bundle packet constituents did not arrive at once : ${d1}, ${d2}`) + } + }) + }) + }) + }) }) }