diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2c80125a..408636fc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,6 @@ on: jobs: Lint: runs-on: ubuntu-latest - steps: - uses: actions/checkout@v2 - name: Use Node.js 22.x diff --git a/docs/README.md b/docs/README.md index 028e05bf..e2ca6009 100644 --- a/docs/README.md +++ b/docs/README.md @@ -18,7 +18,7 @@ Parse and serialize minecraft packets, plus authentication and encryption. 1.15 (1.15, 1.15.1, 1.15.2), 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, 1.21.3, 1.21.4, 1.21.5, 1.21.6, 1.21.8 + 1.21, 1.21.1, 1.21.3, 1.21.4, 1.21.5, 1.21.6, 1.21.8, 1.21.9 * Parses all packets and emits events with packet fields as JavaScript diff --git a/src/client/play.js b/src/client/play.js index 4dc1c313..71ad739f 100644 --- a/src/client/play.js +++ b/src/client/play.js @@ -56,6 +56,9 @@ module.exports = function (client, options) { client.once('select_known_packs', () => { client.write('select_known_packs', { packs: [] }) }) + client.once('code_of_conduct', () => { + client.write('accept_code_of_conduct', {}) + }) // Server should send finish_configuration on its own right after sending the client a dimension codec // for login (that has data about world height, world gen, etc) after getting a login success from client client.once('finish_configuration', () => { diff --git a/src/datatypes/compiler-minecraft.js b/src/datatypes/compiler-minecraft.js index 9068641a..8a252a7c 100644 --- a/src/datatypes/compiler-minecraft.js +++ b/src/datatypes/compiler-minecraft.js @@ -70,7 +70,8 @@ if (n !== 0) { return { value: { ${opts.otherwise.name}: set }, size: accSize } } `.trim()) - }] + }], + lpVec3: ['native', minecraft.lpVec3[0]] }, Write: { varlong: ['native', minecraft.varlong[1]], @@ -135,7 +136,8 @@ if (${baseName} != null) { } return offset `.trim()) - }] + }], + lpVec3: ['native', minecraft.lpVec3[1]] }, SizeOf: { varlong: ['native', minecraft.varlong[2]], @@ -194,6 +196,7 @@ if (${baseName} != null) { } return size `.trim()) - }] + }], + lpVec3: ['native', minecraft.lpVec3[2]] } } diff --git a/src/datatypes/lpVec3.js b/src/datatypes/lpVec3.js new file mode 100644 index 00000000..9e0eb140 --- /dev/null +++ b/src/datatypes/lpVec3.js @@ -0,0 +1,105 @@ +const [readVarInt, writeVarInt, sizeOfVarInt] = require('protodef').types.varint + +const DATA_BITS_MASK = 32767 +const MAX_QUANTIZED_VALUE = 32766.0 +const ABS_MIN_VALUE = 3.051944088384301e-5 +const ABS_MAX_VALUE = 1.7179869183e10 + +function sanitize (value) { + if (isNaN(value)) return 0.0 + return Math.max(-ABS_MAX_VALUE, Math.min(value, ABS_MAX_VALUE)) +} + +function pack (value) { + return Math.round((value * 0.5 + 0.5) * MAX_QUANTIZED_VALUE) +} + +function unpack (packed, shift) { + // We use division by power of 2 to simulate a 64-bit right shift + const val = Math.floor(packed / Math.pow(2, shift)) & DATA_BITS_MASK + const clamped = val > 32766 ? 32766 : val + return (clamped * 2.0) / 32766.0 - 1.0 +} + +function readLpVec3 (buffer, offset) { + const a = buffer[offset] + if (a === 0) { + return { value: { x: 0, y: 0, z: 0 }, size: 1 } + } + + const b = buffer[offset + 1] + const c = buffer.readUInt32LE(offset + 2) + + // Combine into 48-bit safe integer (up to 2^53 is safe in JS) + const packed = (c * 65536) + (b << 8) + a + + let scale = a & 3 + let size = 6 + + if ((a & 4) === 4) { + const { value: varIntVal, size: varIntSize } = readVarInt(buffer, offset + 6) + scale = (varIntVal * 4) + scale + size += varIntSize + } + + return { + value: { + x: unpack(packed, 3) * scale, + y: unpack(packed, 18) * scale, + z: unpack(packed, 33) * scale + }, + size + } +} + +function writeLpVec3 (value, buffer, offset) { + const x = sanitize(value.x) + const y = sanitize(value.y) + const z = sanitize(value.z) + + const max = Math.max(Math.abs(x), Math.abs(y), Math.abs(z)) + + if (max < ABS_MIN_VALUE) { + buffer[offset] = 0 + return offset + 1 + } + + const scale = Math.ceil(max) + const needsContinuation = (scale & 3) !== scale + const scaleByte = needsContinuation ? ((scale & 3) | 4) : (scale & 3) + + const pX = pack(x / scale) + const pY = pack(y / scale) + const pZ = pack(z / scale) + + // Layout: + // [Z (15)] [Y (15)] [X (15)] [Flags (3)] + + // low32 contains Flags(3), X(15), and the first 14 bits of Y (3+15+14 = 32) + const low32 = (scaleByte | (pX << 3) | (pY << 18)) >>> 0 + + // high16 contains the 15th bit of Y and all 15 bits of Z + const high16 = ((pY >> 14) & 0x01) | (pZ << 1) + + buffer.writeUInt32LE(low32, offset) + buffer.writeUInt16LE(high16, offset + 4) + + if (needsContinuation) { + return writeVarInt(Math.floor(scale / 4), buffer, offset + 6) + } + + return offset + 6 +} + +function sizeOfLpVec3 (value) { + const max = Math.max(Math.abs(value.x), Math.abs(value.y), Math.abs(value.z)) + if (max < ABS_MIN_VALUE) return 1 + + const scale = Math.ceil(max) + if ((scale & 3) !== scale) { + return 6 + sizeOfVarInt(Math.floor(scale / 4)) + } + return 6 +} + +module.exports = [readLpVec3, writeLpVec3, sizeOfLpVec3] diff --git a/src/datatypes/minecraft.js b/src/datatypes/minecraft.js index 09e90355..83ab50e8 100644 --- a/src/datatypes/minecraft.js +++ b/src/datatypes/minecraft.js @@ -4,6 +4,7 @@ const nbt = require('prismarine-nbt') const UUID = require('uuid-1345') const zlib = require('zlib') const [readVarInt, writeVarInt, sizeOfVarInt] = require('protodef').types.varint +const [readLpVec3, writeLpVec3, sizeOfLpVec3] = require('./lpVec3') module.exports = { varlong: [readVarLong, writeVarLong, sizeOfVarLong], @@ -11,7 +12,8 @@ module.exports = { compressedNbt: [readCompressedNbt, writeCompressedNbt, sizeOfCompressedNbt], restBuffer: [readRestBuffer, writeRestBuffer, sizeOfRestBuffer], entityMetadataLoop: [readEntityMetadata, writeEntityMetadata, sizeOfEntityMetadata], - topBitSetTerminatedArray: [readTopBitSetTerminatedArray, writeTopBitSetTerminatedArray, sizeOfTopBitSetTerminatedArray] + topBitSetTerminatedArray: [readTopBitSetTerminatedArray, writeTopBitSetTerminatedArray, sizeOfTopBitSetTerminatedArray], + lpVec3: [readLpVec3, writeLpVec3, sizeOfLpVec3] } const PartialReadError = require('protodef').utils.PartialReadError diff --git a/src/version.js b/src/version.js index 7525bd70..0e93d0dd 100644 --- a/src/version.js +++ b/src/version.js @@ -1,6 +1,6 @@ 'use strict' module.exports = { - defaultVersion: '1.21.8', - 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', '1.21.4', '1.21.5', '1.21.6', '1.21.8'] + defaultVersion: '1.21.9', + 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', '1.21.4', '1.21.5', '1.21.6', '1.21.8', '1.21.9'] } diff --git a/test/packetTest.js b/test/packetTest.js index 81ee1695..d4074f66 100644 --- a/test/packetTest.js +++ b/test/packetTest.js @@ -415,6 +415,37 @@ const values = { RecipeBookSetting: { open: false, filtering: false + }, + lpVec3: { x: 0, y: 0, z: 0 }, + ExplosionParticleEntry: { + data: { // ExplosionParticleInfo + particle: { + particleId: 0, + data: null + }, + speed: 0, + scaling: 0 + }, + weight: 1 + }, + DebugSubscriptionDataType: 0, + DebugSubscriptionUpdate: { + type: 0 + }, + DebugSubscriptionEvent: { + type: 0 + }, + RespawnData: { + globalPos: { + dimensionName: 'minecraft:overworld', + location: { x: 0, y: 64, z: 0 } + }, + yaw: 0, + pitch: 0 + }, + GlobalPos: { + dimensionName: 'minecraft:overworld', + location: { x: 0, y: 64, z: 0 } } }