Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ on:
jobs:
Lint:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Use Node.js 22.x
Expand Down
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
<!--add_next_version_above-->

* Parses all packets and emits events with packet fields as JavaScript
Expand Down
3 changes: 3 additions & 0 deletions src/client/play.js
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down
9 changes: 6 additions & 3 deletions src/datatypes/compiler-minecraft.js
Original file line number Diff line number Diff line change
Expand Up @@ -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]],
Expand Down Expand Up @@ -135,7 +136,8 @@ if (${baseName} != null) {
}
return offset
`.trim())
}]
}],
lpVec3: ['native', minecraft.lpVec3[1]]
},
SizeOf: {
varlong: ['native', minecraft.varlong[2]],
Expand Down Expand Up @@ -194,6 +196,7 @@ if (${baseName} != null) {
}
return size
`.trim())
}]
}],
lpVec3: ['native', minecraft.lpVec3[2]]
}
}
105 changes: 105 additions & 0 deletions src/datatypes/lpVec3.js
Original file line number Diff line number Diff line change
@@ -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]
4 changes: 3 additions & 1 deletion src/datatypes/minecraft.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@ 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],
UUID: [readUUID, writeUUID, 16],
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

Expand Down
4 changes: 2 additions & 2 deletions src/version.js
Original file line number Diff line number Diff line change
@@ -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']
}
31 changes: 31 additions & 0 deletions test/packetTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
}
}

Expand Down
Loading