Skip to content

Commit

Permalink
Merge branch 'master' into dependabot/npm_and_yarn/mocha-11.0.1
Browse files Browse the repository at this point in the history
  • Loading branch information
rom1504 authored Jan 6, 2025
2 parents fa337cb + 8e131c3 commit 95e89c5
Show file tree
Hide file tree
Showing 14 changed files with 288 additions and 20 deletions.
5 changes: 5 additions & 0 deletions docs/HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# History

## 1.51.0
* [Add type to serverKey in server (#1349)](https://github.com/PrismarineJS/node-minecraft-protocol/commit/d6b4e82eb170984380e7ea9f125ea5d72777bef2) (thanks @u9g)
* [support 1.21.3 (#1347)](https://github.com/PrismarineJS/node-minecraft-protocol/commit/2224d824065908e910520dfa8ea9f3f3ade242e4) (thanks @rom1504)
* [Bump @types/node from 20.16.15 to 22.7.9 (#1345)](https://github.com/PrismarineJS/node-minecraft-protocol/commit/590dc33fed2100e77ef58e7db716dfc45eb61159) (thanks @dependabot[bot])

## 1.50.0
* [1.21 Support (#1342)](https://github.com/PrismarineJS/node-minecraft-protocol/commit/5bebac36620d8f8ec256d19483e20e643d63de2a) (thanks @GroobleDierne)

Expand Down
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Parse and serialize minecraft packets, plus authentication and encryption.

* Supports Minecraft PC version 1.7.10, 1.8.8, 1.9 (15w40b, 1.9, 1.9.1-pre2, 1.9.2, 1.9.4),
1.10 (16w20a, 1.10-pre1, 1.10, 1.10.1, 1.10.2), 1.11 (16w35a, 1.11, 1.11.2), 1.12 (17w15a, 17w18b, 1.12-pre4, 1.12, 1.12.1, 1.12.2), and 1.13 (17w50a, 1.13, 1.13.1, 1.13.2-pre1, 1.13.2-pre2, 1.13.2), 1.14 (1.14, 1.14.1, 1.14.3, 1.14.4)
, 1.15 (1.15, 1.15.1, 1.15.2) and 1.16 (20w13b, 20w14a, 1.16-rc1, 1.16, 1.16.1, 1.16.2, 1.16.3, 1.16.4, 1.16.5), 1.17 (21w07a, 1.17, 1.17.1), 1.18 (1.18, 1.18.1 and 1.18.2), 1.19 (1.19, 1.19.1, 1.19.2, 1.19.3, 1.19.4, 1.20, 1.20.1, 1.20.2, 1.20.3, 1.20.4, 1.20.5, 1.20.6, 1.21.1)
, 1.15 (1.15, 1.15.1, 1.15.2) and 1.16 (20w13b, 20w14a, 1.16-rc1, 1.16, 1.16.1, 1.16.2, 1.16.3, 1.16.4, 1.16.5), 1.17 (21w07a, 1.17, 1.17.1), 1.18 (1.18, 1.18.1 and 1.18.2), 1.19 (1.19, 1.19.1, 1.19.2, 1.19.3, 1.19.4), 1.20 (1.20, 1.20.1, 1.20.2, 1.20.3, 1.20.4, 1.20.5, 1.20.6), 1.21 (1.21, 1.21.1, 1.21.3)
* Parses all packets and emits events with packet fields as JavaScript
objects.
* Send a packet by supplying fields as a JavaScript object.
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "minecraft-protocol",
"version": "1.50.0",
"version": "1.51.0",
"description": "Parse and serialize minecraft packets, plus authentication and encryption.",
"main": "src/index.js",
"types": "src/index.d.ts",
Expand Down Expand Up @@ -45,6 +45,7 @@
"prismarine-registry": "^1.8.0"
},
"dependencies": {
"@types/node-rsa": "^1.1.4",
"@types/readable-stream": "^4.0.0",
"aes-js": "^3.1.2",
"buffer-equal": "^1.0.0",
Expand Down
1 change: 1 addition & 0 deletions src/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')
}
Expand Down
1 change: 1 addition & 0 deletions src/createServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
159 changes: 158 additions & 1 deletion src/datatypes/compiler-minecraft.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable no-return-assign */
const UUID = require('uuid-1345')
const minecraft = require('./minecraft')

Expand Down Expand Up @@ -41,7 +42,7 @@ module.exports = {
code += '}'
return compiler.wrapCode(code)
}],
arrayWithLengthOffset: ['parametrizable', (compiler, array) => {
arrayWithLengthOffset: ['parametrizable', (compiler, array) => { // TODO: remove
let code = ''
if (array.countType) {
code += 'const { value: count, size: countSize } = ' + compiler.callType(array.countType) + '\n'
Expand All @@ -61,6 +62,56 @@ module.exports = {
code += '}\n'
code += 'return { value: data, size }'
return compiler.wrapCode(code)
}],
bitflags: ['parametrizable', (compiler, { type, flags, shift, big }) => {
let fstr = JSON.stringify(flags)
if (Array.isArray(flags)) {
fstr = '{'
for (const [k, v] of Object.entries(flags)) fstr += `"${v}": ${big ? (1n << BigInt(k)) : (1 << k)}` + (big ? 'n,' : ',')
fstr += '}'
} else if (shift) {
fstr = '{'
for (const key in flags) fstr += `"${key}": ${1 << flags[key]},`
fstr += '}'
}
return compiler.wrapCode(`
const { value: _value, size } = ${compiler.callType(type, 'offset')}
const value = { _value }
const flags = ${fstr}
for (const key in flags) {
value[key] = (_value & flags[key]) == flags[key]
}
return { value, size }
`.trim())
}],
registryEntryHolder: ['parametrizable', (compiler, opts) => {
return compiler.wrapCode(`
const { value: n, size: nSize } = ${compiler.callType('varint')}
if (n !== 0) {
return { value: { ${opts.baseName}: n - 1 }, size: nSize }
} else {
const holder = ${compiler.callType(opts.otherwise.type)}
return { value: { ${opts.otherwise.name}: holder.data }, size: nSize + holder.size }
}
`.trim())
}],
registryEntryHolderSet: ['parametrizable', (compiler, opts) => {
return compiler.wrapCode(`
const { value: n, size: nSize } = ${compiler.callType('varint')}
if (n === 0) {
const base = ${compiler.callType(opts.base.type, 'offset + nSize')}
return { value: { ${opts.base.name}: base.value }, size: base.size + nSize }
} else {
const set = []
let accSize = nSize
for (let i = 0; i < n - 1; i++) {
const entry = ${compiler.callType(opts.otherwise.type, 'offset + accSize')}
set.push(entry.value)
accSize += entry.size
}
return { value: { ${opts.otherwise.name}: set }, size: accSize }
}
`.trim())
}]
},
Write: {
Expand Down Expand Up @@ -106,6 +157,58 @@ module.exports = {
code += '}\n'
code += 'return offset'
return compiler.wrapCode(code)
}],
bitflags: ['parametrizable', (compiler, { type, flags, shift, big }) => {
let fstr = JSON.stringify(flags)
if (Array.isArray(flags)) {
fstr = '{'
for (const [k, v] of Object.entries(flags)) fstr += `"${v}": ${big ? (1n << BigInt(k)) : (1 << k)}` + (big ? 'n,' : ',')
fstr += '}'
} else if (shift) {
fstr = '{'
for (const key in flags) fstr += `"${key}": ${1 << flags[key]},`
fstr += '}'
}
return compiler.wrapCode(`
const flags = ${fstr}
let val = value._value ${big ? '|| 0n' : ''}
for (const key in flags) {
if (value[key]) val |= flags[key]
}
return (ctx.${type})(val, buffer, offset)
`.trim())
}],
registryEntryHolder: ['parametrizable', (compiler, opts) => {
const baseName = `value.${opts.baseName}`
const otherwiseName = `value.${opts.otherwise.name}`
return compiler.wrapCode(`
if (${baseName}) {
offset = ${compiler.callType(`${baseName} + 1`, 'varint')}
} else if (${otherwiseName}) {
offset = ${compiler.callType(`${otherwiseName}`, opts.otherwise.type)}
} else {
throw new Error('registryEntryHolder type requires "${baseName}" or "${otherwiseName}" fields to be set')
}
return offset
`.trim())
}],
registryEntryHolderSet: ['parametrizable', (compiler, opts) => {
const baseName = `value.${opts.base.name}`
const otherwiseName = `value.${opts.otherwise.name}`
return compiler.wrapCode(`
if (${baseName}) {
offset = ${compiler.callType(0, 'varint')}
offset = ${compiler.callType(`${baseName}`, opts.base.type)}
} else if (${otherwiseName}) {
offset = ${compiler.callType(`${otherwiseName}.length + 1`, 'varint')}
for (let i = 0; i < ${otherwiseName}.length; i++) {
offset = ${compiler.callType(`${otherwiseName}[i]`, opts.otherwise.type)}
}
} else {
throw new Error('registryEntryHolder type requires "${opts.base.name}" or "${opts.otherwise.name}" fields to be set')
}
return offset
`.trim())
}]
},
SizeOf: {
Expand Down Expand Up @@ -149,6 +252,60 @@ module.exports = {
}
code += 'return size'
return compiler.wrapCode(code)
}],
bitflags: ['parametrizable', (compiler, { type, flags, shift, big }) => {
let fstr = JSON.stringify(flags)
if (Array.isArray(flags)) {
fstr = '{'
for (const [k, v] of Object.entries(flags)) fstr += `"${v}": ${big ? (1n << BigInt(k)) : (1 << k)}` + (big ? 'n,' : ',')
fstr += '}'
} else if (shift) {
fstr = '{'
for (const key in flags) fstr += `"${key}": ${1 << flags[key]},`
fstr += '}'
}
return compiler.wrapCode(`
const flags = ${fstr}
let val = value._value ${big ? '|| 0n' : ''}
for (const key in flags) {
if (value[key]) val |= flags[key]
}
return (ctx.${type})(val)
`.trim())
}],
registryEntryHolder: ['parametrizable', (compiler, opts) => {
const baseName = `value.${opts.baseName}`
const otherwiseName = `value.${opts.otherwise.name}`
return compiler.wrapCode(`
let size = 0
if (${baseName}) {
size += ${compiler.callType(`${baseName} + 1`, 'varint')}
} else if (${otherwiseName}) {
size += ${compiler.callType(`${otherwiseName}`, opts.otherwise.type)}
} else {
throw new Error('registryEntryHolder type requires "${baseName}" or "${otherwiseName}" fields to be set')
}
return size
`.trim())
}],
registryEntryHolderSet: ['parametrizable', (compiler, opts) => {
const baseName = `value.${opts.base.name}`
const otherwiseName = `value.${opts.otherwise.name}`
return compiler.wrapCode(`
let size = 0
if (${baseName}) {
size += ${compiler.callType(0, 'varint')}
size += ${compiler.callType(`${baseName}`, opts.base.type)}
} else if (${otherwiseName}) {
size += ${compiler.callType(`${otherwiseName}.length + 1`, 'varint')}
for (let i = 0; i < ${otherwiseName}.length; i++) {
size += ${compiler.callType(`${otherwiseName}[i]`, opts.otherwise.type)}
}
} else {
throw new Error('registryEntryHolder type requires "${opts.base.name}" or "${opts.otherwise.name}" fields to be set')
}
return size
`.trim())
}]
}
}
2 changes: 2 additions & 0 deletions src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Agent } from 'http'
import { Transform } from "readable-stream";
import { BinaryLike, KeyObject } from 'crypto';
import { Realm } from "prismarine-realms"
import NodeRSA from 'node-rsa';

type PromiseLike = Promise<void> | void

Expand Down Expand Up @@ -166,6 +167,7 @@ declare module 'minecraft-protocol' {
motd: string
motdMsg?: Object
favicon: string
serverKey: NodeRSA
close(): void
on(event: 'connection', handler: (client: ServerClient) => PromiseLike): this
on(event: 'error', listener: (error: Error) => PromiseLike): this
Expand Down
8 changes: 6 additions & 2 deletions src/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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)
}
Expand Down
11 changes: 11 additions & 0 deletions src/server/login.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ 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
* @param {import('../index').Server} server
* @param {Object} options
*/
module.exports = function (client, server, options) {
const mojangPubKey = crypto.createPublicKey(mojangPublicKeyPem)
const raise = (translatableError) => client.end(translatableError, JSON.stringify({ translate: translatableError }))
Expand Down Expand Up @@ -191,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
Expand Down
2 changes: 1 addition & 1 deletion src/version.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@

module.exports = {
defaultVersion: '1.21.1',
supportedVersions: ['1.7', '1.8.8', '1.9.4', '1.10.2', '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', '1.20', '1.20.1', '1.20.2', '1.20.4', '1.20.6', '1.21.1']
supportedVersions: ['1.7', '1.8.8', '1.9.4', '1.10.2', '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', '1.20', '1.20.1', '1.20.2', '1.20.4', '1.20.6', '1.21.1', '1.21.3']
}
17 changes: 9 additions & 8 deletions test/benchmark.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,18 @@ const ITERATIONS = 10000
const mc = require('../')
const states = mc.states

const testDataWrite = [
{ name: 'keep_alive', params: { keepAliveId: 957759560 } },
// TODO: 1.19+ `chat` -> `player_chat` feature toggle
// { name: 'chat', params: { message: '<Bob> Hello World!' } },
{ name: 'position_look', params: { x: 6.5, y: 65.62, stance: 67.24, z: 7.5, yaw: 0, pitch: 0, onGround: true } }
// TODO: add more packets for better quality data
]

for (const supportedVersion of mc.supportedVersions) {
const mcData = require('minecraft-data')(supportedVersion)
const version = mcData.version
const positionFlags = mcData.isNewerOrEqualTo('1.21.3') ? { flags: { onGround: true, hasHorizontalCollision: false } } : { onGround: true }
const testDataWrite = [
{ name: 'keep_alive', params: { keepAliveId: 957759560 } },
// TODO: 1.19+ `chat` -> `player_chat` feature toggle
// { name: 'chat', params: { message: '<Bob> Hello World!' } },
{ name: 'position_look', params: { x: 6.5, y: 65.62, stance: 67.24, z: 7.5, yaw: 0, pitch: 0, ...positionFlags } }
// TODO: add more packets for better quality data
]

describe('benchmark ' + supportedVersion + 'v', function () {
this.timeout(60 * 1000)
const inputData = []
Expand Down
4 changes: 2 additions & 2 deletions test/clientTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ for (const supportedVersion of mc.supportedVersions) {
'server-port': PORT,
motd: 'test1234',
'max-players': 120,
// 'level-type': 'flat',
'level-type': 'flat',
'generate-structures': 'false', // 12m
'use-native-transport': 'false' // java 16 throws errors without this, https://www.spigotmc.org/threads/unable-to-access-address-of-buffer.311602
}, (err) => {
if (err) reject(err)
Expand Down Expand Up @@ -191,7 +192,6 @@ for (const supportedVersion of mc.supportedVersions) {
}
} else {
// 1.19+
console.log('Chat Message', data)
const sender = JSON.parse(data.senderName)
const msgPayload = data.formattedMessage ? JSON.parse(data.formattedMessage) : data.plainMessage
const plainMessage = client.parseMessage(msgPayload).toString()
Expand Down
Loading

0 comments on commit 95e89c5

Please sign in to comment.