minerender 2.1.0-alpha.91 → 2.1.0-alpha.93
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/index.d.mts +28 -6
- package/dist/browser/index.d.ts +28 -6
- package/dist/browser/index.js +288 -197
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/index.mjs +288 -197
- package/dist/browser/index.mjs.map +1 -1
- package/dist/bundle.js +300 -204
- package/dist/bundle.js.map +1 -1
- package/dist/node/index.d.mts +28 -6
- package/dist/node/index.d.ts +28 -6
- package/dist/node/index.js +289 -197
- package/dist/node/index.js.map +1 -1
- package/dist/node/index.mjs +288 -197
- package/dist/node/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/node/index.mjs
CHANGED
|
@@ -23796,158 +23796,6 @@ function isBlockObject(obj) {
|
|
|
23796
23796
|
init_FluidGeometry();
|
|
23797
23797
|
init_FluidModelObject();
|
|
23798
23798
|
|
|
23799
|
-
// src/model/multiblock/LitematicaParser.ts
|
|
23800
|
-
init_MineRenderError();
|
|
23801
|
-
var LitematicaParser = class {
|
|
23802
|
-
/**
|
|
23803
|
-
* Parses decoded NBT, preserving subregion placement and omitting air.
|
|
23804
|
-
* Later entries in regions replace earlier cells, including with air.
|
|
23805
|
-
* Size encloses all region bounds; positions retain the schematic origin.
|
|
23806
|
-
* Metadata and scheduled ticks are not imported. Named regions can be placed individually.
|
|
23807
|
-
*/
|
|
23808
|
-
static async parse(nbt) {
|
|
23809
|
-
const tags = nbt.value;
|
|
23810
|
-
const version2 = integer2(tags.Version, "Version");
|
|
23811
|
-
if (version2 < 5 || version2 > 7) throw new MineRenderError(`Unsupported Litematica version ${version2}; expected 5, 6, or 7`);
|
|
23812
|
-
const dataVersion = tags.MinecraftDataVersion === void 0 ? void 0 : integer2(tags.MinecraftDataVersion, "MinecraftDataVersion");
|
|
23813
|
-
const blocks = /* @__PURE__ */ new Map();
|
|
23814
|
-
const regions = [];
|
|
23815
|
-
const min = [Infinity, Infinity, Infinity];
|
|
23816
|
-
const max = [-Infinity, -Infinity, -Infinity];
|
|
23817
|
-
for (const [name, tag] of Object.entries(compound(tags.Regions, "Regions"))) {
|
|
23818
|
-
const region = readRegion(compound(tag, `region ${name}`), name, dataVersion, blocks);
|
|
23819
|
-
regions.push(region);
|
|
23820
|
-
for (let axis = 0; axis < 3; axis++) {
|
|
23821
|
-
const start = minimum(region.position[axis], region.signedSize[axis]);
|
|
23822
|
-
min[axis] = Math.min(min[axis], start);
|
|
23823
|
-
max[axis] = Math.max(max[axis], start + region.size[axis]);
|
|
23824
|
-
}
|
|
23825
|
-
}
|
|
23826
|
-
return {
|
|
23827
|
-
size: regions.length ? min.map((value, axis) => max[axis] - value) : [0, 0, 0],
|
|
23828
|
-
blocks: [...blocks.values()],
|
|
23829
|
-
entities: regions.flatMap((region) => region.entities ?? []),
|
|
23830
|
-
dataVersion,
|
|
23831
|
-
regions
|
|
23832
|
-
};
|
|
23833
|
-
}
|
|
23834
|
-
};
|
|
23835
|
-
function readRegion(tags, name, dataVersion, combined) {
|
|
23836
|
-
const position = blockPosition(compound(tags.Position, `region ${name} Position`));
|
|
23837
|
-
const signedSize = blockPosition(compound(tags.Size, `region ${name} Size`));
|
|
23838
|
-
if (signedSize.some((value) => value === 0)) throw new MineRenderError(`Litematica region ${name} dimensions must be nonzero`);
|
|
23839
|
-
const size = signedSize.map(Math.abs);
|
|
23840
|
-
const min = position.map((value, axis) => minimum(value, signedSize[axis]));
|
|
23841
|
-
const [width, height, length] = size;
|
|
23842
|
-
const volume = width * height * length;
|
|
23843
|
-
const palette = compounds(tags.BlockStatePalette, "BlockStatePalette").map(blockState);
|
|
23844
|
-
if (!palette.length) throw new MineRenderError(`Litematica region ${name} has no block palette`);
|
|
23845
|
-
const bits = Math.max(2, Math.ceil(Math.log2(palette.length)));
|
|
23846
|
-
if (!Number.isSafeInteger(volume * bits)) throw new MineRenderError(`Litematica region ${name} volume is too large`);
|
|
23847
|
-
const packed = tags.BlockStates;
|
|
23848
|
-
const expected = Math.ceil(volume * bits / 64);
|
|
23849
|
-
if (packed?.type !== "longArray" || !Array.isArray(packed.value) || packed.value.length !== expected) {
|
|
23850
|
-
throw new MineRenderError(`Litematica region ${name} BlockStates must contain ${expected} longs`);
|
|
23851
|
-
}
|
|
23852
|
-
const words = packed.value.map((pair) => {
|
|
23853
|
-
if (!Array.isArray(pair) || pair.length !== 2 || !pair.every((value) => Number.isInteger(value) && value >= -2147483648 && value <= 4294967295)) {
|
|
23854
|
-
throw new MineRenderError(`Litematica region ${name} has an invalid BlockStates long`);
|
|
23855
|
-
}
|
|
23856
|
-
return [pair[0] >>> 0, pair[1] >>> 0];
|
|
23857
|
-
});
|
|
23858
|
-
const blockEntities = /* @__PURE__ */ new Map();
|
|
23859
|
-
for (const tile of compounds(tags.TileEntities, "TileEntities")) {
|
|
23860
|
-
const pos = blockPosition(tile);
|
|
23861
|
-
if (pos.some((value2, axis) => value2 < 0 || value2 >= size[axis])) {
|
|
23862
|
-
throw new MineRenderError(`Litematica region ${name} block entity is outside its dimensions`);
|
|
23863
|
-
}
|
|
23864
|
-
const index = pos[0] + pos[2] * width + pos[1] * width * length;
|
|
23865
|
-
if (blockEntities.has(index)) throw new MineRenderError(`Duplicate Litematica block entity at index ${index}`);
|
|
23866
|
-
const value = { ...tile };
|
|
23867
|
-
for (const [axis, key] of ["x", "y", "z"].entries()) value[key] = { type: "int", value: pos[axis] + min[axis] };
|
|
23868
|
-
blockEntities.set(index, { type: "compound", value });
|
|
23869
|
-
}
|
|
23870
|
-
const blocks = [];
|
|
23871
|
-
const mask = 2 ** bits - 1;
|
|
23872
|
-
for (let index = 0; index < volume; index++) {
|
|
23873
|
-
const word = Math.floor(index * bits / 64);
|
|
23874
|
-
const shift = index * bits % 64;
|
|
23875
|
-
const [high, low] = words[word];
|
|
23876
|
-
let value = shift < 32 ? low >>> shift : high >>> shift - 32;
|
|
23877
|
-
if (shift < 32 && shift + bits > 32) value |= high << 32 - shift;
|
|
23878
|
-
if (shift + bits > 64) value |= words[word + 1][1] << 64 - shift;
|
|
23879
|
-
const id = (value & mask) >>> 0;
|
|
23880
|
-
const state = palette[id];
|
|
23881
|
-
if (!state) throw new MineRenderError(`Litematica region ${name} palette index ${id} does not exist`);
|
|
23882
|
-
const pos = [
|
|
23883
|
-
index % width + min[0],
|
|
23884
|
-
Math.floor(index / (width * length)) + min[1],
|
|
23885
|
-
Math.floor(index / width) % length + min[2]
|
|
23886
|
-
];
|
|
23887
|
-
const key = pos.join(",");
|
|
23888
|
-
if (["minecraft:air", "minecraft:cave_air", "minecraft:void_air"].includes(state.type)) {
|
|
23889
|
-
combined.delete(key);
|
|
23890
|
-
continue;
|
|
23891
|
-
}
|
|
23892
|
-
const block = { type: state.type, properties: { ...state.properties }, position: pos, nbt: blockEntities.get(index) };
|
|
23893
|
-
blocks.push(block);
|
|
23894
|
-
combined.set(key, block);
|
|
23895
|
-
}
|
|
23896
|
-
const entities = compounds(tags.Entities, "Entities").map((entity) => {
|
|
23897
|
-
const pos = entity.Pos;
|
|
23898
|
-
if (pos?.type !== "list" || pos.value.type !== "double" || pos.value.value.length !== 3 || !pos.value.value.every(Number.isFinite)) {
|
|
23899
|
-
throw new MineRenderError(`Litematica region ${name} entity Pos must contain three doubles`);
|
|
23900
|
-
}
|
|
23901
|
-
const absolute = pos.value.value.map((value2, axis) => value2 + position[axis]);
|
|
23902
|
-
const value = { ...entity, Pos: { type: "list", value: { type: "double", value: [...absolute] } } };
|
|
23903
|
-
return { position: absolute, nbt: { type: "compound", value } };
|
|
23904
|
-
});
|
|
23905
|
-
return { name, position, signedSize, size, blocks, entities, dataVersion };
|
|
23906
|
-
}
|
|
23907
|
-
function minimum(position, size) {
|
|
23908
|
-
return position + Math.min(0, size + 1);
|
|
23909
|
-
}
|
|
23910
|
-
function integer2(tag, name) {
|
|
23911
|
-
if (tag?.type !== "int" || !Number.isInteger(tag.value) || tag.value < -2147483648 || tag.value > 2147483647) {
|
|
23912
|
-
throw new MineRenderError(`Litematica ${name} must be an int`);
|
|
23913
|
-
}
|
|
23914
|
-
return tag.value;
|
|
23915
|
-
}
|
|
23916
|
-
function compound(tag, name) {
|
|
23917
|
-
if (tag?.type !== "compound" || !tag.value || typeof tag.value !== "object" || Array.isArray(tag.value)) {
|
|
23918
|
-
throw new MineRenderError(`Litematica ${name} must be a compound`);
|
|
23919
|
-
}
|
|
23920
|
-
return tag.value;
|
|
23921
|
-
}
|
|
23922
|
-
function blockPosition(tags) {
|
|
23923
|
-
return ["x", "y", "z"].map((axis) => integer2(tags[axis], axis));
|
|
23924
|
-
}
|
|
23925
|
-
function compounds(tag, name) {
|
|
23926
|
-
if (tag === void 0) return [];
|
|
23927
|
-
if (tag.type === "list" && Array.isArray(tag.value.value)) {
|
|
23928
|
-
if (tag.value.value.length === 0) return [];
|
|
23929
|
-
if (tag.value.type === "compound") return tag.value.value;
|
|
23930
|
-
}
|
|
23931
|
-
throw new MineRenderError(`Litematica ${name} must be a compound list`);
|
|
23932
|
-
}
|
|
23933
|
-
function blockState(tags) {
|
|
23934
|
-
const name = tags.Name;
|
|
23935
|
-
if (name?.type !== "string" || !/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(name.value)) {
|
|
23936
|
-
throw new MineRenderError("Litematica palette entry must have a block Name");
|
|
23937
|
-
}
|
|
23938
|
-
const properties = {};
|
|
23939
|
-
if (tags.Properties !== void 0) {
|
|
23940
|
-
for (const [key, tag] of Object.entries(compound(tags.Properties, "block Properties"))) {
|
|
23941
|
-
if (tag?.type !== "string") throw new MineRenderError("Litematica block properties must be strings");
|
|
23942
|
-
Object.defineProperty(properties, key, { value: tag.value, enumerable: true, writable: true, configurable: true });
|
|
23943
|
-
}
|
|
23944
|
-
}
|
|
23945
|
-
return { type: name.value.includes(":") ? name.value : `minecraft:${name.value}`, properties };
|
|
23946
|
-
}
|
|
23947
|
-
|
|
23948
|
-
// src/model/multiblock/SchematicParser.ts
|
|
23949
|
-
init_MineRenderError();
|
|
23950
|
-
|
|
23951
23799
|
// src/model/multiblock/legacyBlocks.json
|
|
23952
23800
|
var legacyBlocks_default = {
|
|
23953
23801
|
__comment: "Block mappings from res/idsToNames.json (WorldEdit legacy.json); block names and cauldrons updated for Minecraft 1.21.11. Source: https://github.com/sk89q/WorldEdit/blob/master/worldedit-core/src/main/resources/com/sk89q/worldedit/world/registry/legacy.json",
|
|
@@ -24791,12 +24639,20 @@ var legacyBlocks_default = {
|
|
|
24791
24639
|
"131:15": "minecraft:tripwire_hook[powered=true,attached=true,facing=east]",
|
|
24792
24640
|
"132:0": "minecraft:tripwire[disarmed=false,east=false,powered=false,south=false,north=false,west=false,attached=false]",
|
|
24793
24641
|
"132:1": "minecraft:tripwire[disarmed=false,east=false,powered=true,south=false,north=false,west=false,attached=false]",
|
|
24642
|
+
"132:2": "minecraft:tripwire[disarmed=false,east=false,powered=false,south=false,north=false,west=false,attached=false]",
|
|
24643
|
+
"132:3": "minecraft:tripwire[disarmed=false,east=false,powered=true,south=false,north=false,west=false,attached=false]",
|
|
24794
24644
|
"132:4": "minecraft:tripwire[disarmed=false,east=false,powered=false,south=false,north=false,west=false,attached=true]",
|
|
24795
24645
|
"132:5": "minecraft:tripwire[disarmed=false,east=false,powered=true,south=false,north=false,west=false,attached=true]",
|
|
24646
|
+
"132:6": "minecraft:tripwire[disarmed=false,east=false,powered=false,south=false,north=false,west=false,attached=true]",
|
|
24647
|
+
"132:7": "minecraft:tripwire[disarmed=false,east=false,powered=true,south=false,north=false,west=false,attached=true]",
|
|
24796
24648
|
"132:8": "minecraft:tripwire[disarmed=true,east=false,powered=false,south=false,north=false,west=false,attached=false]",
|
|
24797
24649
|
"132:9": "minecraft:tripwire[disarmed=true,east=false,powered=true,south=false,north=false,west=false,attached=false]",
|
|
24650
|
+
"132:10": "minecraft:tripwire[disarmed=true,east=false,powered=false,south=false,north=false,west=false,attached=false]",
|
|
24651
|
+
"132:11": "minecraft:tripwire[disarmed=true,east=false,powered=true,south=false,north=false,west=false,attached=false]",
|
|
24798
24652
|
"132:12": "minecraft:tripwire[disarmed=true,east=false,powered=false,south=false,north=false,west=false,attached=true]",
|
|
24799
24653
|
"132:13": "minecraft:tripwire[disarmed=true,east=false,powered=true,south=false,north=false,west=false,attached=true]",
|
|
24654
|
+
"132:14": "minecraft:tripwire[disarmed=true,east=false,powered=false,south=false,north=false,west=false,attached=true]",
|
|
24655
|
+
"132:15": "minecraft:tripwire[disarmed=true,east=false,powered=true,south=false,north=false,west=false,attached=true]",
|
|
24800
24656
|
"133:0": "minecraft:emerald_block",
|
|
24801
24657
|
"134:0": "minecraft:spruce_stairs[half=bottom,shape=outer_right,facing=east]",
|
|
24802
24658
|
"134:1": "minecraft:spruce_stairs[half=bottom,shape=outer_right,facing=west]",
|
|
@@ -25637,7 +25493,175 @@ var legacyBlocks_default = {
|
|
|
25637
25493
|
}
|
|
25638
25494
|
};
|
|
25639
25495
|
|
|
25496
|
+
// src/model/multiblock/LegacyBlocks.ts
|
|
25497
|
+
function resolveLegacyBlock(id, metadata, customMappings = {}, lenient = false) {
|
|
25498
|
+
const mapping = legacyBlocks_default.blocks;
|
|
25499
|
+
const key = `${id}:${metadata}`;
|
|
25500
|
+
let mapped = customMappings[key] ?? mapping[key];
|
|
25501
|
+
if (!mapped && lenient) mapped = customMappings[`${id}:0`] ?? mapping[`${id}:0`];
|
|
25502
|
+
if (!mapped) return void 0;
|
|
25503
|
+
const [type, state] = mapped.split("[");
|
|
25504
|
+
const properties = {};
|
|
25505
|
+
if (state) {
|
|
25506
|
+
for (const property of state.slice(0, -1).split(",")) {
|
|
25507
|
+
const [key2, value] = property.split("=");
|
|
25508
|
+
properties[key2] = value;
|
|
25509
|
+
}
|
|
25510
|
+
}
|
|
25511
|
+
return { type, properties };
|
|
25512
|
+
}
|
|
25513
|
+
|
|
25514
|
+
// src/model/multiblock/LitematicaParser.ts
|
|
25515
|
+
init_MineRenderError();
|
|
25516
|
+
var LitematicaParser = class {
|
|
25517
|
+
/**
|
|
25518
|
+
* Parses decoded NBT, preserving subregion placement and omitting air.
|
|
25519
|
+
* Later entries in regions replace earlier cells, including with air.
|
|
25520
|
+
* Size encloses all region bounds; positions retain the schematic origin.
|
|
25521
|
+
* Metadata and scheduled ticks are not imported. Named regions can be placed individually.
|
|
25522
|
+
*/
|
|
25523
|
+
static async parse(nbt) {
|
|
25524
|
+
const tags = nbt.value;
|
|
25525
|
+
const version2 = integer2(tags.Version, "Version");
|
|
25526
|
+
if (version2 < 5 || version2 > 7) throw new MineRenderError(`Unsupported Litematica version ${version2}; expected 5, 6, or 7`);
|
|
25527
|
+
const dataVersion = tags.MinecraftDataVersion === void 0 ? void 0 : integer2(tags.MinecraftDataVersion, "MinecraftDataVersion");
|
|
25528
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
25529
|
+
const regions = [];
|
|
25530
|
+
const min = [Infinity, Infinity, Infinity];
|
|
25531
|
+
const max = [-Infinity, -Infinity, -Infinity];
|
|
25532
|
+
for (const [name, tag] of Object.entries(compound(tags.Regions, "Regions"))) {
|
|
25533
|
+
const region = readRegion(compound(tag, `region ${name}`), name, dataVersion, blocks);
|
|
25534
|
+
regions.push(region);
|
|
25535
|
+
for (let axis = 0; axis < 3; axis++) {
|
|
25536
|
+
const start = minimum(region.position[axis], region.signedSize[axis]);
|
|
25537
|
+
min[axis] = Math.min(min[axis], start);
|
|
25538
|
+
max[axis] = Math.max(max[axis], start + region.size[axis]);
|
|
25539
|
+
}
|
|
25540
|
+
}
|
|
25541
|
+
return {
|
|
25542
|
+
size: regions.length ? min.map((value, axis) => max[axis] - value) : [0, 0, 0],
|
|
25543
|
+
blocks: [...blocks.values()],
|
|
25544
|
+
entities: regions.flatMap((region) => region.entities ?? []),
|
|
25545
|
+
dataVersion,
|
|
25546
|
+
regions
|
|
25547
|
+
};
|
|
25548
|
+
}
|
|
25549
|
+
};
|
|
25550
|
+
function readRegion(tags, name, dataVersion, combined) {
|
|
25551
|
+
const position = blockPosition(compound(tags.Position, `region ${name} Position`));
|
|
25552
|
+
const signedSize = blockPosition(compound(tags.Size, `region ${name} Size`));
|
|
25553
|
+
if (signedSize.some((value) => value === 0)) throw new MineRenderError(`Litematica region ${name} dimensions must be nonzero`);
|
|
25554
|
+
const size = signedSize.map(Math.abs);
|
|
25555
|
+
const min = position.map((value, axis) => minimum(value, signedSize[axis]));
|
|
25556
|
+
const [width, height, length] = size;
|
|
25557
|
+
const volume = width * height * length;
|
|
25558
|
+
const palette = compounds(tags.BlockStatePalette, "BlockStatePalette").map(blockState);
|
|
25559
|
+
if (!palette.length) throw new MineRenderError(`Litematica region ${name} has no block palette`);
|
|
25560
|
+
const bits = Math.max(2, Math.ceil(Math.log2(palette.length)));
|
|
25561
|
+
if (!Number.isSafeInteger(volume * bits)) throw new MineRenderError(`Litematica region ${name} volume is too large`);
|
|
25562
|
+
const packed = tags.BlockStates;
|
|
25563
|
+
const expected = Math.ceil(volume * bits / 64);
|
|
25564
|
+
if (packed?.type !== "longArray" || !Array.isArray(packed.value) || packed.value.length !== expected) {
|
|
25565
|
+
throw new MineRenderError(`Litematica region ${name} BlockStates must contain ${expected} longs`);
|
|
25566
|
+
}
|
|
25567
|
+
const words = packed.value.map((pair) => {
|
|
25568
|
+
if (!Array.isArray(pair) || pair.length !== 2 || !pair.every((value) => Number.isInteger(value) && value >= -2147483648 && value <= 4294967295)) {
|
|
25569
|
+
throw new MineRenderError(`Litematica region ${name} has an invalid BlockStates long`);
|
|
25570
|
+
}
|
|
25571
|
+
return [pair[0] >>> 0, pair[1] >>> 0];
|
|
25572
|
+
});
|
|
25573
|
+
const blockEntities = /* @__PURE__ */ new Map();
|
|
25574
|
+
for (const tile of compounds(tags.TileEntities, "TileEntities")) {
|
|
25575
|
+
const pos = blockPosition(tile);
|
|
25576
|
+
if (pos.some((value2, axis) => value2 < 0 || value2 >= size[axis])) {
|
|
25577
|
+
throw new MineRenderError(`Litematica region ${name} block entity is outside its dimensions`);
|
|
25578
|
+
}
|
|
25579
|
+
const index = pos[0] + pos[2] * width + pos[1] * width * length;
|
|
25580
|
+
if (blockEntities.has(index)) throw new MineRenderError(`Duplicate Litematica block entity at index ${index}`);
|
|
25581
|
+
const value = { ...tile };
|
|
25582
|
+
for (const [axis, key] of ["x", "y", "z"].entries()) value[key] = { type: "int", value: pos[axis] + min[axis] };
|
|
25583
|
+
blockEntities.set(index, { type: "compound", value });
|
|
25584
|
+
}
|
|
25585
|
+
const blocks = [];
|
|
25586
|
+
const mask = 2 ** bits - 1;
|
|
25587
|
+
for (let index = 0; index < volume; index++) {
|
|
25588
|
+
const word = Math.floor(index * bits / 64);
|
|
25589
|
+
const shift = index * bits % 64;
|
|
25590
|
+
const [high, low] = words[word];
|
|
25591
|
+
let value = shift < 32 ? low >>> shift : high >>> shift - 32;
|
|
25592
|
+
if (shift < 32 && shift + bits > 32) value |= high << 32 - shift;
|
|
25593
|
+
if (shift + bits > 64) value |= words[word + 1][1] << 64 - shift;
|
|
25594
|
+
const id = (value & mask) >>> 0;
|
|
25595
|
+
const state = palette[id];
|
|
25596
|
+
if (!state) throw new MineRenderError(`Litematica region ${name} palette index ${id} does not exist`);
|
|
25597
|
+
const pos = [
|
|
25598
|
+
index % width + min[0],
|
|
25599
|
+
Math.floor(index / (width * length)) + min[1],
|
|
25600
|
+
Math.floor(index / width) % length + min[2]
|
|
25601
|
+
];
|
|
25602
|
+
const key = pos.join(",");
|
|
25603
|
+
if (["minecraft:air", "minecraft:cave_air", "minecraft:void_air"].includes(state.type)) {
|
|
25604
|
+
combined.delete(key);
|
|
25605
|
+
continue;
|
|
25606
|
+
}
|
|
25607
|
+
const block = { type: state.type, properties: { ...state.properties }, position: pos, nbt: blockEntities.get(index) };
|
|
25608
|
+
blocks.push(block);
|
|
25609
|
+
combined.set(key, block);
|
|
25610
|
+
}
|
|
25611
|
+
const entities = compounds(tags.Entities, "Entities").map((entity) => {
|
|
25612
|
+
const pos = entity.Pos;
|
|
25613
|
+
if (pos?.type !== "list" || pos.value.type !== "double" || pos.value.value.length !== 3 || !pos.value.value.every(Number.isFinite)) {
|
|
25614
|
+
throw new MineRenderError(`Litematica region ${name} entity Pos must contain three doubles`);
|
|
25615
|
+
}
|
|
25616
|
+
const absolute = pos.value.value.map((value2, axis) => value2 + position[axis]);
|
|
25617
|
+
const value = { ...entity, Pos: { type: "list", value: { type: "double", value: [...absolute] } } };
|
|
25618
|
+
return { position: absolute, nbt: { type: "compound", value } };
|
|
25619
|
+
});
|
|
25620
|
+
return { name, position, signedSize, size, blocks, entities, dataVersion };
|
|
25621
|
+
}
|
|
25622
|
+
function minimum(position, size) {
|
|
25623
|
+
return position + Math.min(0, size + 1);
|
|
25624
|
+
}
|
|
25625
|
+
function integer2(tag, name) {
|
|
25626
|
+
if (tag?.type !== "int" || !Number.isInteger(tag.value) || tag.value < -2147483648 || tag.value > 2147483647) {
|
|
25627
|
+
throw new MineRenderError(`Litematica ${name} must be an int`);
|
|
25628
|
+
}
|
|
25629
|
+
return tag.value;
|
|
25630
|
+
}
|
|
25631
|
+
function compound(tag, name) {
|
|
25632
|
+
if (tag?.type !== "compound" || !tag.value || typeof tag.value !== "object" || Array.isArray(tag.value)) {
|
|
25633
|
+
throw new MineRenderError(`Litematica ${name} must be a compound`);
|
|
25634
|
+
}
|
|
25635
|
+
return tag.value;
|
|
25636
|
+
}
|
|
25637
|
+
function blockPosition(tags) {
|
|
25638
|
+
return ["x", "y", "z"].map((axis) => integer2(tags[axis], axis));
|
|
25639
|
+
}
|
|
25640
|
+
function compounds(tag, name) {
|
|
25641
|
+
if (tag === void 0) return [];
|
|
25642
|
+
if (tag.type === "list" && Array.isArray(tag.value.value)) {
|
|
25643
|
+
if (tag.value.value.length === 0) return [];
|
|
25644
|
+
if (tag.value.type === "compound") return tag.value.value;
|
|
25645
|
+
}
|
|
25646
|
+
throw new MineRenderError(`Litematica ${name} must be a compound list`);
|
|
25647
|
+
}
|
|
25648
|
+
function blockState(tags) {
|
|
25649
|
+
const name = tags.Name;
|
|
25650
|
+
if (name?.type !== "string" || !/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(name.value)) {
|
|
25651
|
+
throw new MineRenderError("Litematica palette entry must have a block Name");
|
|
25652
|
+
}
|
|
25653
|
+
const properties = {};
|
|
25654
|
+
if (tags.Properties !== void 0) {
|
|
25655
|
+
for (const [key, tag] of Object.entries(compound(tags.Properties, "block Properties"))) {
|
|
25656
|
+
if (tag?.type !== "string") throw new MineRenderError("Litematica block properties must be strings");
|
|
25657
|
+
Object.defineProperty(properties, key, { value: tag.value, enumerable: true, writable: true, configurable: true });
|
|
25658
|
+
}
|
|
25659
|
+
}
|
|
25660
|
+
return { type: name.value.includes(":") ? name.value : `minecraft:${name.value}`, properties };
|
|
25661
|
+
}
|
|
25662
|
+
|
|
25640
25663
|
// src/model/multiblock/SchematicParser.ts
|
|
25664
|
+
init_MineRenderError();
|
|
25641
25665
|
var SchematicParser = class {
|
|
25642
25666
|
/**
|
|
25643
25667
|
* Parses a legacy schematic using custom mappings before the bundled defaults.
|
|
@@ -25679,35 +25703,21 @@ var SchematicParser = class {
|
|
|
25679
25703
|
tileEntities.set((y * length + z) * width + x, { type: "compound", value: tile });
|
|
25680
25704
|
}
|
|
25681
25705
|
const blocks = [];
|
|
25682
|
-
const mapping = legacyBlocks_default.blocks;
|
|
25683
25706
|
for (let index = 0; index < volume; index++) {
|
|
25684
25707
|
const packed = addBlocks?.value[index >> 1] ?? 0;
|
|
25685
25708
|
const high = packed >> (index & 1) * 4 & 15;
|
|
25686
25709
|
const id = high << 8 | ids.value[index] & 255;
|
|
25687
25710
|
const metadata = data.value[index] & 255;
|
|
25688
|
-
const
|
|
25689
|
-
|
|
25690
|
-
if (!mapped && options.lenient) {
|
|
25691
|
-
mapped = customMappings[`${id}:0`] ?? mapping[`${id}:0`];
|
|
25692
|
-
}
|
|
25693
|
-
if (!mapped) {
|
|
25711
|
+
const block = resolveLegacyBlock(id, metadata, customMappings, options.lenient);
|
|
25712
|
+
if (!block) {
|
|
25694
25713
|
if (options.lenient) {
|
|
25695
25714
|
continue;
|
|
25696
25715
|
}
|
|
25697
25716
|
throw new MineRenderError(`Unsupported legacy block ${id}:${metadata} at schematic index ${index}`);
|
|
25698
25717
|
}
|
|
25699
|
-
|
|
25700
|
-
if (type === "minecraft:air") continue;
|
|
25701
|
-
const properties = {};
|
|
25702
|
-
if (state) {
|
|
25703
|
-
for (const property of state.slice(0, -1).split(",")) {
|
|
25704
|
-
const [key2, value] = property.split("=");
|
|
25705
|
-
properties[key2] = value;
|
|
25706
|
-
}
|
|
25707
|
-
}
|
|
25718
|
+
if (block.type === "minecraft:air") continue;
|
|
25708
25719
|
blocks.push({
|
|
25709
|
-
|
|
25710
|
-
properties,
|
|
25720
|
+
...block,
|
|
25711
25721
|
position: [index % width, Math.floor(index / (width * length)), Math.floor(index / width) % length],
|
|
25712
25722
|
nbt: tileEntities.get(index)
|
|
25713
25723
|
});
|
|
@@ -29097,10 +29107,13 @@ var AnvilParser = class {
|
|
|
29097
29107
|
return this.locations(this.bytes(data)).map(({ x, z }) => ({ x, z }));
|
|
29098
29108
|
}
|
|
29099
29109
|
/** Decodes all stored chunks. Gzip, zlib, LZ4, and uncompressed payloads are supported. */
|
|
29100
|
-
static async parse(data) {
|
|
29110
|
+
static async parse(data, options = {}) {
|
|
29111
|
+
options.signal?.throwIfAborted();
|
|
29101
29112
|
const bytes = this.bytes(data);
|
|
29102
29113
|
const chunks = [];
|
|
29103
|
-
for (const location of this.locations(bytes))
|
|
29114
|
+
for (const location of this.locations(bytes)) {
|
|
29115
|
+
chunks.push(await this.abortable(this.readChunk(bytes, location, options), options.signal));
|
|
29116
|
+
}
|
|
29104
29117
|
return { chunks };
|
|
29105
29118
|
}
|
|
29106
29119
|
/**
|
|
@@ -29109,13 +29122,28 @@ var AnvilParser = class {
|
|
|
29109
29122
|
* @param localZ - Chunk z within the region, from 0 to 31.
|
|
29110
29123
|
* @returns The chunk with absolute coordinates, or `undefined` if the region has no chunk there.
|
|
29111
29124
|
*/
|
|
29112
|
-
static async parseChunk(data, localX, localZ) {
|
|
29125
|
+
static async parseChunk(data, localX, localZ, options = {}) {
|
|
29126
|
+
options.signal?.throwIfAborted();
|
|
29113
29127
|
if (![localX, localZ].every((value) => Number.isInteger(value) && value >= 0 && value < 32)) {
|
|
29114
29128
|
throw new RangeError("Region-local chunk coordinates must be integers from 0 to 31");
|
|
29115
29129
|
}
|
|
29116
29130
|
const bytes = this.bytes(data);
|
|
29117
29131
|
const location = this.locations(bytes).find(({ x, z }) => x === localX && z === localZ);
|
|
29118
|
-
return location ? this.readChunk(bytes, location) : void 0;
|
|
29132
|
+
return location ? this.abortable(this.readChunk(bytes, location, options), options.signal) : void 0;
|
|
29133
|
+
}
|
|
29134
|
+
static async abortable(promise, signal) {
|
|
29135
|
+
if (!signal) return promise;
|
|
29136
|
+
let abort;
|
|
29137
|
+
const cancelled = new Promise((_, reject) => {
|
|
29138
|
+
abort = () => reject(signal.reason);
|
|
29139
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
29140
|
+
if (signal.aborted) abort();
|
|
29141
|
+
});
|
|
29142
|
+
try {
|
|
29143
|
+
return await Promise.race([promise, cancelled]);
|
|
29144
|
+
} finally {
|
|
29145
|
+
signal.removeEventListener("abort", abort);
|
|
29146
|
+
}
|
|
29119
29147
|
}
|
|
29120
29148
|
static bytes(data) {
|
|
29121
29149
|
return data instanceof Uint8Array ? data : new Uint8Array(data);
|
|
@@ -29141,20 +29169,41 @@ var AnvilParser = class {
|
|
|
29141
29169
|
}
|
|
29142
29170
|
return locations;
|
|
29143
29171
|
}
|
|
29144
|
-
static async readChunk(data, location) {
|
|
29145
|
-
|
|
29146
|
-
|
|
29172
|
+
static async readChunk(data, location, options) {
|
|
29173
|
+
options.signal?.throwIfAborted();
|
|
29174
|
+
const format2 = data[location.offset + 4];
|
|
29175
|
+
const external = (format2 & 128) !== 0;
|
|
29176
|
+
const compression = format2 & 127;
|
|
29147
29177
|
if (compression !== 1 && compression !== 2 && compression !== 3 && compression !== 4) {
|
|
29148
29178
|
throw new MineRenderError(`Unsupported Anvil compression ${compression}`);
|
|
29149
29179
|
}
|
|
29150
29180
|
let payload = data.subarray(location.offset + 5, location.offset + 4 + location.length);
|
|
29181
|
+
let expected;
|
|
29182
|
+
if (external) {
|
|
29183
|
+
if (!options.region || !options.readExternalChunk) {
|
|
29184
|
+
throw new MineRenderError("External Anvil .mcc chunks require region coordinates and readExternalChunk");
|
|
29185
|
+
}
|
|
29186
|
+
if (![options.region.x, options.region.z].every(Number.isSafeInteger)) {
|
|
29187
|
+
throw new RangeError("Anvil region coordinates must be safe integers");
|
|
29188
|
+
}
|
|
29189
|
+
expected = { x: options.region.x * 32 + location.x, z: options.region.z * 32 + location.z };
|
|
29190
|
+
if (![expected.x, expected.z].every(Number.isSafeInteger)) {
|
|
29191
|
+
throw new RangeError("External Anvil chunk coordinates must be safe integers");
|
|
29192
|
+
}
|
|
29193
|
+
const externalData = await options.readExternalChunk(expected.x, expected.z, options.signal);
|
|
29194
|
+
options.signal?.throwIfAborted();
|
|
29195
|
+
if (externalData === void 0) throw new MineRenderError(`External Anvil chunk c.${expected.x}.${expected.z}.mcc is missing`);
|
|
29196
|
+
payload = this.bytes(externalData);
|
|
29197
|
+
}
|
|
29151
29198
|
if (compression === 4) {
|
|
29152
29199
|
payload = decodeAnvilLz4(payload);
|
|
29153
29200
|
} else if (compression !== 3) {
|
|
29154
29201
|
const stream = new Blob([payload]).stream().pipeThrough(new DecompressionStream(compression === 1 ? "gzip" : "deflate"));
|
|
29155
29202
|
payload = new Uint8Array(await new Response(stream).arrayBuffer());
|
|
29156
29203
|
}
|
|
29204
|
+
options.signal?.throwIfAborted();
|
|
29157
29205
|
const nbt = await NBTHelper.fromBuffer(payload, "big");
|
|
29206
|
+
options.signal?.throwIfAborted();
|
|
29158
29207
|
const root = nbt.value;
|
|
29159
29208
|
const level = root.Level?.type === "compound" ? root.Level.value : root;
|
|
29160
29209
|
const dataVersion = root.DataVersion?.type === "int" ? root.DataVersion.value : void 0;
|
|
@@ -29167,28 +29216,37 @@ var AnvilParser = class {
|
|
|
29167
29216
|
if ((chunk.x % 32 + 32) % 32 !== location.x || (chunk.z % 32 + 32) % 32 !== location.z) {
|
|
29168
29217
|
throw new MineRenderError("Anvil chunk coordinates do not match its region location");
|
|
29169
29218
|
}
|
|
29219
|
+
if (expected && (chunk.x !== expected.x || chunk.z !== expected.z)) {
|
|
29220
|
+
throw new MineRenderError(`Anvil chunk coordinates do not match external file c.${expected.x}.${expected.z}.mcc`);
|
|
29221
|
+
}
|
|
29222
|
+
const numericStates = /* @__PURE__ */ new Map();
|
|
29170
29223
|
for (const section of this.compounds(level.sections ?? level.Sections, "sections")) {
|
|
29171
29224
|
const y = this.integer(section.Y, "section Y");
|
|
29225
|
+
let data2;
|
|
29172
29226
|
if (section.Blocks || section.Data || section.Add) {
|
|
29173
|
-
|
|
29174
|
-
|
|
29175
|
-
|
|
29176
|
-
|
|
29177
|
-
}
|
|
29178
|
-
|
|
29179
|
-
|
|
29180
|
-
|
|
29181
|
-
|
|
29182
|
-
|
|
29183
|
-
const
|
|
29184
|
-
|
|
29185
|
-
|
|
29186
|
-
const
|
|
29187
|
-
|
|
29188
|
-
|
|
29227
|
+
if (section.Palette || section.BlockStates || section.block_states) {
|
|
29228
|
+
throw new MineRenderError(`Anvil section ${y} mixes numeric and paletted block states`);
|
|
29229
|
+
}
|
|
29230
|
+
data2 = this.numericSection(section, y, options, numericStates);
|
|
29231
|
+
} else {
|
|
29232
|
+
if (section.block_states && section.block_states.type !== "compound") {
|
|
29233
|
+
throw new MineRenderError("Anvil block_states must be a compound");
|
|
29234
|
+
}
|
|
29235
|
+
const modern = section.block_states?.type === "compound" ? section.block_states.value : void 0;
|
|
29236
|
+
const paletteTag = modern ? modern.palette : section.Palette;
|
|
29237
|
+
const palette = paletteTag?.type === "list" && paletteTag.value.type === "string" ? paletteTag.value.value : this.compounds(paletteTag, "block palette");
|
|
29238
|
+
data2 = new ChunkData();
|
|
29239
|
+
if (palette.length) {
|
|
29240
|
+
const states = palette.map((entry) => this.block(entry));
|
|
29241
|
+
const indices = this.unpack(modern ? modern.data : section.BlockStates, states.length, !!modern, dataVersion);
|
|
29242
|
+
for (let index = 0; index < 4096; index++) {
|
|
29243
|
+
const state = states[indices[index]];
|
|
29244
|
+
if (!state) throw new MineRenderError(`Anvil palette index ${indices[index]} is out of range`);
|
|
29245
|
+
if (!ChunkData.isAir(state)) data2.set(index, state);
|
|
29246
|
+
}
|
|
29247
|
+
} else if (modern || section.Palette || section.BlockStates) {
|
|
29248
|
+
throw new MineRenderError("Anvil section has block states without a palette");
|
|
29189
29249
|
}
|
|
29190
|
-
} else if (modern || section.Palette || section.BlockStates) {
|
|
29191
|
-
throw new MineRenderError("Anvil section has block states without a palette");
|
|
29192
29250
|
}
|
|
29193
29251
|
if (chunk.sections.some((section2) => section2.y === y)) throw new MineRenderError(`Duplicate Anvil section Y ${y}`);
|
|
29194
29252
|
chunk.sections.push({ y, data: data2 });
|
|
@@ -29216,6 +29274,33 @@ var AnvilParser = class {
|
|
|
29216
29274
|
}
|
|
29217
29275
|
return chunk;
|
|
29218
29276
|
}
|
|
29277
|
+
static numericSection(section, y, options, states) {
|
|
29278
|
+
const bytes = (name, length, optional = false) => {
|
|
29279
|
+
const tag = section[name];
|
|
29280
|
+
if (!tag && optional) return void 0;
|
|
29281
|
+
if (tag?.type !== "byteArray" || !Array.isArray(tag.value) || tag.value.length !== length) {
|
|
29282
|
+
throw new MineRenderError(`Anvil section ${y} ${name} must contain ${length} bytes`);
|
|
29283
|
+
}
|
|
29284
|
+
return tag.value;
|
|
29285
|
+
};
|
|
29286
|
+
const ids = bytes("Blocks", 4096);
|
|
29287
|
+
const metadata = bytes("Data", 2048, true);
|
|
29288
|
+
const extra = bytes("Add", 2048, true);
|
|
29289
|
+
const data = new ChunkData();
|
|
29290
|
+
for (let index = 0; index < 4096; index++) {
|
|
29291
|
+
const shift = (index & 1) * 4;
|
|
29292
|
+
const id = ids[index] & 255 | ((extra?.[index >> 1] ?? 0) >> shift & 15) << 8;
|
|
29293
|
+
const value = (metadata?.[index >> 1] ?? 0) >> shift & 15;
|
|
29294
|
+
const key = id * 16 + value;
|
|
29295
|
+
if (!states.has(key)) states.set(key, resolveLegacyBlock(id, value, options.legacyMappings, options.lenient));
|
|
29296
|
+
const block = states.get(key);
|
|
29297
|
+
if (!block && !options.lenient) {
|
|
29298
|
+
throw new MineRenderError(`Unsupported legacy block ${id}:${value} at Anvil section ${y} index ${index}`);
|
|
29299
|
+
}
|
|
29300
|
+
if (block && !ChunkData.isAir(block)) data.set(index, block);
|
|
29301
|
+
}
|
|
29302
|
+
return data;
|
|
29303
|
+
}
|
|
29219
29304
|
static compounds(tag, name) {
|
|
29220
29305
|
if (!tag) return [];
|
|
29221
29306
|
if (tag.type !== "list" || tag.value.type !== "compound" && tag.value.value.length !== 0) {
|
|
@@ -29298,6 +29383,7 @@ var AnvilWorldSource = class {
|
|
|
29298
29383
|
this.cachedBytes = 0;
|
|
29299
29384
|
this.maxCachedRegions = options.maxCachedRegions ?? 4;
|
|
29300
29385
|
this.maxCachedBytes = options.maxCachedBytes ?? 64 * 1024 * 1024;
|
|
29386
|
+
this.parseOptions = { readExternalChunk: options.readExternalChunk, legacyMappings: options.legacyMappings, lenient: options.lenient };
|
|
29301
29387
|
for (const [name, value] of Object.entries({ maxCachedRegions: this.maxCachedRegions, maxCachedBytes: this.maxCachedBytes })) {
|
|
29302
29388
|
if (!Number.isSafeInteger(value) || value < 0) throw new RangeError(`${name} must be a nonnegative safe integer`);
|
|
29303
29389
|
}
|
|
@@ -29311,7 +29397,11 @@ var AnvilWorldSource = class {
|
|
|
29311
29397
|
const data = await this.getRegion(regionX, regionZ, signal);
|
|
29312
29398
|
signal?.throwIfAborted();
|
|
29313
29399
|
if (!data) return void 0;
|
|
29314
|
-
const chunk = await
|
|
29400
|
+
const chunk = await AnvilParser.parseChunk(data, x - regionX * 32, z - regionZ * 32, {
|
|
29401
|
+
...this.parseOptions,
|
|
29402
|
+
region: { x: regionX, z: regionZ },
|
|
29403
|
+
signal
|
|
29404
|
+
});
|
|
29315
29405
|
signal?.throwIfAborted();
|
|
29316
29406
|
if (chunk && (chunk.x !== x || chunk.z !== z)) {
|
|
29317
29407
|
throw new MineRenderError(`Anvil chunk coordinates ${chunk.x},${chunk.z} do not match requested column ${x},${z}`);
|
|
@@ -29342,7 +29432,7 @@ var AnvilWorldSource = class {
|
|
|
29342
29432
|
settled: false,
|
|
29343
29433
|
promise: Promise.resolve().then(() => {
|
|
29344
29434
|
controller.signal.throwIfAborted();
|
|
29345
|
-
return this.readRegion(x, z, controller.signal);
|
|
29435
|
+
return abortable2(this.readRegion(x, z, controller.signal), controller.signal);
|
|
29346
29436
|
}).then((data) => {
|
|
29347
29437
|
controller.signal.throwIfAborted();
|
|
29348
29438
|
let bytes = data instanceof Uint8Array ? data : data === void 0 ? void 0 : new Uint8Array(data);
|
|
@@ -30690,6 +30780,7 @@ export {
|
|
|
30690
30780
|
parseSceneDocument,
|
|
30691
30781
|
prefix,
|
|
30692
30782
|
registerNodeEnv,
|
|
30783
|
+
resolveLegacyBlock,
|
|
30693
30784
|
sampleEntityAnimation,
|
|
30694
30785
|
sampleEntityKeyframes,
|
|
30695
30786
|
sampleFluid,
|