Skip to content

Commit

Permalink
support 1.21.3 (#1347)
Browse files Browse the repository at this point in the history
* support 1.21.3

* Add bitflags, registryEntryHolder and  registryEntryHolderSet types

* Fix spacing in compiler types

* Update compiler-minecraft.js

* Fix registryEntryHolderSet read code (#1351)

* Update ci.yml

* Fix test for 1.21.3 (#1353)

* Remove debug logging

* Fix benchmark tests for 1.21.3

* Start updating packetTest for 1.21.3

* Update packetTest.js with new types

* Fix minecraft-compiler

* Speedup tests by setting world type to flat and disabling structures.

* Didn't mean to commit that

---------

Co-authored-by: extremeheat <extreme@protonmail.ch>
Co-authored-by: Grooble <grooble.dierne@gmail.com>
  • Loading branch information
3 people authored Dec 4, 2024
1 parent 590dc33 commit 2224d82
Show file tree
Hide file tree
Showing 6 changed files with 247 additions and 15 deletions.
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
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: 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
78 changes: 76 additions & 2 deletions test/packetTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,32 @@ const nbtValue = {

function getFixedPacketPayload (version, packetName) {
if (packetName === 'declare_recipes') {
if (version['>=']('1.20.5')) {
if (version['>=']('1.21.3')) {
return {
recipes: [
{
name: 'minecraft:campfire_input',
items: [
903,
976
]
}
],
stoneCutterRecipes: [
{
input: {
ids: [
6
]
},
slotDisplay: {
type: 'item_stack',
data: slotValue
}
}
]
}
} else if (version['>=']('1.20.5')) {
return {
recipes: [
{
Expand Down Expand Up @@ -241,6 +266,13 @@ const values = {
suggestionType: 'minecraft:summonable_entities'
}
},
bitflags: function (typeArgs, context) {
const results = {}
Object.keys(typeArgs.flags).forEach(function (index) {
results[typeArgs.flags[index]] = true
})
return results
},
soundSource: 'master',
packedChunkPos: {
x: 10,
Expand All @@ -263,7 +295,49 @@ const values = {
isDebug: false,
isFlat: false,
portalCooldown: 0
}
},
MovementFlags: {
onGround: true,
hasHorizontalCollision: false
},
ContainerID: 0,
PositionUpdateRelatives: {
x: true,
y: true,
z: true,
yaw: true,
pitch: true,
dx: true,
dy: true,
dz: true,
yawDelta: true
},
RecipeDisplay: {
type: 'stonecutter',
data: {
ingredient: { type: 'empty' },
result: { type: 'empty' },
craftingStation: { type: 'empty' }
}
},
SlotDisplay: { type: 'empty' },
game_profile: {
name: 'test',
properties: [{
key: 'foo',
value: 'bar'
}]
},
optvarint: 1,
chat_session: {
uuid: '00112233-4455-6677-8899-aabbccddeeff',
publicKey: {
expireTime: 30,
keyBytes: [],
keySignature: []
}
},
IDSet: { ids: [2, 5] }
}

function getValue (_type, packet) {
Expand Down

0 comments on commit 2224d82

Please sign in to comment.