* 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 <romain.rom1@gmail.com>
This commit is contained in:
extremeheat 2023-06-03 15:54:31 -04:00 committed by GitHub
parent a32a1cf478
commit 2718bc64c0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 94 additions and 28 deletions

View file

@ -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:

View file

@ -1,2 +1,2 @@
tasks:
- command: npm install
- command: npm install && sdk install java

View file

@ -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",

View file

@ -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 }

View file

@ -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,

View file

@ -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

View file

@ -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']
}

View file

@ -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]

View file

@ -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}`)
}
})
})
})
})
})
}