prismarine-chunk
Version:
A class to hold chunk data for prismarine
200 lines (169 loc) • 5.63 kB
JavaScript
const BitArray = require('../common/BitArray')
const neededBits = require('../common/neededBits')
const constants = require('../common/constants')
const varInt = require('../common/varInt')
const GLOBAL_BITS_PER_BLOCK = 13
function getBlockIndex (pos) {
return (pos.y << 8) | (pos.z << 4) | pos.x
}
class ChunkSection {
constructor (options = {}) {
if (options === null) {
return
}
if (typeof options.solidBlockCount === 'undefined') {
options.solidBlockCount = 0
if (options.data) {
for (let i = 0; i < constants.BLOCK_SECTION_VOLUME; i++) {
if (options.data.get(i) !== 0) {
options.solidBlockCount += 1
}
}
}
}
if (!options.data) {
options.data = new BitArray({
bitsPerValue: 4,
capacity: constants.BLOCK_SECTION_VOLUME
})
}
if (options.palette === undefined) { // dont create palette if its null
options.palette = [0]
}
if (!options.blockLight) {
options.blockLight = new BitArray({
bitsPerValue: 4,
capacity: constants.BLOCK_SECTION_VOLUME
})
}
if (options.skyLight === undefined) { // dont create skylight if its null
options.skyLight = new BitArray({
bitsPerValue: 4,
capacity: constants.BLOCK_SECTION_VOLUME
})
}
this.data = options.data
this.palette = options.palette
this.isDirty = false
this.blockLight = options.blockLight
this.skyLight = options.skyLight
this.solidBlockCount = options.solidBlockCount
this.maxBitsPerBlock = options.maxBitsPerBlock || GLOBAL_BITS_PER_BLOCK
}
toJson () {
return JSON.stringify({
data: this.data.toJson(),
palette: this.palette,
isDirty: this.isDirty,
blockLight: this.blockLight.toJson(),
skyLight: this.skyLight ? this.skyLight.toJson() : this.skyLight,
solidBlockCount: this.solidBlockCount
})
}
static fromJson (j) {
const parsed = JSON.parse(j)
return new ChunkSection({
data: BitArray.fromJson(parsed.data),
palette: parsed.palette,
blockLight: BitArray.fromJson(parsed.blockLight),
skyLight: parsed.skyLight ? BitArray.fromJson(parsed.skyLight) : parsed.skyLight,
solidBlockCount: parsed.solidBlockCount
})
}
getBlock (pos) {
// index in palette or block id
// depending on if the global palette or the section palette is used
let stateId = this.data.get(getBlockIndex(pos))
if (this.palette !== null) {
stateId = this.palette[stateId]
}
return stateId
}
setBlock (pos, stateId) {
const blockIndex = getBlockIndex(pos)
let palettedIndex
if (this.palette !== null) {
// if necessary, add the block to the palette
const indexInPalette = this.palette.indexOf(stateId) // binarySearch(this.palette, stateId, cmp)
if (indexInPalette >= 0) {
// block already in our palette
palettedIndex = indexInPalette
} else {
// get new block palette index
this.palette.push(stateId)
palettedIndex = this.palette.length - 1
// check if resize is necessary
const bitsPerValue = neededBits(palettedIndex)
// if new block requires more bits than the current data array
if (bitsPerValue > this.data.getBitsPerValue()) {
// is value still enough for section palette
if (bitsPerValue <= constants.MAX_BITS_PER_BLOCK) {
this.data = this.data.resizeTo(bitsPerValue)
} else {
// switches to the global palette
const newData = new BitArray({
bitsPerValue: this.maxBitsPerBlock,
capacity: constants.BLOCK_SECTION_VOLUME
})
for (let i = 0; i < constants.BLOCK_SECTION_VOLUME; i++) {
const stateId = this.palette[this.data.get(i)]
newData.set(i, stateId)
}
this.palette = null
palettedIndex = stateId
this.data = newData
}
}
}
} else {
// uses global palette
palettedIndex = stateId
}
const oldBlock = this.getBlock(pos)
if (stateId === 0 && oldBlock !== 0) {
this.solidBlockCount -= 1
} else if (stateId !== 0 && oldBlock === 0) {
this.solidBlockCount += 1
}
this.data.set(blockIndex, palettedIndex)
}
getBlockLight (pos) {
return this.blockLight.get(getBlockIndex(pos))
}
getSkyLight (pos) {
return this.skyLight ? this.skyLight.get(getBlockIndex(pos)) : 0
}
setBlockLight (pos, light) {
return this.blockLight.set(getBlockIndex(pos), light)
}
setSkyLight (pos, light) {
return this.skyLight ? this.skyLight.set(getBlockIndex(pos), light) : 0
}
isEmpty () {
return this.solidBlockCount === 0
}
// writes the complete section into a smart buffer object
write (smartBuffer) {
smartBuffer.writeUInt8(this.data.getBitsPerValue())
// write palette
if (this.palette !== null) {
varInt.write(smartBuffer, this.palette.length)
this.palette.forEach(paletteElement => {
varInt.write(smartBuffer, paletteElement)
})
} else {
// write 0 length for missing palette
varInt.write(smartBuffer, 0)
}
// write block data
varInt.write(smartBuffer, this.data.length())
this.data.writeBuffer(smartBuffer)
// write block light data
this.blockLight.writeBuffer(smartBuffer)
if (this.skyLight !== null) {
// write sky light data
this.skyLight.writeBuffer(smartBuffer)
}
}
}
module.exports = ChunkSection