diff --git a/HISTORY.md b/HISTORY.md index 9dfb2d4..799d0a9 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,3 +1,28 @@ +## 3.49.0 +* [1.21.111 (#649)](https://github.com/PrismarineJS/bedrock-protocol/commit/b48518a6e79e72101fe7136433cbd6277339fc5c) (thanks @Slauh) +* [Skin Data Changes (#647)](https://github.com/PrismarineJS/bedrock-protocol/commit/407756b93880cdda4fdbff194fc4163ceedf4e82) (thanks @thejfkvis) + +## 3.48.1 +* [Update login client skinData (#635)](https://github.com/PrismarineJS/bedrock-protocol/commit/6b1474d2c6f93b47dee9d4816de59579f82ed5a9) (thanks @TSL534) + +## 3.48.0 +* [1.21.100 (#632)](https://github.com/PrismarineJS/bedrock-protocol/commit/06fb3de3a0023d03201dbcee7e9178c269462766) (thanks @extremeheat) + +## 3.47.0 +* [1.21.93 support (#623)](https://github.com/PrismarineJS/bedrock-protocol/commit/14daa2d95aac90ffcc7b42d625e270020ec2f162) (thanks @CreeperG16) + +## 3.46.0 +* [1.21.90 support (#617)](https://github.com/PrismarineJS/bedrock-protocol/commit/c66cdd3d62d2fa9c581693d8c70d7b41f355b63e) (thanks @CreeperG16) + +## 3.45.0 +* [1.21.80 (#602)](https://github.com/PrismarineJS/bedrock-protocol/commit/e71fd513ddbd432983f221980080b61e11576965) (thanks @extremeheat) + +## 3.44.0 +* [1.21.70 (#594)](https://github.com/PrismarineJS/bedrock-protocol/commit/065f41db8cfc8cbd8106bd9e376c899ec25f3f77) (thanks @CreeperG16) + +## 3.43.1 +* [Fix server not correctly removing clients (#588)](https://github.com/PrismarineJS/bedrock-protocol/commit/47f342ca958ba87a7719783bd5c855cebdd4aa65) (thanks @EntifiedOptics) + ## 3.43.0 * [1.21.60 support (#570)](https://github.com/PrismarineJS/bedrock-protocol/commit/eeb5e47e35f31cc571a9a8a491f5a89b27e637f1) (thanks @CreeperG16) * [Fix version feature handling (#572)](https://github.com/PrismarineJS/bedrock-protocol/commit/0ed8e32be85f05926cd97d5f0317ed004ae5eefa) (thanks @ItsMax123) diff --git a/README.md b/README.md index cb2dcd4..5e3add2 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Minecraft Bedrock Edition (aka MCPE) protocol library, supporting authentication ## Features - - Supports Minecraft Bedrock version 1.16.201, 1.16.210, 1.16.220, 1.17.0, 1.17.10, 1.17.30, 1.17.40, 1.18.0, 1.18.11, 1.18.30, 1.19.1, 1.19.10, 1.19.20, 1.19.21, 1.19.30, 1.19.40, 1.19.41, 1.19.50, 1.19.60, 1.19.62, 1.19.63, 1.19.70, 1.19.80, 1.20.0, 1.20.10, 1.20.30, 1.20.40, 1.20.50, 1.20.61, 1.20.71, 1.20.80, 1.21.0, 1.21.2, 1.21.21, 1.21.30, 1.21.42, 1.21.50, 1.21.60 + - Supports Minecraft Bedrock version 1.16.201, 1.16.210, 1.16.220, 1.17.0, 1.17.10, 1.17.30, 1.17.40, 1.18.0, 1.18.11, 1.18.30, 1.19.1, 1.19.10, 1.19.20, 1.19.21, 1.19.30, 1.19.40, 1.19.41, 1.19.50, 1.19.60, 1.19.62, 1.19.63, 1.19.70, 1.19.80, 1.20.0, 1.20.10, 1.20.30, 1.20.40, 1.20.50, 1.20.61, 1.20.71, 1.20.80, 1.21.0, 1.21.2, 1.21.21, 1.21.30, 1.21.42, 1.21.50, 1.21.60, 1.21.70, 1.21.80, 1.21.90, 1.21.93, 1.21.100, 1.21.111 - Parse and serialize packets as JavaScript objects - Automatically respond to keep-alive packets - [Proxy and mitm connections](docs/API.md#proxy-docs) diff --git a/index.d.ts b/index.d.ts index 581e87a..3f4a571 100644 --- a/index.d.ts +++ b/index.d.ts @@ -3,7 +3,7 @@ import { Realm } from 'prismarine-realms' import { ServerDeviceCodeResponse } from 'prismarine-auth' declare module 'bedrock-protocol' { - type Version = '1.21.60' | '1.21.50' | '1.21.42' | '1.21.30' | '1.21.2' | '1.21.0' | '1.20.80' | '1.20.71' | '1.20.61' | '1.20.50' | '1.20.40' | '1.20.30' | '1.20.10' | '1.20.0' | '1.19.80' | '1.19.70' | '1.19.63' | '1.19.62' | '1.19.60' | '1.19.51' | '1.19.50' | '1.19.41' | '1.19.40' | '1.19.31' | '1.19.30' | '1.19.22' | '1.19.21' | '1.19.20' | '1.19.11' | '1.19.10' | '1.19.2' | '1.19.1' | '1.18.31' | '1.18.30' | '1.18.12' | '1.18.11' | '1.18.10' | '1.18.2' | '1.18.1' | '1.18.0' | '1.17.41' | '1.17.40' | '1.17.34' | '1.17.30' | '1.17.11' | '1.17.10' | '1.17.0' | '1.16.220' | '1.16.210' | '1.16.201' + type Version = '1.21.93' | '1.21.90' | '1.21.80' | '1.21.70' | '1.21.60' | '1.21.50' | '1.21.42' | '1.21.30' | '1.21.2' | '1.21.0' | '1.20.80' | '1.20.71' | '1.20.61' | '1.20.50' | '1.20.40' | '1.20.30' | '1.20.10' | '1.20.0' | '1.19.80' | '1.19.70' | '1.19.63' | '1.19.62' | '1.19.60' | '1.19.51' | '1.19.50' | '1.19.41' | '1.19.40' | '1.19.31' | '1.19.30' | '1.19.22' | '1.19.21' | '1.19.20' | '1.19.11' | '1.19.10' | '1.19.2' | '1.19.1' | '1.18.31' | '1.18.30' | '1.18.12' | '1.18.11' | '1.18.10' | '1.18.2' | '1.18.1' | '1.18.0' | '1.17.41' | '1.17.40' | '1.17.34' | '1.17.30' | '1.17.11' | '1.17.10' | '1.17.0' | '1.16.220' | '1.16.210' | '1.16.201' export interface Options { // The string version to start the client or server as diff --git a/package.json b/package.json index b5a34cc..6301be0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bedrock-protocol", - "version": "3.43.0", + "version": "3.49.0", "description": "Minecraft Bedrock Edition protocol library", "main": "index.js", "types": "index.d.ts", diff --git a/src/client.js b/src/client.js index 472a777..e3af1d0 100644 --- a/src/client.js +++ b/src/client.js @@ -62,7 +62,8 @@ class Client extends Connection { const mcData = require('minecraft-data')('bedrock_' + this.options.version) this.features = { compressorInHeader: mcData.supportFeature('compressorInPacketHeader'), - itemRegistryPacket: mcData.supportFeature('itemRegistryPacket') + itemRegistryPacket: mcData.supportFeature('itemRegistryPacket'), + newLoginIdentityFields: mcData.supportFeature('newLoginIdentityFields') } } catch (e) { throw new Error(`Unsupported version: '${this.options.version}', no data available`) @@ -146,9 +147,18 @@ class Client extends Connection { ...this.accessToken // Mojang + Xbox JWT from auth ] - const encodedChain = JSON.stringify({ chain }) - - debug('Auth chain', chain) + let encodedChain + if (this.features.newLoginIdentityFields) { // 1.21.90+ + encodedChain = JSON.stringify({ + Certificate: JSON.stringify({ chain }), + // 0 = normal, 1 = ss, 2 = offline + AuthenticationType: this.options.offline ? 2 : 0, + Token: '' + }) + } else { + encodedChain = JSON.stringify({ chain }) + } + debug('Auth chain', encodedChain) this.write('login', { protocol_version: this.options.protocolVersion, diff --git a/src/handshake/login.js b/src/handshake/login.js index 4523a50..8a2830d 100644 --- a/src/handshake/login.js +++ b/src/handshake/login.js @@ -36,7 +36,6 @@ module.exports = (client, server, options) => { client.createClientUserChain = (privateKey) => { let payload = { ...skinData, - SkinGeometryDataEngineVersion: client.versionGreaterThanOrEqualTo('1.17.30') ? '' : undefined, ClientRandomId: Date.now(), CurrentInputMode: 1, @@ -47,19 +46,20 @@ module.exports = (client, server, options) => { GameVersion: options.version || '1.16.201', GuiScale: -1, LanguageCode: 'en_GB', // TODO locale + GraphicsMode: 1, // 1:simple, 2:fancy, 3:advanced, 4:ray_traced PlatformOfflineId: '', PlatformOnlineId: '', // chat // PlayFabID is the PlayFab ID produced for the skin. PlayFab is the company that hosts the Marketplace, // skins and other related features from the game. This ID is the ID of the skin used to store the skin - // inside of PlayFab. - PlayFabId: nextUUID().replace(/-/g, '').slice(0, 16), // 1.16.210 + // inside of PlayFab.The playfab ID is always lowercased. + PlayFabId: nextUUID().replace(/-/g, '').slice(0, 16).toLowerCase(), // 1.16.210 SelfSignedId: nextUUID(), ServerAddress: `${options.host}:${options.port}`, - ThirdPartyName: client.profile.name, - ThirdPartyNameOnly: false, + ThirdPartyName: client.profile.name, // Gamertag + ThirdPartyNameOnly: client.versionGreaterThanOrEqualTo('1.21.90') ? undefined : false, UIProfile: 0, IsEditorMode: false, diff --git a/src/options.js b/src/options.js index 8eb5e0b..6e27dbe 100644 --- a/src/options.js +++ b/src/options.js @@ -3,12 +3,12 @@ const mcData = require('minecraft-data') // Minimum supported version (< will be kicked) const MIN_VERSION = '1.16.201' // Currently supported verson. Note, clients with newer versions can still connect as long as data is in minecraft-data -const CURRENT_VERSION = '1.21.60' +const CURRENT_VERSION = '1.21.111' const Versions = Object.fromEntries(mcData.versions.bedrock.filter(e => e.releaseType === 'release').map(e => [e.minecraftVersion, e.version])) // Skip some low priority versions (middle major) on Github Actions to allow faster CI -const skippedVersionsOnGithubCI = ['1.16.210', '1.17.10', '1.17.30', '1.18.11', '1.19.10', '1.19.20', '1.19.30', '1.19.40', '1.19.50', '1.19.60', '1.19.63', '1.19.70', '1.20.10', '1.20.15', '1.20.30', '1.20.40', '1.20.50', '1.20.61', '1.20.71'] +const skippedVersionsOnGithubCI = ['1.16.210', '1.17.10', '1.17.30', '1.18.11', '1.19.10', '1.19.20', '1.19.30', '1.19.40', '1.19.50', '1.19.60', '1.19.63', '1.19.70', '1.20.10', '1.20.15', '1.20.30', '1.20.40', '1.20.50', '1.20.61', '1.20.71', '1.21.2', '1.21.20', '1.21.30', '1.21.42', '1.21.50', '1.21.60', '1.21.70', '1.21.80', '1.21.90'] const testedVersions = process.env.CI ? Object.keys(Versions).filter(v => !skippedVersionsOnGithubCI.includes(v)) : Object.keys(Versions) const defaultOptions = { diff --git a/src/server.js b/src/server.js index 4dc9852..0b43fd2 100644 --- a/src/server.js +++ b/src/server.js @@ -33,7 +33,8 @@ class Server extends EventEmitter { try { const mcData = require('minecraft-data')('bedrock_' + version) this.features = { - compressorInHeader: mcData.supportFeature('compressorInPacketHeader') + compressorInHeader: mcData.supportFeature('compressorInPacketHeader'), + newLoginIdentityFields: mcData.supportFeature('newLoginIdentityFields') } } catch (e) { throw new Error(`Unsupported version: '${version}', no data available`) @@ -89,12 +90,10 @@ class Server extends EventEmitter { this.emit('connect', player) } - onCloseConnection = (inetAddr, reason) => { - this.conLog('Connection closed: ', inetAddr?.address, reason) - - delete this.clients[inetAddr]?.connection // Prevent close loop - this.clients[inetAddr?.address ?? inetAddr]?.close() - delete this.clients[inetAddr] + onCloseConnection = (conn, reason) => { + this.conLog('Connection closed: ', conn.address, reason) + this.clients[conn.address]?.close() + delete this.clients[conn.address] this.clientCount-- } diff --git a/src/serverPlayer.js b/src/serverPlayer.js index b7ef02d..2e8ea77 100644 --- a/src/serverPlayer.js +++ b/src/serverPlayer.js @@ -78,11 +78,18 @@ class Player extends Connection { // Parse login data const tokens = body.params.tokens - const authChain = JSON.parse(tokens.identity) - const skinChain = tokens.client - try { - var { key, userData, skinData } = this.decodeLoginJWT(authChain.chain, skinChain) // eslint-disable-line + const skinChain = tokens.client + const authChain = JSON.parse(tokens.identity) + let chain + if (authChain.Certificate) { // 1.21.90+ + chain = JSON.parse(authChain.Certificate).chain + } else if (authChain.chain) { + chain = authChain.chain + } else { + throw new Error('Invalid login packet: missing chain or Certificate') + } + var { key, userData, skinData } = this.decodeLoginJWT(chain, skinChain) // eslint-disable-line } catch (e) { debug(this.address, e) this.disconnect('Server authentication error') diff --git a/test/internal.js b/test/internal.js index 70c7b11..ffb403e 100644 --- a/test/internal.js +++ b/test/internal.js @@ -101,11 +101,11 @@ async function startTest (version = CURRENT_VERSION, ok) { loop = setInterval(() => { client.write('network_chunk_publisher_update', { coordinates: { x: 646, y: 130, z: 77 }, radius: 64 }) - }, 9500) + }, 6500) setTimeout(() => { client.write('play_status', { status: 'player_spawn' }) - }, 6000) + }, 3000) // Respond to tick synchronization packets client.on('tick_sync', (packet) => { diff --git a/test/proxy.js b/test/proxy.js index 345e89f..af443d7 100644 --- a/test/proxy.js +++ b/test/proxy.js @@ -22,12 +22,12 @@ function proxyTest (version, raknetBackend = 'raknet-native', timeout = 1000 * 4 console.debug('Client has authenticated') setTimeout(() => { client.disconnect('Hello world !') - }, 1000) // allow some time for client to connect + }, 500) // allow some time for client to connect }) }) console.debug('Server started', server.options.version) - await new Promise(resolve => setTimeout(resolve, 1000)) + await new Promise(resolve => setTimeout(resolve, 500)) const relay = new Relay({ version, @@ -46,7 +46,7 @@ function proxyTest (version, raknetBackend = 'raknet-native', timeout = 1000 * 4 await relay.listen() console.debug('Proxy started', server.options.version) - await new Promise(resolve => setTimeout(resolve, 1000)) + await new Promise(resolve => setTimeout(resolve, 500)) const client = createClient({ host: '127.0.0.1', port: CLIENT_PORT, version, username: 'Boat', offline: true, raknetBackend, skipPing: true }) console.debug('Client started') @@ -58,7 +58,7 @@ function proxyTest (version, raknetBackend = 'raknet-native', timeout = 1000 * 4 server.close() relay.close() console.log('✔ OK') - sleep(500).then(res) + sleep(200).then(res) }) }, timeout, () => { throw Error('timed out') }) } diff --git a/test/proxy.test.js b/test/proxy.test.js index 70ea5f1..27761ae 100644 --- a/test/proxy.test.js +++ b/test/proxy.test.js @@ -11,7 +11,7 @@ describe('proxies client/server', function () { it('proxies ' + version, async () => { console.debug(version) await proxyTest(version) - await sleep(1000) + await sleep(100) console.debug('Done', version) }) } diff --git a/tools/compileProtocol.js b/tools/compileProtocol.js index dcf6545..a7c3aa0 100644 --- a/tools/compileProtocol.js +++ b/tools/compileProtocol.js @@ -15,7 +15,6 @@ function createProtocol (version) { const compiler = new ProtoDefCompiler() const protocol = mcData('bedrock_' + version).protocol.types compiler.addTypes(require('../src/datatypes/compiler-minecraft')) - compiler.addTypes(require('prismarine-nbt/zigzag').compiler) compiler.addTypesToCompile(protocol) fs.writeFileSync('./read.js', 'module.exports = ' + compiler.readCompiler.generate().replace('() =>', 'native =>')) @@ -39,7 +38,7 @@ require('minecraft-data/bin/generate_data') // If no argument, build everything if (!process.argv[2]) { - convert('latest') + convert('bedrock', 'latest') for (const version of versions) { main(version) }