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/browser/index.js
CHANGED
|
@@ -18872,6 +18872,7 @@ __export(index_browser_exports, {
|
|
|
18872
18872
|
prefix: () => prefix,
|
|
18873
18873
|
probeImageSize: () => probeImageSize,
|
|
18874
18874
|
registerBrowserEnv: () => registerBrowserEnv,
|
|
18875
|
+
resolveLegacyBlock: () => resolveLegacyBlock,
|
|
18875
18876
|
sampleEntityAnimation: () => sampleEntityAnimation,
|
|
18876
18877
|
sampleEntityKeyframes: () => sampleEntityKeyframes,
|
|
18877
18878
|
sampleFluid: () => sampleFluid,
|
|
@@ -23900,158 +23901,6 @@ function isBlockObject(obj) {
|
|
|
23900
23901
|
init_FluidGeometry();
|
|
23901
23902
|
init_FluidModelObject();
|
|
23902
23903
|
|
|
23903
|
-
// src/model/multiblock/LitematicaParser.ts
|
|
23904
|
-
init_MineRenderError();
|
|
23905
|
-
var LitematicaParser = class {
|
|
23906
|
-
/**
|
|
23907
|
-
* Parses decoded NBT, preserving subregion placement and omitting air.
|
|
23908
|
-
* Later entries in regions replace earlier cells, including with air.
|
|
23909
|
-
* Size encloses all region bounds; positions retain the schematic origin.
|
|
23910
|
-
* Metadata and scheduled ticks are not imported. Named regions can be placed individually.
|
|
23911
|
-
*/
|
|
23912
|
-
static async parse(nbt) {
|
|
23913
|
-
const tags = nbt.value;
|
|
23914
|
-
const version2 = integer2(tags.Version, "Version");
|
|
23915
|
-
if (version2 < 5 || version2 > 7) throw new MineRenderError(`Unsupported Litematica version ${version2}; expected 5, 6, or 7`);
|
|
23916
|
-
const dataVersion = tags.MinecraftDataVersion === void 0 ? void 0 : integer2(tags.MinecraftDataVersion, "MinecraftDataVersion");
|
|
23917
|
-
const blocks = /* @__PURE__ */ new Map();
|
|
23918
|
-
const regions = [];
|
|
23919
|
-
const min = [Infinity, Infinity, Infinity];
|
|
23920
|
-
const max = [-Infinity, -Infinity, -Infinity];
|
|
23921
|
-
for (const [name, tag] of Object.entries(compound(tags.Regions, "Regions"))) {
|
|
23922
|
-
const region = readRegion(compound(tag, `region ${name}`), name, dataVersion, blocks);
|
|
23923
|
-
regions.push(region);
|
|
23924
|
-
for (let axis = 0; axis < 3; axis++) {
|
|
23925
|
-
const start = minimum(region.position[axis], region.signedSize[axis]);
|
|
23926
|
-
min[axis] = Math.min(min[axis], start);
|
|
23927
|
-
max[axis] = Math.max(max[axis], start + region.size[axis]);
|
|
23928
|
-
}
|
|
23929
|
-
}
|
|
23930
|
-
return {
|
|
23931
|
-
size: regions.length ? min.map((value, axis) => max[axis] - value) : [0, 0, 0],
|
|
23932
|
-
blocks: [...blocks.values()],
|
|
23933
|
-
entities: regions.flatMap((region) => region.entities ?? []),
|
|
23934
|
-
dataVersion,
|
|
23935
|
-
regions
|
|
23936
|
-
};
|
|
23937
|
-
}
|
|
23938
|
-
};
|
|
23939
|
-
function readRegion(tags, name, dataVersion, combined) {
|
|
23940
|
-
const position = blockPosition(compound(tags.Position, `region ${name} Position`));
|
|
23941
|
-
const signedSize = blockPosition(compound(tags.Size, `region ${name} Size`));
|
|
23942
|
-
if (signedSize.some((value) => value === 0)) throw new MineRenderError(`Litematica region ${name} dimensions must be nonzero`);
|
|
23943
|
-
const size = signedSize.map(Math.abs);
|
|
23944
|
-
const min = position.map((value, axis) => minimum(value, signedSize[axis]));
|
|
23945
|
-
const [width, height, length] = size;
|
|
23946
|
-
const volume = width * height * length;
|
|
23947
|
-
const palette = compounds(tags.BlockStatePalette, "BlockStatePalette").map(blockState);
|
|
23948
|
-
if (!palette.length) throw new MineRenderError(`Litematica region ${name} has no block palette`);
|
|
23949
|
-
const bits = Math.max(2, Math.ceil(Math.log2(palette.length)));
|
|
23950
|
-
if (!Number.isSafeInteger(volume * bits)) throw new MineRenderError(`Litematica region ${name} volume is too large`);
|
|
23951
|
-
const packed = tags.BlockStates;
|
|
23952
|
-
const expected = Math.ceil(volume * bits / 64);
|
|
23953
|
-
if (packed?.type !== "longArray" || !Array.isArray(packed.value) || packed.value.length !== expected) {
|
|
23954
|
-
throw new MineRenderError(`Litematica region ${name} BlockStates must contain ${expected} longs`);
|
|
23955
|
-
}
|
|
23956
|
-
const words = packed.value.map((pair) => {
|
|
23957
|
-
if (!Array.isArray(pair) || pair.length !== 2 || !pair.every((value) => Number.isInteger(value) && value >= -2147483648 && value <= 4294967295)) {
|
|
23958
|
-
throw new MineRenderError(`Litematica region ${name} has an invalid BlockStates long`);
|
|
23959
|
-
}
|
|
23960
|
-
return [pair[0] >>> 0, pair[1] >>> 0];
|
|
23961
|
-
});
|
|
23962
|
-
const blockEntities = /* @__PURE__ */ new Map();
|
|
23963
|
-
for (const tile of compounds(tags.TileEntities, "TileEntities")) {
|
|
23964
|
-
const pos = blockPosition(tile);
|
|
23965
|
-
if (pos.some((value2, axis) => value2 < 0 || value2 >= size[axis])) {
|
|
23966
|
-
throw new MineRenderError(`Litematica region ${name} block entity is outside its dimensions`);
|
|
23967
|
-
}
|
|
23968
|
-
const index = pos[0] + pos[2] * width + pos[1] * width * length;
|
|
23969
|
-
if (blockEntities.has(index)) throw new MineRenderError(`Duplicate Litematica block entity at index ${index}`);
|
|
23970
|
-
const value = { ...tile };
|
|
23971
|
-
for (const [axis, key] of ["x", "y", "z"].entries()) value[key] = { type: "int", value: pos[axis] + min[axis] };
|
|
23972
|
-
blockEntities.set(index, { type: "compound", value });
|
|
23973
|
-
}
|
|
23974
|
-
const blocks = [];
|
|
23975
|
-
const mask = 2 ** bits - 1;
|
|
23976
|
-
for (let index = 0; index < volume; index++) {
|
|
23977
|
-
const word = Math.floor(index * bits / 64);
|
|
23978
|
-
const shift = index * bits % 64;
|
|
23979
|
-
const [high, low] = words[word];
|
|
23980
|
-
let value = shift < 32 ? low >>> shift : high >>> shift - 32;
|
|
23981
|
-
if (shift < 32 && shift + bits > 32) value |= high << 32 - shift;
|
|
23982
|
-
if (shift + bits > 64) value |= words[word + 1][1] << 64 - shift;
|
|
23983
|
-
const id = (value & mask) >>> 0;
|
|
23984
|
-
const state = palette[id];
|
|
23985
|
-
if (!state) throw new MineRenderError(`Litematica region ${name} palette index ${id} does not exist`);
|
|
23986
|
-
const pos = [
|
|
23987
|
-
index % width + min[0],
|
|
23988
|
-
Math.floor(index / (width * length)) + min[1],
|
|
23989
|
-
Math.floor(index / width) % length + min[2]
|
|
23990
|
-
];
|
|
23991
|
-
const key = pos.join(",");
|
|
23992
|
-
if (["minecraft:air", "minecraft:cave_air", "minecraft:void_air"].includes(state.type)) {
|
|
23993
|
-
combined.delete(key);
|
|
23994
|
-
continue;
|
|
23995
|
-
}
|
|
23996
|
-
const block = { type: state.type, properties: { ...state.properties }, position: pos, nbt: blockEntities.get(index) };
|
|
23997
|
-
blocks.push(block);
|
|
23998
|
-
combined.set(key, block);
|
|
23999
|
-
}
|
|
24000
|
-
const entities = compounds(tags.Entities, "Entities").map((entity) => {
|
|
24001
|
-
const pos = entity.Pos;
|
|
24002
|
-
if (pos?.type !== "list" || pos.value.type !== "double" || pos.value.value.length !== 3 || !pos.value.value.every(Number.isFinite)) {
|
|
24003
|
-
throw new MineRenderError(`Litematica region ${name} entity Pos must contain three doubles`);
|
|
24004
|
-
}
|
|
24005
|
-
const absolute = pos.value.value.map((value2, axis) => value2 + position[axis]);
|
|
24006
|
-
const value = { ...entity, Pos: { type: "list", value: { type: "double", value: [...absolute] } } };
|
|
24007
|
-
return { position: absolute, nbt: { type: "compound", value } };
|
|
24008
|
-
});
|
|
24009
|
-
return { name, position, signedSize, size, blocks, entities, dataVersion };
|
|
24010
|
-
}
|
|
24011
|
-
function minimum(position, size) {
|
|
24012
|
-
return position + Math.min(0, size + 1);
|
|
24013
|
-
}
|
|
24014
|
-
function integer2(tag, name) {
|
|
24015
|
-
if (tag?.type !== "int" || !Number.isInteger(tag.value) || tag.value < -2147483648 || tag.value > 2147483647) {
|
|
24016
|
-
throw new MineRenderError(`Litematica ${name} must be an int`);
|
|
24017
|
-
}
|
|
24018
|
-
return tag.value;
|
|
24019
|
-
}
|
|
24020
|
-
function compound(tag, name) {
|
|
24021
|
-
if (tag?.type !== "compound" || !tag.value || typeof tag.value !== "object" || Array.isArray(tag.value)) {
|
|
24022
|
-
throw new MineRenderError(`Litematica ${name} must be a compound`);
|
|
24023
|
-
}
|
|
24024
|
-
return tag.value;
|
|
24025
|
-
}
|
|
24026
|
-
function blockPosition(tags) {
|
|
24027
|
-
return ["x", "y", "z"].map((axis) => integer2(tags[axis], axis));
|
|
24028
|
-
}
|
|
24029
|
-
function compounds(tag, name) {
|
|
24030
|
-
if (tag === void 0) return [];
|
|
24031
|
-
if (tag.type === "list" && Array.isArray(tag.value.value)) {
|
|
24032
|
-
if (tag.value.value.length === 0) return [];
|
|
24033
|
-
if (tag.value.type === "compound") return tag.value.value;
|
|
24034
|
-
}
|
|
24035
|
-
throw new MineRenderError(`Litematica ${name} must be a compound list`);
|
|
24036
|
-
}
|
|
24037
|
-
function blockState(tags) {
|
|
24038
|
-
const name = tags.Name;
|
|
24039
|
-
if (name?.type !== "string" || !/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(name.value)) {
|
|
24040
|
-
throw new MineRenderError("Litematica palette entry must have a block Name");
|
|
24041
|
-
}
|
|
24042
|
-
const properties = {};
|
|
24043
|
-
if (tags.Properties !== void 0) {
|
|
24044
|
-
for (const [key, tag] of Object.entries(compound(tags.Properties, "block Properties"))) {
|
|
24045
|
-
if (tag?.type !== "string") throw new MineRenderError("Litematica block properties must be strings");
|
|
24046
|
-
Object.defineProperty(properties, key, { value: tag.value, enumerable: true, writable: true, configurable: true });
|
|
24047
|
-
}
|
|
24048
|
-
}
|
|
24049
|
-
return { type: name.value.includes(":") ? name.value : `minecraft:${name.value}`, properties };
|
|
24050
|
-
}
|
|
24051
|
-
|
|
24052
|
-
// src/model/multiblock/SchematicParser.ts
|
|
24053
|
-
init_MineRenderError();
|
|
24054
|
-
|
|
24055
23904
|
// src/model/multiblock/legacyBlocks.json
|
|
24056
23905
|
var legacyBlocks_default = {
|
|
24057
23906
|
__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",
|
|
@@ -24895,12 +24744,20 @@ var legacyBlocks_default = {
|
|
|
24895
24744
|
"131:15": "minecraft:tripwire_hook[powered=true,attached=true,facing=east]",
|
|
24896
24745
|
"132:0": "minecraft:tripwire[disarmed=false,east=false,powered=false,south=false,north=false,west=false,attached=false]",
|
|
24897
24746
|
"132:1": "minecraft:tripwire[disarmed=false,east=false,powered=true,south=false,north=false,west=false,attached=false]",
|
|
24747
|
+
"132:2": "minecraft:tripwire[disarmed=false,east=false,powered=false,south=false,north=false,west=false,attached=false]",
|
|
24748
|
+
"132:3": "minecraft:tripwire[disarmed=false,east=false,powered=true,south=false,north=false,west=false,attached=false]",
|
|
24898
24749
|
"132:4": "minecraft:tripwire[disarmed=false,east=false,powered=false,south=false,north=false,west=false,attached=true]",
|
|
24899
24750
|
"132:5": "minecraft:tripwire[disarmed=false,east=false,powered=true,south=false,north=false,west=false,attached=true]",
|
|
24751
|
+
"132:6": "minecraft:tripwire[disarmed=false,east=false,powered=false,south=false,north=false,west=false,attached=true]",
|
|
24752
|
+
"132:7": "minecraft:tripwire[disarmed=false,east=false,powered=true,south=false,north=false,west=false,attached=true]",
|
|
24900
24753
|
"132:8": "minecraft:tripwire[disarmed=true,east=false,powered=false,south=false,north=false,west=false,attached=false]",
|
|
24901
24754
|
"132:9": "minecraft:tripwire[disarmed=true,east=false,powered=true,south=false,north=false,west=false,attached=false]",
|
|
24755
|
+
"132:10": "minecraft:tripwire[disarmed=true,east=false,powered=false,south=false,north=false,west=false,attached=false]",
|
|
24756
|
+
"132:11": "minecraft:tripwire[disarmed=true,east=false,powered=true,south=false,north=false,west=false,attached=false]",
|
|
24902
24757
|
"132:12": "minecraft:tripwire[disarmed=true,east=false,powered=false,south=false,north=false,west=false,attached=true]",
|
|
24903
24758
|
"132:13": "minecraft:tripwire[disarmed=true,east=false,powered=true,south=false,north=false,west=false,attached=true]",
|
|
24759
|
+
"132:14": "minecraft:tripwire[disarmed=true,east=false,powered=false,south=false,north=false,west=false,attached=true]",
|
|
24760
|
+
"132:15": "minecraft:tripwire[disarmed=true,east=false,powered=true,south=false,north=false,west=false,attached=true]",
|
|
24904
24761
|
"133:0": "minecraft:emerald_block",
|
|
24905
24762
|
"134:0": "minecraft:spruce_stairs[half=bottom,shape=outer_right,facing=east]",
|
|
24906
24763
|
"134:1": "minecraft:spruce_stairs[half=bottom,shape=outer_right,facing=west]",
|
|
@@ -25741,7 +25598,175 @@ var legacyBlocks_default = {
|
|
|
25741
25598
|
}
|
|
25742
25599
|
};
|
|
25743
25600
|
|
|
25601
|
+
// src/model/multiblock/LegacyBlocks.ts
|
|
25602
|
+
function resolveLegacyBlock(id, metadata, customMappings = {}, lenient = false) {
|
|
25603
|
+
const mapping = legacyBlocks_default.blocks;
|
|
25604
|
+
const key = `${id}:${metadata}`;
|
|
25605
|
+
let mapped = customMappings[key] ?? mapping[key];
|
|
25606
|
+
if (!mapped && lenient) mapped = customMappings[`${id}:0`] ?? mapping[`${id}:0`];
|
|
25607
|
+
if (!mapped) return void 0;
|
|
25608
|
+
const [type, state] = mapped.split("[");
|
|
25609
|
+
const properties = {};
|
|
25610
|
+
if (state) {
|
|
25611
|
+
for (const property of state.slice(0, -1).split(",")) {
|
|
25612
|
+
const [key2, value] = property.split("=");
|
|
25613
|
+
properties[key2] = value;
|
|
25614
|
+
}
|
|
25615
|
+
}
|
|
25616
|
+
return { type, properties };
|
|
25617
|
+
}
|
|
25618
|
+
|
|
25619
|
+
// src/model/multiblock/LitematicaParser.ts
|
|
25620
|
+
init_MineRenderError();
|
|
25621
|
+
var LitematicaParser = class {
|
|
25622
|
+
/**
|
|
25623
|
+
* Parses decoded NBT, preserving subregion placement and omitting air.
|
|
25624
|
+
* Later entries in regions replace earlier cells, including with air.
|
|
25625
|
+
* Size encloses all region bounds; positions retain the schematic origin.
|
|
25626
|
+
* Metadata and scheduled ticks are not imported. Named regions can be placed individually.
|
|
25627
|
+
*/
|
|
25628
|
+
static async parse(nbt) {
|
|
25629
|
+
const tags = nbt.value;
|
|
25630
|
+
const version2 = integer2(tags.Version, "Version");
|
|
25631
|
+
if (version2 < 5 || version2 > 7) throw new MineRenderError(`Unsupported Litematica version ${version2}; expected 5, 6, or 7`);
|
|
25632
|
+
const dataVersion = tags.MinecraftDataVersion === void 0 ? void 0 : integer2(tags.MinecraftDataVersion, "MinecraftDataVersion");
|
|
25633
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
25634
|
+
const regions = [];
|
|
25635
|
+
const min = [Infinity, Infinity, Infinity];
|
|
25636
|
+
const max = [-Infinity, -Infinity, -Infinity];
|
|
25637
|
+
for (const [name, tag] of Object.entries(compound(tags.Regions, "Regions"))) {
|
|
25638
|
+
const region = readRegion(compound(tag, `region ${name}`), name, dataVersion, blocks);
|
|
25639
|
+
regions.push(region);
|
|
25640
|
+
for (let axis = 0; axis < 3; axis++) {
|
|
25641
|
+
const start = minimum(region.position[axis], region.signedSize[axis]);
|
|
25642
|
+
min[axis] = Math.min(min[axis], start);
|
|
25643
|
+
max[axis] = Math.max(max[axis], start + region.size[axis]);
|
|
25644
|
+
}
|
|
25645
|
+
}
|
|
25646
|
+
return {
|
|
25647
|
+
size: regions.length ? min.map((value, axis) => max[axis] - value) : [0, 0, 0],
|
|
25648
|
+
blocks: [...blocks.values()],
|
|
25649
|
+
entities: regions.flatMap((region) => region.entities ?? []),
|
|
25650
|
+
dataVersion,
|
|
25651
|
+
regions
|
|
25652
|
+
};
|
|
25653
|
+
}
|
|
25654
|
+
};
|
|
25655
|
+
function readRegion(tags, name, dataVersion, combined) {
|
|
25656
|
+
const position = blockPosition(compound(tags.Position, `region ${name} Position`));
|
|
25657
|
+
const signedSize = blockPosition(compound(tags.Size, `region ${name} Size`));
|
|
25658
|
+
if (signedSize.some((value) => value === 0)) throw new MineRenderError(`Litematica region ${name} dimensions must be nonzero`);
|
|
25659
|
+
const size = signedSize.map(Math.abs);
|
|
25660
|
+
const min = position.map((value, axis) => minimum(value, signedSize[axis]));
|
|
25661
|
+
const [width, height, length] = size;
|
|
25662
|
+
const volume = width * height * length;
|
|
25663
|
+
const palette = compounds(tags.BlockStatePalette, "BlockStatePalette").map(blockState);
|
|
25664
|
+
if (!palette.length) throw new MineRenderError(`Litematica region ${name} has no block palette`);
|
|
25665
|
+
const bits = Math.max(2, Math.ceil(Math.log2(palette.length)));
|
|
25666
|
+
if (!Number.isSafeInteger(volume * bits)) throw new MineRenderError(`Litematica region ${name} volume is too large`);
|
|
25667
|
+
const packed = tags.BlockStates;
|
|
25668
|
+
const expected = Math.ceil(volume * bits / 64);
|
|
25669
|
+
if (packed?.type !== "longArray" || !Array.isArray(packed.value) || packed.value.length !== expected) {
|
|
25670
|
+
throw new MineRenderError(`Litematica region ${name} BlockStates must contain ${expected} longs`);
|
|
25671
|
+
}
|
|
25672
|
+
const words = packed.value.map((pair) => {
|
|
25673
|
+
if (!Array.isArray(pair) || pair.length !== 2 || !pair.every((value) => Number.isInteger(value) && value >= -2147483648 && value <= 4294967295)) {
|
|
25674
|
+
throw new MineRenderError(`Litematica region ${name} has an invalid BlockStates long`);
|
|
25675
|
+
}
|
|
25676
|
+
return [pair[0] >>> 0, pair[1] >>> 0];
|
|
25677
|
+
});
|
|
25678
|
+
const blockEntities = /* @__PURE__ */ new Map();
|
|
25679
|
+
for (const tile of compounds(tags.TileEntities, "TileEntities")) {
|
|
25680
|
+
const pos = blockPosition(tile);
|
|
25681
|
+
if (pos.some((value2, axis) => value2 < 0 || value2 >= size[axis])) {
|
|
25682
|
+
throw new MineRenderError(`Litematica region ${name} block entity is outside its dimensions`);
|
|
25683
|
+
}
|
|
25684
|
+
const index = pos[0] + pos[2] * width + pos[1] * width * length;
|
|
25685
|
+
if (blockEntities.has(index)) throw new MineRenderError(`Duplicate Litematica block entity at index ${index}`);
|
|
25686
|
+
const value = { ...tile };
|
|
25687
|
+
for (const [axis, key] of ["x", "y", "z"].entries()) value[key] = { type: "int", value: pos[axis] + min[axis] };
|
|
25688
|
+
blockEntities.set(index, { type: "compound", value });
|
|
25689
|
+
}
|
|
25690
|
+
const blocks = [];
|
|
25691
|
+
const mask = 2 ** bits - 1;
|
|
25692
|
+
for (let index = 0; index < volume; index++) {
|
|
25693
|
+
const word = Math.floor(index * bits / 64);
|
|
25694
|
+
const shift = index * bits % 64;
|
|
25695
|
+
const [high, low] = words[word];
|
|
25696
|
+
let value = shift < 32 ? low >>> shift : high >>> shift - 32;
|
|
25697
|
+
if (shift < 32 && shift + bits > 32) value |= high << 32 - shift;
|
|
25698
|
+
if (shift + bits > 64) value |= words[word + 1][1] << 64 - shift;
|
|
25699
|
+
const id = (value & mask) >>> 0;
|
|
25700
|
+
const state = palette[id];
|
|
25701
|
+
if (!state) throw new MineRenderError(`Litematica region ${name} palette index ${id} does not exist`);
|
|
25702
|
+
const pos = [
|
|
25703
|
+
index % width + min[0],
|
|
25704
|
+
Math.floor(index / (width * length)) + min[1],
|
|
25705
|
+
Math.floor(index / width) % length + min[2]
|
|
25706
|
+
];
|
|
25707
|
+
const key = pos.join(",");
|
|
25708
|
+
if (["minecraft:air", "minecraft:cave_air", "minecraft:void_air"].includes(state.type)) {
|
|
25709
|
+
combined.delete(key);
|
|
25710
|
+
continue;
|
|
25711
|
+
}
|
|
25712
|
+
const block = { type: state.type, properties: { ...state.properties }, position: pos, nbt: blockEntities.get(index) };
|
|
25713
|
+
blocks.push(block);
|
|
25714
|
+
combined.set(key, block);
|
|
25715
|
+
}
|
|
25716
|
+
const entities = compounds(tags.Entities, "Entities").map((entity) => {
|
|
25717
|
+
const pos = entity.Pos;
|
|
25718
|
+
if (pos?.type !== "list" || pos.value.type !== "double" || pos.value.value.length !== 3 || !pos.value.value.every(Number.isFinite)) {
|
|
25719
|
+
throw new MineRenderError(`Litematica region ${name} entity Pos must contain three doubles`);
|
|
25720
|
+
}
|
|
25721
|
+
const absolute = pos.value.value.map((value2, axis) => value2 + position[axis]);
|
|
25722
|
+
const value = { ...entity, Pos: { type: "list", value: { type: "double", value: [...absolute] } } };
|
|
25723
|
+
return { position: absolute, nbt: { type: "compound", value } };
|
|
25724
|
+
});
|
|
25725
|
+
return { name, position, signedSize, size, blocks, entities, dataVersion };
|
|
25726
|
+
}
|
|
25727
|
+
function minimum(position, size) {
|
|
25728
|
+
return position + Math.min(0, size + 1);
|
|
25729
|
+
}
|
|
25730
|
+
function integer2(tag, name) {
|
|
25731
|
+
if (tag?.type !== "int" || !Number.isInteger(tag.value) || tag.value < -2147483648 || tag.value > 2147483647) {
|
|
25732
|
+
throw new MineRenderError(`Litematica ${name} must be an int`);
|
|
25733
|
+
}
|
|
25734
|
+
return tag.value;
|
|
25735
|
+
}
|
|
25736
|
+
function compound(tag, name) {
|
|
25737
|
+
if (tag?.type !== "compound" || !tag.value || typeof tag.value !== "object" || Array.isArray(tag.value)) {
|
|
25738
|
+
throw new MineRenderError(`Litematica ${name} must be a compound`);
|
|
25739
|
+
}
|
|
25740
|
+
return tag.value;
|
|
25741
|
+
}
|
|
25742
|
+
function blockPosition(tags) {
|
|
25743
|
+
return ["x", "y", "z"].map((axis) => integer2(tags[axis], axis));
|
|
25744
|
+
}
|
|
25745
|
+
function compounds(tag, name) {
|
|
25746
|
+
if (tag === void 0) return [];
|
|
25747
|
+
if (tag.type === "list" && Array.isArray(tag.value.value)) {
|
|
25748
|
+
if (tag.value.value.length === 0) return [];
|
|
25749
|
+
if (tag.value.type === "compound") return tag.value.value;
|
|
25750
|
+
}
|
|
25751
|
+
throw new MineRenderError(`Litematica ${name} must be a compound list`);
|
|
25752
|
+
}
|
|
25753
|
+
function blockState(tags) {
|
|
25754
|
+
const name = tags.Name;
|
|
25755
|
+
if (name?.type !== "string" || !/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(name.value)) {
|
|
25756
|
+
throw new MineRenderError("Litematica palette entry must have a block Name");
|
|
25757
|
+
}
|
|
25758
|
+
const properties = {};
|
|
25759
|
+
if (tags.Properties !== void 0) {
|
|
25760
|
+
for (const [key, tag] of Object.entries(compound(tags.Properties, "block Properties"))) {
|
|
25761
|
+
if (tag?.type !== "string") throw new MineRenderError("Litematica block properties must be strings");
|
|
25762
|
+
Object.defineProperty(properties, key, { value: tag.value, enumerable: true, writable: true, configurable: true });
|
|
25763
|
+
}
|
|
25764
|
+
}
|
|
25765
|
+
return { type: name.value.includes(":") ? name.value : `minecraft:${name.value}`, properties };
|
|
25766
|
+
}
|
|
25767
|
+
|
|
25744
25768
|
// src/model/multiblock/SchematicParser.ts
|
|
25769
|
+
init_MineRenderError();
|
|
25745
25770
|
var SchematicParser = class {
|
|
25746
25771
|
/**
|
|
25747
25772
|
* Parses a legacy schematic using custom mappings before the bundled defaults.
|
|
@@ -25783,35 +25808,21 @@ var SchematicParser = class {
|
|
|
25783
25808
|
tileEntities.set((y * length + z) * width + x, { type: "compound", value: tile });
|
|
25784
25809
|
}
|
|
25785
25810
|
const blocks = [];
|
|
25786
|
-
const mapping = legacyBlocks_default.blocks;
|
|
25787
25811
|
for (let index = 0; index < volume; index++) {
|
|
25788
25812
|
const packed = addBlocks?.value[index >> 1] ?? 0;
|
|
25789
25813
|
const high = packed >> (index & 1) * 4 & 15;
|
|
25790
25814
|
const id = high << 8 | ids.value[index] & 255;
|
|
25791
25815
|
const metadata = data.value[index] & 255;
|
|
25792
|
-
const
|
|
25793
|
-
|
|
25794
|
-
if (!mapped && options.lenient) {
|
|
25795
|
-
mapped = customMappings[`${id}:0`] ?? mapping[`${id}:0`];
|
|
25796
|
-
}
|
|
25797
|
-
if (!mapped) {
|
|
25816
|
+
const block = resolveLegacyBlock(id, metadata, customMappings, options.lenient);
|
|
25817
|
+
if (!block) {
|
|
25798
25818
|
if (options.lenient) {
|
|
25799
25819
|
continue;
|
|
25800
25820
|
}
|
|
25801
25821
|
throw new MineRenderError(`Unsupported legacy block ${id}:${metadata} at schematic index ${index}`);
|
|
25802
25822
|
}
|
|
25803
|
-
|
|
25804
|
-
if (type === "minecraft:air") continue;
|
|
25805
|
-
const properties = {};
|
|
25806
|
-
if (state) {
|
|
25807
|
-
for (const property of state.slice(0, -1).split(",")) {
|
|
25808
|
-
const [key2, value] = property.split("=");
|
|
25809
|
-
properties[key2] = value;
|
|
25810
|
-
}
|
|
25811
|
-
}
|
|
25823
|
+
if (block.type === "minecraft:air") continue;
|
|
25812
25824
|
blocks.push({
|
|
25813
|
-
|
|
25814
|
-
properties,
|
|
25825
|
+
...block,
|
|
25815
25826
|
position: [index % width, Math.floor(index / (width * length)), Math.floor(index / width) % length],
|
|
25816
25827
|
nbt: tileEntities.get(index)
|
|
25817
25828
|
});
|
|
@@ -29191,10 +29202,13 @@ var AnvilParser = class {
|
|
|
29191
29202
|
return this.locations(this.bytes(data)).map(({ x, z }) => ({ x, z }));
|
|
29192
29203
|
}
|
|
29193
29204
|
/** Decodes all stored chunks. Gzip, zlib, LZ4, and uncompressed payloads are supported. */
|
|
29194
|
-
static async parse(data) {
|
|
29205
|
+
static async parse(data, options = {}) {
|
|
29206
|
+
options.signal?.throwIfAborted();
|
|
29195
29207
|
const bytes = this.bytes(data);
|
|
29196
29208
|
const chunks = [];
|
|
29197
|
-
for (const location of this.locations(bytes))
|
|
29209
|
+
for (const location of this.locations(bytes)) {
|
|
29210
|
+
chunks.push(await this.abortable(this.readChunk(bytes, location, options), options.signal));
|
|
29211
|
+
}
|
|
29198
29212
|
return { chunks };
|
|
29199
29213
|
}
|
|
29200
29214
|
/**
|
|
@@ -29203,13 +29217,28 @@ var AnvilParser = class {
|
|
|
29203
29217
|
* @param localZ - Chunk z within the region, from 0 to 31.
|
|
29204
29218
|
* @returns The chunk with absolute coordinates, or `undefined` if the region has no chunk there.
|
|
29205
29219
|
*/
|
|
29206
|
-
static async parseChunk(data, localX, localZ) {
|
|
29220
|
+
static async parseChunk(data, localX, localZ, options = {}) {
|
|
29221
|
+
options.signal?.throwIfAborted();
|
|
29207
29222
|
if (![localX, localZ].every((value) => Number.isInteger(value) && value >= 0 && value < 32)) {
|
|
29208
29223
|
throw new RangeError("Region-local chunk coordinates must be integers from 0 to 31");
|
|
29209
29224
|
}
|
|
29210
29225
|
const bytes = this.bytes(data);
|
|
29211
29226
|
const location = this.locations(bytes).find(({ x, z }) => x === localX && z === localZ);
|
|
29212
|
-
return location ? this.readChunk(bytes, location) : void 0;
|
|
29227
|
+
return location ? this.abortable(this.readChunk(bytes, location, options), options.signal) : void 0;
|
|
29228
|
+
}
|
|
29229
|
+
static async abortable(promise, signal) {
|
|
29230
|
+
if (!signal) return promise;
|
|
29231
|
+
let abort;
|
|
29232
|
+
const cancelled = new Promise((_, reject) => {
|
|
29233
|
+
abort = () => reject(signal.reason);
|
|
29234
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
29235
|
+
if (signal.aborted) abort();
|
|
29236
|
+
});
|
|
29237
|
+
try {
|
|
29238
|
+
return await Promise.race([promise, cancelled]);
|
|
29239
|
+
} finally {
|
|
29240
|
+
signal.removeEventListener("abort", abort);
|
|
29241
|
+
}
|
|
29213
29242
|
}
|
|
29214
29243
|
static bytes(data) {
|
|
29215
29244
|
return data instanceof Uint8Array ? data : new Uint8Array(data);
|
|
@@ -29235,20 +29264,41 @@ var AnvilParser = class {
|
|
|
29235
29264
|
}
|
|
29236
29265
|
return locations;
|
|
29237
29266
|
}
|
|
29238
|
-
static async readChunk(data, location) {
|
|
29239
|
-
|
|
29240
|
-
|
|
29267
|
+
static async readChunk(data, location, options) {
|
|
29268
|
+
options.signal?.throwIfAborted();
|
|
29269
|
+
const format2 = data[location.offset + 4];
|
|
29270
|
+
const external = (format2 & 128) !== 0;
|
|
29271
|
+
const compression = format2 & 127;
|
|
29241
29272
|
if (compression !== 1 && compression !== 2 && compression !== 3 && compression !== 4) {
|
|
29242
29273
|
throw new MineRenderError(`Unsupported Anvil compression ${compression}`);
|
|
29243
29274
|
}
|
|
29244
29275
|
let payload = data.subarray(location.offset + 5, location.offset + 4 + location.length);
|
|
29276
|
+
let expected;
|
|
29277
|
+
if (external) {
|
|
29278
|
+
if (!options.region || !options.readExternalChunk) {
|
|
29279
|
+
throw new MineRenderError("External Anvil .mcc chunks require region coordinates and readExternalChunk");
|
|
29280
|
+
}
|
|
29281
|
+
if (![options.region.x, options.region.z].every(Number.isSafeInteger)) {
|
|
29282
|
+
throw new RangeError("Anvil region coordinates must be safe integers");
|
|
29283
|
+
}
|
|
29284
|
+
expected = { x: options.region.x * 32 + location.x, z: options.region.z * 32 + location.z };
|
|
29285
|
+
if (![expected.x, expected.z].every(Number.isSafeInteger)) {
|
|
29286
|
+
throw new RangeError("External Anvil chunk coordinates must be safe integers");
|
|
29287
|
+
}
|
|
29288
|
+
const externalData = await options.readExternalChunk(expected.x, expected.z, options.signal);
|
|
29289
|
+
options.signal?.throwIfAborted();
|
|
29290
|
+
if (externalData === void 0) throw new MineRenderError(`External Anvil chunk c.${expected.x}.${expected.z}.mcc is missing`);
|
|
29291
|
+
payload = this.bytes(externalData);
|
|
29292
|
+
}
|
|
29245
29293
|
if (compression === 4) {
|
|
29246
29294
|
payload = decodeAnvilLz4(payload);
|
|
29247
29295
|
} else if (compression !== 3) {
|
|
29248
29296
|
const stream = new Blob([payload]).stream().pipeThrough(new DecompressionStream(compression === 1 ? "gzip" : "deflate"));
|
|
29249
29297
|
payload = new Uint8Array(await new Response(stream).arrayBuffer());
|
|
29250
29298
|
}
|
|
29299
|
+
options.signal?.throwIfAborted();
|
|
29251
29300
|
const nbt = await NBTHelper.fromBuffer(payload, "big");
|
|
29301
|
+
options.signal?.throwIfAborted();
|
|
29252
29302
|
const root = nbt.value;
|
|
29253
29303
|
const level = root.Level?.type === "compound" ? root.Level.value : root;
|
|
29254
29304
|
const dataVersion = root.DataVersion?.type === "int" ? root.DataVersion.value : void 0;
|
|
@@ -29261,28 +29311,37 @@ var AnvilParser = class {
|
|
|
29261
29311
|
if ((chunk.x % 32 + 32) % 32 !== location.x || (chunk.z % 32 + 32) % 32 !== location.z) {
|
|
29262
29312
|
throw new MineRenderError("Anvil chunk coordinates do not match its region location");
|
|
29263
29313
|
}
|
|
29314
|
+
if (expected && (chunk.x !== expected.x || chunk.z !== expected.z)) {
|
|
29315
|
+
throw new MineRenderError(`Anvil chunk coordinates do not match external file c.${expected.x}.${expected.z}.mcc`);
|
|
29316
|
+
}
|
|
29317
|
+
const numericStates = /* @__PURE__ */ new Map();
|
|
29264
29318
|
for (const section of this.compounds(level.sections ?? level.Sections, "sections")) {
|
|
29265
29319
|
const y = this.integer(section.Y, "section Y");
|
|
29320
|
+
let data2;
|
|
29266
29321
|
if (section.Blocks || section.Data || section.Add) {
|
|
29267
|
-
|
|
29268
|
-
|
|
29269
|
-
|
|
29270
|
-
|
|
29271
|
-
}
|
|
29272
|
-
|
|
29273
|
-
|
|
29274
|
-
|
|
29275
|
-
|
|
29276
|
-
|
|
29277
|
-
const
|
|
29278
|
-
|
|
29279
|
-
|
|
29280
|
-
const
|
|
29281
|
-
|
|
29282
|
-
|
|
29322
|
+
if (section.Palette || section.BlockStates || section.block_states) {
|
|
29323
|
+
throw new MineRenderError(`Anvil section ${y} mixes numeric and paletted block states`);
|
|
29324
|
+
}
|
|
29325
|
+
data2 = this.numericSection(section, y, options, numericStates);
|
|
29326
|
+
} else {
|
|
29327
|
+
if (section.block_states && section.block_states.type !== "compound") {
|
|
29328
|
+
throw new MineRenderError("Anvil block_states must be a compound");
|
|
29329
|
+
}
|
|
29330
|
+
const modern = section.block_states?.type === "compound" ? section.block_states.value : void 0;
|
|
29331
|
+
const paletteTag = modern ? modern.palette : section.Palette;
|
|
29332
|
+
const palette = paletteTag?.type === "list" && paletteTag.value.type === "string" ? paletteTag.value.value : this.compounds(paletteTag, "block palette");
|
|
29333
|
+
data2 = new ChunkData();
|
|
29334
|
+
if (palette.length) {
|
|
29335
|
+
const states = palette.map((entry) => this.block(entry));
|
|
29336
|
+
const indices = this.unpack(modern ? modern.data : section.BlockStates, states.length, !!modern, dataVersion);
|
|
29337
|
+
for (let index = 0; index < 4096; index++) {
|
|
29338
|
+
const state = states[indices[index]];
|
|
29339
|
+
if (!state) throw new MineRenderError(`Anvil palette index ${indices[index]} is out of range`);
|
|
29340
|
+
if (!ChunkData.isAir(state)) data2.set(index, state);
|
|
29341
|
+
}
|
|
29342
|
+
} else if (modern || section.Palette || section.BlockStates) {
|
|
29343
|
+
throw new MineRenderError("Anvil section has block states without a palette");
|
|
29283
29344
|
}
|
|
29284
|
-
} else if (modern || section.Palette || section.BlockStates) {
|
|
29285
|
-
throw new MineRenderError("Anvil section has block states without a palette");
|
|
29286
29345
|
}
|
|
29287
29346
|
if (chunk.sections.some((section2) => section2.y === y)) throw new MineRenderError(`Duplicate Anvil section Y ${y}`);
|
|
29288
29347
|
chunk.sections.push({ y, data: data2 });
|
|
@@ -29310,6 +29369,33 @@ var AnvilParser = class {
|
|
|
29310
29369
|
}
|
|
29311
29370
|
return chunk;
|
|
29312
29371
|
}
|
|
29372
|
+
static numericSection(section, y, options, states) {
|
|
29373
|
+
const bytes = (name, length, optional = false) => {
|
|
29374
|
+
const tag = section[name];
|
|
29375
|
+
if (!tag && optional) return void 0;
|
|
29376
|
+
if (tag?.type !== "byteArray" || !Array.isArray(tag.value) || tag.value.length !== length) {
|
|
29377
|
+
throw new MineRenderError(`Anvil section ${y} ${name} must contain ${length} bytes`);
|
|
29378
|
+
}
|
|
29379
|
+
return tag.value;
|
|
29380
|
+
};
|
|
29381
|
+
const ids = bytes("Blocks", 4096);
|
|
29382
|
+
const metadata = bytes("Data", 2048, true);
|
|
29383
|
+
const extra = bytes("Add", 2048, true);
|
|
29384
|
+
const data = new ChunkData();
|
|
29385
|
+
for (let index = 0; index < 4096; index++) {
|
|
29386
|
+
const shift = (index & 1) * 4;
|
|
29387
|
+
const id = ids[index] & 255 | ((extra?.[index >> 1] ?? 0) >> shift & 15) << 8;
|
|
29388
|
+
const value = (metadata?.[index >> 1] ?? 0) >> shift & 15;
|
|
29389
|
+
const key = id * 16 + value;
|
|
29390
|
+
if (!states.has(key)) states.set(key, resolveLegacyBlock(id, value, options.legacyMappings, options.lenient));
|
|
29391
|
+
const block = states.get(key);
|
|
29392
|
+
if (!block && !options.lenient) {
|
|
29393
|
+
throw new MineRenderError(`Unsupported legacy block ${id}:${value} at Anvil section ${y} index ${index}`);
|
|
29394
|
+
}
|
|
29395
|
+
if (block && !ChunkData.isAir(block)) data.set(index, block);
|
|
29396
|
+
}
|
|
29397
|
+
return data;
|
|
29398
|
+
}
|
|
29313
29399
|
static compounds(tag, name) {
|
|
29314
29400
|
if (!tag) return [];
|
|
29315
29401
|
if (tag.type !== "list" || tag.value.type !== "compound" && tag.value.value.length !== 0) {
|
|
@@ -29392,6 +29478,7 @@ var AnvilWorldSource = class {
|
|
|
29392
29478
|
this.cachedBytes = 0;
|
|
29393
29479
|
this.maxCachedRegions = options.maxCachedRegions ?? 4;
|
|
29394
29480
|
this.maxCachedBytes = options.maxCachedBytes ?? 64 * 1024 * 1024;
|
|
29481
|
+
this.parseOptions = { readExternalChunk: options.readExternalChunk, legacyMappings: options.legacyMappings, lenient: options.lenient };
|
|
29395
29482
|
for (const [name, value] of Object.entries({ maxCachedRegions: this.maxCachedRegions, maxCachedBytes: this.maxCachedBytes })) {
|
|
29396
29483
|
if (!Number.isSafeInteger(value) || value < 0) throw new RangeError(`${name} must be a nonnegative safe integer`);
|
|
29397
29484
|
}
|
|
@@ -29405,7 +29492,11 @@ var AnvilWorldSource = class {
|
|
|
29405
29492
|
const data = await this.getRegion(regionX, regionZ, signal);
|
|
29406
29493
|
signal?.throwIfAborted();
|
|
29407
29494
|
if (!data) return void 0;
|
|
29408
|
-
const chunk = await
|
|
29495
|
+
const chunk = await AnvilParser.parseChunk(data, x - regionX * 32, z - regionZ * 32, {
|
|
29496
|
+
...this.parseOptions,
|
|
29497
|
+
region: { x: regionX, z: regionZ },
|
|
29498
|
+
signal
|
|
29499
|
+
});
|
|
29409
29500
|
signal?.throwIfAborted();
|
|
29410
29501
|
if (chunk && (chunk.x !== x || chunk.z !== z)) {
|
|
29411
29502
|
throw new MineRenderError(`Anvil chunk coordinates ${chunk.x},${chunk.z} do not match requested column ${x},${z}`);
|
|
@@ -29436,7 +29527,7 @@ var AnvilWorldSource = class {
|
|
|
29436
29527
|
settled: false,
|
|
29437
29528
|
promise: Promise.resolve().then(() => {
|
|
29438
29529
|
controller.signal.throwIfAborted();
|
|
29439
|
-
return this.readRegion(x, z, controller.signal);
|
|
29530
|
+
return abortable2(this.readRegion(x, z, controller.signal), controller.signal);
|
|
29440
29531
|
}).then((data) => {
|
|
29441
29532
|
controller.signal.throwIfAborted();
|
|
29442
29533
|
let bytes = data instanceof Uint8Array ? data : data === void 0 ? void 0 : new Uint8Array(data);
|