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
JavaScript
"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