Skip to content
Open
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
4 changes: 3 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ const chunkImplementations = {
1.19: require('./pc/1.18/chunk'),
'1.20': require('./pc/1.18/chunk'),
1.21: require('./pc/1.18/chunk'),
26.1: require('./pc/1.18/chunk')
26.1: require('./pc/1.18/chunk'),
26.2: require('./pc/1.18/chunk'),
26.3: require('./pc/1.18/chunk')
},
bedrock: {
0.14: require('./bedrock/0.14/chunk'),
Expand Down
22 changes: 16 additions & 6 deletions src/pc/1.18/ChunkColumn.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@ module.exports = (Block, mcData) => {
// 1.21.5+ writes no size prefix before chunk containers, it's computed dynamically to save 1 byte
const noSizePrefix = mcData.version['>=']('1.21.5')
const hasFluidCount = mcData.version['>=']('26.1')
// 26.3+ (protocol 777): the light-mask BIT_SET codec switched from a
// varint-count-of-longs encoding to a varint-count-of-raw-bytes encoding.
// Found live-testing against a 26.3 Realm: loadParsedLight threw
// "The first argument must be of type string or an instance of Buffer...
// Received undefined" because the sky/block light mask arrays minecraft-data
// decoded (now byte arrays, per the corresponding pc-26.3-support
// minecraft-data schema fix) were being fed through fromLongArray, which
// interpreted them as ~8x fewer/wrong bits than the real mask - confirmed
// by decompiling ByteBufCodecs.BIT_SET from the official 26.3 server jar.
const lightMasksAreByteArrays = mcData.version['>=']('26.3')
const ChunkSection = require('../common/PaletteChunkSection')(Block)
return class ChunkColumn extends CommonChunkColumn {
static get section () { return ChunkSection }
Expand Down Expand Up @@ -261,8 +271,8 @@ module.exports = (Block, mcData) => {
loadParsedLight (skyLight, blockLight, skyLightMask, blockLightMask, emptySkyLightMask, emptyBlockLightMask) {
function readSection (sections, data, lightMask, pLightMask, emptyMask, pEmptyMask) {
let currentSectionIndex = 0
const incomingLightMask = BitArray.fromLongArray(pLightMask, 1)
const incomingEmptyMask = BitArray.fromLongArray(pEmptyMask, 1)
const incomingLightMask = lightMasksAreByteArrays ? BitArray.fromByteArray(pLightMask, 1) : BitArray.fromLongArray(pLightMask, 1)
const incomingEmptyMask = lightMasksAreByteArrays ? BitArray.fromByteArray(pEmptyMask, 1) : BitArray.fromLongArray(pEmptyMask, 1)

for (let y = 0; y < sections.length; y++) {
const isEmpty = incomingEmptyMask.get(y)
Expand Down Expand Up @@ -360,10 +370,10 @@ module.exports = (Block, mcData) => {
return {
skyLight,
blockLight,
skyLightMask: this.skyLightMask.toLongArray(),
blockLightMask: this.blockLightMask.toLongArray(),
emptySkyLightMask: this.emptySkyLightMask.toLongArray(),
emptyBlockLightMask: this.emptyBlockLightMask.toLongArray()
skyLightMask: lightMasksAreByteArrays ? this.skyLightMask.toByteArray() : this.skyLightMask.toLongArray(),
blockLightMask: lightMasksAreByteArrays ? this.blockLightMask.toByteArray() : this.blockLightMask.toLongArray(),
emptySkyLightMask: lightMasksAreByteArrays ? this.emptySkyLightMask.toByteArray() : this.emptySkyLightMask.toLongArray(),
emptyBlockLightMask: lightMasksAreByteArrays ? this.emptyBlockLightMask.toByteArray() : this.emptyBlockLightMask.toLongArray()
}
}
}
Expand Down
39 changes: 39 additions & 0 deletions src/pc/common/BitArrayNoSpan.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,22 @@ class BitArray {
return array
}

// 26.3+ (protocol 777) light-mask wire format - see fromByteArray. Trims
// trailing all-zero bytes to match java.util.BitSet.toByteArray()/
// FriendlyByteBuf.writeByteArray semantics (the real server does the same).
toByteArray () {
const bytes = []
for (let i = 0; i < this.capacity; i++) {
const byteIndex = i >> 3
const bitIndex = i & 7
if (!bytes[byteIndex]) bytes[byteIndex] = 0
if (this.get(i)) bytes[byteIndex] |= (1 << bitIndex)
}
let end = bytes.length
while (end > 0 && !bytes[end - 1]) end--
return bytes.slice(0, end)
}

static fromLongArray (array, bitsPerValue) {
const bitArray = new BitArray({
capacity: Math.floor(64 / bitsPerValue) * array.length,
Expand All @@ -75,6 +91,29 @@ class BitArray {
return bitArray
}

// 26.3+ (protocol 777): Mojang's light-mask BIT_SET codec switched from
// FriendlyByteBuf.writeLongArray (varint count of longs, java.util.BitSet
// valueOf(long[])) to FriendlyByteBuf.writeByteArray (varint count of raw
// bytes, java.util.BitSet.valueOf(byte[])) - confirmed by decompiling
// ByteBufCodecs.BIT_SET from the official 26.3 server jar. Java's
// BitSet.valueOf(byte[]) is little-endian within each byte: bit i comes
// from bit (i % 8) of bytes[i / 8].
static fromByteArray (bytes, bitsPerValue) {
const bitArray = new BitArray({
capacity: Math.floor(8 / bitsPerValue) * bytes.length,
bitsPerValue
})
for (let i = 0; i < bytes.length; i++) {
const byte = bytes[i]
for (let b = 0; b < 8; b++) {
if ((byte >> b) & 1) {
bitArray.set(i * 8 + b, 1)
}
}
}
return bitArray
}

static or (a, b) {
const long = a.data.length > b.data.length ? a.data : b.data
const short = a.data.length > b.data.length ? b.data : a.data
Expand Down
Loading