UNPKG

mc-anvil

Version:

A Typescript library for reading Minecraft Anvil format files and Minecraft NBT format files in the browser.

328 lines 14.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Chunk = exports.blockParserFromSection = exports.sectionPalette = exports.sectionBlockStates = exports.indexFromBiomeCoordinate = exports.biomeCoordinateFromIndex = exports.indexFromChunkCoordinate = exports.chunkCoordinateFromIndex = exports.mod = void 0; const __1 = require(".."); const __2 = require("../.."); const block_1 = require("../block"); function mod(n, m) { if (n < 0) return ((n % m) + m) % m; return n % m; } exports.mod = mod; function chunkCoordinateFromIndex(index) { return [ index % 16, Math.floor(index / 256), Math.floor(index / 16) % 16 ]; } exports.chunkCoordinateFromIndex = chunkCoordinateFromIndex; function indexFromChunkCoordinate(coordinate) { const [x, y, z] = coordinate; return (y * 16 + z) * 16 + x; } exports.indexFromChunkCoordinate = indexFromChunkCoordinate; function biomeCoordinateFromIndex(index) { return [ index % 4, Math.floor(index / 16), Math.floor(index / 4) % 4 ]; } exports.biomeCoordinateFromIndex = biomeCoordinateFromIndex; function indexFromBiomeCoordinate(coordinate) { const [x, y, z] = coordinate; return (y * 4 + z) * 4 + x; } exports.indexFromBiomeCoordinate = indexFromBiomeCoordinate; function sectionBlockStates(section) { var _a; const b = section.find(x => x.name === "BlockStates") || ((_a = section.find(x => x.name === "block_states")) === null || _a === void 0 ? void 0 : _a.data).find(x => x.name === "data"); return b; } exports.sectionBlockStates = sectionBlockStates; function sectionPalette(section) { var _a; const p = section.find(x => x.name === "Palette" || x.name === "palette") || ((_a = section.find(x => x.name === "block_states")) === null || _a === void 0 ? void 0 : _a.data).find(x => x.name === "palette"); return p; } exports.sectionPalette = sectionPalette; function blockParserFromSection(section) { const blockstates = sectionBlockStates(section); const palette = sectionPalette(section); if (!blockstates || !palette) return; return new __1.BlockDataParser(blockstates, palette); } exports.blockParserFromSection = blockParserFromSection; class Chunk { constructor(root) { this.blockStates = new Map(); this.palettes = new Map(); this.blockStatesDirty = new Map(); this.root = root; } static isValidChunkSectionTag(tag) { const t = tag; return t && t.type === __2.TagType.LIST && t.name.toLowerCase() === "sections" && t.data && t.data.subType === __2.TagType.COMPOUND && t.data.data && t.data.data.length !== undefined; } static isValidChunkRootTag(tag) { const t = tag; return t && t.type === __2.TagType.COMPOUND && t.name === "" && t.data && t.data.length !== undefined; } static emptyX(y = 16, z = 16) { const r = []; for (let i = 0; i < y; ++i) { const c = []; for (let j = 0; j < z; ++j) c.push(0); r.push(c); } return r; } containsCoordinate(c) { if (c[1] < -64 || c[1] > 256) return false; const cc = this.getCoordinates(); if (!cc) return false; return c[0] >= cc[0] && c[0] < cc[0] + 16 && c[2] >= cc[1] && c[2] < cc[1] + 16; } coordinateKey() { const coordinates = this.getChunkCoordinates(); if (!coordinates) return; return `${coordinates[0]},${coordinates[1]}`; } getChunkCoordinates() { var _a, _b; if (!Chunk.isValidChunkRootTag(this.root)) return; const x = (_a = (__2.findChildTagAtPath("Level/xPos", this.root) || __2.findChildTagAtPath("xPos", this.root))) === null || _a === void 0 ? void 0 : _a.data; const z = (_b = (__2.findChildTagAtPath("Level/zPos", this.root) || __2.findChildTagAtPath("zPos", this.root))) === null || _b === void 0 ? void 0 : _b.data; if (x !== undefined && z !== undefined) return [x, z]; } getCoordinates() { const c = this.getChunkCoordinates(); if (!c) return; return [c[0] * 16, c[1] * 16]; } sections() { if (!Chunk.isValidChunkRootTag(this.root)) return; const sectionTag = __2.findChildTagAtPath("Level/Sections", this.root) || __2.findChildTagAtPath("sections", this.root); if (!Chunk.isValidChunkSectionTag(sectionTag)) return; return sectionTag; } sortedSections() { var _a; const s = this.sections(); if (s === undefined) return; const yTags = (_a = __2.findCompoundListChildren(s, x => x.name === "Y")) === null || _a === void 0 ? void 0 : _a.map((v, i) => ({ v, i })).filter(x => x.v); return yTags === null || yTags === void 0 ? void 0 : yTags.sort((a, b) => a.v.data - b.v.data).map(x => s.data.data[x.i]); } findBlocksByName(name) { this.chunkData(); const sections = this.sortedSections(); if (sections === undefined) return []; return sections.flatMap(section => { var _a; let yy = (((_a = section.find(x => x.name === "Y")) === null || _a === void 0 ? void 0 : _a.data) || 0) * 16; if (yy >= 4032) yy -= 4096; const [xx, zz] = this.getCoordinates() || [0, 0]; const blockData = sectionBlockStates(section); const palette = sectionPalette(section); if (blockData === undefined || palette === undefined) return []; return (new __1.BlockDataParser(blockData, palette)) .findBlocksByName(name) .map(chunkCoordinateFromIndex) .map(x => [x[0] + xx, x[1] + yy, x[2] + zz]); }); } getBlock(coordinates) { const yIndex = coordinates[1] >= 0 ? Math.floor(coordinates[1] / 16) : Math.floor((coordinates[1] + 4096) / 16); const [s, palette] = this.sectionBlockStateTensor(yIndex); const i = s[mod(coordinates[0], 16)][(coordinates[1] + 64) % 16][mod(coordinates[2], 16)]; return (palette ? block_1.paletteAsList(palette) : [])[i]; } getSectionContainingCoordinate(coordinates) { const s = this.sortedSections(); const yIndex = coordinates[1] >= 0 ? Math.floor(coordinates[1] / 16) : Math.floor((coordinates[1] + 4096) / 16); if (!s) return; return s.find(x => { var _a; return ((_a = x.find(xx => xx.name === "Y")) === null || _a === void 0 ? void 0 : _a.data) === yIndex; }); } setBlock(coordinates, name, properties) { const yIndex = coordinates[1] >= 0 ? Math.floor(coordinates[1] / 16) : Math.floor((coordinates[1] + 4096) / 16); const fullName = `${name}(${Object.keys(properties).map(k => `${k}:${properties[k]}`).sort((a, b) => a.localeCompare(b)).join(",")})`; const [s, palette] = this.sectionBlockStateTensor(yIndex); const nameOrder = palette ? block_1.paletteBlockList(palette) : []; const i = nameOrder.findIndex(x => x === fullName); const index = i !== -1 ? i : nameOrder.length; s[mod(coordinates[0], 16)][(coordinates[1] + 64) % 16][mod(coordinates[2], 16)] = index; if (i === -1) this.palettes.set(yIndex, __2.nbtTagReducer(palette || { type: __2.TagType.LIST, name: "palette", data: { subType: __2.TagType.COMPOUND, data: [] } }, { type: __2.NBTActions.NBT_ADD_COMPOUND_LIST_ITEM, path: "", index, tags: [{ type: __2.TagType.STRING, name: "Name", data: name }, ...(Object.keys(properties).length === 0 ? [] : [{ type: __2.TagType.COMPOUND, name: "Properties", data: Object.keys(properties).map(k => ({ type: __2.TagType.STRING, name: k, data: properties[k] })) }])] })); } uniqueBlockNames() { var _a; return new Set(((_a = this.sortedSections()) === null || _a === void 0 ? void 0 : _a.flatMap(x => block_1.paletteNameList(x.find(xx => xx.name === "Palette" || xx.name === "palette")))) || []); } worldHeights(name = "WORLD_SURFACE") { if (!Chunk.isValidChunkRootTag(this.root)) return; const r = Chunk.emptyX(16); const map = __2.findChildTagAtPath(`Level/Heightmaps/${name}`, this.root) || __2.findChildTagAtPath(`Heightmaps/${name}`, this.root); if (map === undefined || map.type !== __2.TagType.LONG_ARRAY || !map.data) return; const d = new __2.BinaryParser(map.data); const b = new BigUint64Array(d.remainingLength() / 8); for (let i = 0; i < b.length; ++i) b[i] = d.getUInt64LE(); const p = new __2.BitParser(b.buffer); for (let i = 0; i < 259; ++i) { const ii = i + 6 - 2 * (i % 7); const x = ii % 16; const z = Math.floor(ii / 16); if (i % 7 === 0) p.getBits(1); const cc = p.getBits(9); if (x < 16 && z < 16) r[x][z] = cc; } return r; } sectionBlockStateTensor(yIndex) { this.blockStatesDirty.set(yIndex, true); if (this.blockStates.get(yIndex) && this.palettes.get(yIndex)) return [this.blockStates.get(yIndex), this.palettes.get(yIndex)]; const r = []; for (let x = 0; x < 16; ++x) r.push(Chunk.emptyX()); const sections = this.sortedSections(); if (!sections) return [r, undefined]; const section = sections.find(x => { var _a; return ((_a = x.find(xx => xx.name === "Y")) === null || _a === void 0 ? void 0 : _a.data) === yIndex; }); const blockData = section && sectionBlockStates(section); const palette = section && sectionPalette(section); this.blockStates.set(yIndex, r); this.palettes.set(yIndex, palette); if (blockData === undefined) return [r, palette]; const b = new __1.BlockDataParser(blockData, palette); b.getRawBlocks().forEach((v, i) => { const [x, y, z] = chunkCoordinateFromIndex(i); r[x][y][z] = v || 0; }); return [r, palette]; } blockStateTensor() { const r = []; for (let x = 0; x < 16; ++x) r.push(Chunk.emptyX()); const sections = this.sortedSections() || []; sections.forEach((section, y) => { const yy = y * 16; const blockData = sectionBlockStates(section); const palette = sectionPalette(section); if (blockData === undefined || palette === undefined) return []; const b = new __1.BlockDataParser(blockData, palette); b.getBlockTypeIDs().forEach((v, i) => { const [x, y, z] = chunkCoordinateFromIndex(i); r[x][y + yy + 64][z] = v || 0; }); }); return r; } chunkData() { var _a; const sections = (_a = this.sections()) === null || _a === void 0 ? void 0 : _a.data.data; if (!sections) return this.root; [...this.blockStatesDirty.keys()].filter(k => this.blockStatesDirty.get(k)).forEach(yy => { const blocks = []; for (let y = 0; y < 16; ++y) for (let z = 0; z < 16; ++z) for (let x = 0; x < 16; ++x) blocks.push(this.blockStates.get(yy)[x][y][z]); const [r, palette] = __1.BlockDataParser.writeBlockStates(blocks, this.palettes.get(yy)); const index = sections.findIndex(x => { var _a; return ((_a = x.find(xx => xx.name === "Y")) === null || _a === void 0 ? void 0 : _a.data) === yy; }); this.root = ((palette === null || palette === void 0 ? void 0 : palette.data.data.length) || 0) > 1 ? __2.nbtTagReducer(this.root, { type: __2.NBTActions.NBT_ADD_TAG, overwrite: true, path: `sections/${index}/block_states`, tag: { type: __2.TagType.LONG_ARRAY, name: 'data', data: r } }) : (__2.findChildTagAtPath(`sections/${index}/block_states/data`, this.root) ? __2.nbtTagReducer(this.root, { type: __2.NBTActions.NBT_DELETE_TAG, recursive: true, path: `sections/${index}/block_states/data` }) : this.root); this.palettes.set(yy, palette); this.blockStates.delete(yy); this.root = __2.nbtTagReducer(this.root, { type: __2.NBTActions.NBT_ADD_TAG, overwrite: true, path: `sections/${index}/block_states`, tag: (this.palettes.get(yy) || __2.findChildTagAtPath(`sections/${index}/block_states/palette`, this.root)) }); if (__2.findChildTagAtPath(`sections/${index}/SkyLight`, this.root) !== undefined) this.root = __2.nbtTagReducer(this.root, { type: __2.NBTActions.NBT_DELETE_TAG, path: `sections/${index}/SkyLight` }); if (__2.findChildTagAtPath(`sections/${index}/BlockLight`, this.root) !== undefined) this.root = __2.nbtTagReducer(this.root, { type: __2.NBTActions.NBT_DELETE_TAG, path: `sections/${index}/BlockLight` }); this.blockStatesDirty.set(yy, false); }); if (__2.findChildTagAtPath("Heightmaps", this.root) !== undefined) this.root = __2.nbtTagReducer(this.root, { path: "Heightmaps", recursive: true, type: __2.NBTActions.NBT_DELETE_TAG }); this.root = __2.nbtTagReducer(this.root, { type: __2.NBTActions.NBT_EDIT_TAG, path: "Status", tag: { name: "Status", type: __2.TagType.STRING, data: "features" } }); return this.root; } } exports.Chunk = Chunk; Chunk.AIR = -968583441; Chunk.BLOCKS_PER_CHUNK = 4096; ; //# sourceMappingURL=chunk.js.map