UNPKG

prismarine-chunk

Version:
245 lines (200 loc) 7.81 kB
const CommonChunkColumn = require('../common/CommonChunkColumn') const Section = require('./section') const Vec3 = require('vec3').Vec3 const BIOME_SIZE = 256 const h = 256 const { w, l, sh } = Section const sectionCount = h >> 4 module.exports = (registry) => { const Block = require('prismarine-block')(registry) return class Chunk extends CommonChunkColumn { static get section () { return Section } static get w () { return w } static get l () { return l } static get h () { return h } static get version () { return registry.version } constructor () { super(registry) this.skyLightSent = true this.minY = 0 this.worldHeight = h this.sections = new Array(sectionCount) for (let i = 0; i < sectionCount; i++) { this.sections[i] = new Section() } // alloc automatically zero initializes this.biome = Buffer.alloc(BIOME_SIZE) } toJson () { return JSON.stringify({ skyLightSent: this.skyLightSent, biome: this.biome.toJSON(), blockEntities: this.blockEntities, sections: this.sections.map(section => section.toJson()) }) } static fromJson (j) { const parsed = JSON.parse(j) const chunk = new Chunk() chunk.skyLightSent = parsed.skyLightSent chunk.biome = Buffer.from(parsed.biome) chunk.blockEntities = parsed.blockEntities chunk.sections = parsed.sections.map(s => Section.fromJson(s)) return chunk } initialize (iniFunc) { let biome = 0 for (let i = 0; i < sectionCount; i++) { this.sections[i].initialize((x, y, z, n) => { const block = iniFunc(x, y % sh, z, n) if (block == null) { return } if (y === 0 && sectionCount === 0) { this.biome.writeUInt8(block.biome.id || 0, biome) biome++ } return block }) } } getBlock (pos) { const block = new Block(this.getBlockType(pos), this.getBiome(pos), this.getBlockData(pos)) block.light = this.getBlockLight(pos) block.skyLight = this.getSkyLight(pos) block.entity = this.getBlockEntity(pos) return block } setBlock (pos, block) { if (exists(block.type)) { this.setBlockType(pos, block.type) } if (exists(block.metadata)) { this.setBlockData(pos, block.metadata) } if (exists(block.biome)) { this.setBiome(pos, block.biome.id) } if (exists(block.skyLight) && this.skyLightSent) { this.setSkyLight(pos, block.skyLight) } if (exists(block.light)) { this.setBlockLight(pos, block.light) } if (block.entity) { this.setBlockEntity(pos, block.entity) } else { this.removeBlockEntity(pos) } } _getSection (pos) { return this.sections[pos.y >> 4] } getBlockStateId (pos) { const section = this._getSection(pos) return section ? section.getBlockStateId(posInSection(pos)) : 0 } getBlockType (pos) { const section = this._getSection(pos) return section ? section.getBlockType(posInSection(pos)) : 0 } getBlockData (pos) { const section = this._getSection(pos) return section ? section.getBlockData(posInSection(pos)) : 0 } getBlockLight (pos) { const section = this._getSection(pos) return section ? section.getBlockLight(posInSection(pos)) : 0 } getSkyLight (pos) { if (!this.skyLightSent) return 0 const section = this._getSection(pos) return section ? section.getSkyLight(posInSection(pos)) : 15 } getBiome (pos) { const cursor = getBiomeCursor(pos) return this.biome.readUInt8(cursor) } setBlockStateId (pos, stateId) { const section = this._getSection(pos) return section && section.setBlockStateId(posInSection(pos), stateId) } setBlockType (pos, id) { const data = this.getBlockData(pos) this.setBlockStateId(pos, (id << 4) | data) } setBlockData (pos, data) { const id = this.getBlockType(pos) this.setBlockStateId(pos, (id << 4) | data) } setBlockLight (pos, light) { const section = this._getSection(pos) return section && section.setBlockLight(posInSection(pos), light) } setSkyLight (pos, light) { const section = this._getSection(pos) return section && section.setSkyLight(posInSection(pos), light) } setBiome (pos, biome) { const cursor = getBiomeCursor(pos) this.biome.writeUInt8(biome, cursor) } // These methods do nothing, and are present only for API compatibility dumpBiomes () { } dumpLight () { } loadLight () { } loadBiomes () { } dump (bitMap = 0xFFFF, skyLightSent = true) { const SECTION_SIZE = Section.sectionSize(this.skyLightSent && skyLightSent) const { chunkIncluded, chunkCount } = parseBitMap(bitMap) const bufferLength = chunkCount * SECTION_SIZE + BIOME_SIZE const buffer = Buffer.alloc(bufferLength) let offset = 0 let offsetLight = w * l * sectionCount * chunkCount * 2 let offsetSkyLight = (this.skyLightSent && skyLightSent) ? w * l * sectionCount * chunkCount / 2 * 5 : undefined for (let i = 0; i < sectionCount; i++) { if (chunkIncluded[i]) { offset += this.sections[i].dump().copy(buffer, offset, 0, w * l * sh * 2) offsetLight += this.sections[i].dump().copy(buffer, offsetLight, w * l * sh * 2, w * l * sh * 2 + w * l * sh / 2) if (this.skyLightSent && skyLightSent) offsetSkyLight += this.sections[i].dump().copy(buffer, offsetSkyLight, w * l * sh / 2 * 5, w * l * sh / 2 * 5 + w * l * sh / 2) } } this.biome.copy(buffer, w * l * sectionCount * chunkCount * ((this.skyLightSent && skyLightSent) ? 3 : 5 / 2)) return buffer } load (data, bitMap = 0xFFFF, skyLightSent = true, fullChunk = true) { if (!Buffer.isBuffer(data)) { throw (new Error('Data must be a buffer')) } this.skyLightSent = skyLightSent const SECTION_SIZE = Section.sectionSize(skyLightSent) const { chunkIncluded, chunkCount } = parseBitMap(bitMap) let offset = 0 let offsetLight = w * l * sectionCount * chunkCount * 2 let offsetSkyLight = (this.skyLightSent) ? w * l * sectionCount * chunkCount / 2 * 5 : undefined for (let i = 0; i < sectionCount; i++) { if (chunkIncluded[i]) { const sectionBuffer = Buffer.alloc(SECTION_SIZE) offset += data.copy(sectionBuffer, 0, offset, offset + w * l * sh * 2) offsetLight += data.copy(sectionBuffer, w * l * sh * 2, offsetLight, offsetLight + w * l * sh / 2) if (this.skyLightSent) offsetSkyLight += data.copy(sectionBuffer, w * l * sh * 5 / 2, offsetSkyLight, offsetSkyLight + w * l * sh / 2) this.sections[i].load(sectionBuffer, skyLightSent) } } if (fullChunk) { data.copy(this.biome, 0, w * l * sectionCount * chunkCount * (skyLightSent ? 3 : 5 / 2)) } const expectedSize = SECTION_SIZE * chunkCount + (fullChunk ? w * l : 0) if (data.length !== expectedSize) { throw (new Error(`Data buffer not correct size (was ${data.length}, expected ${expectedSize})`)) } } getMask () { return 0xFFFF } } } const exists = function (val) { return val !== undefined } const getBiomeCursor = function (pos) { return (pos.z * w) + pos.x } function posInSection (pos) { return pos.modulus(new Vec3(w, l, sh)) } function parseBitMap (bitMap) { const chunkIncluded = new Array(sectionCount) let chunkCount = 0 for (let y = 0; y < sectionCount; ++y) { chunkIncluded[y] = bitMap & (1 << y) if (chunkIncluded[y]) chunkCount++ } return { chunkIncluded, chunkCount } }