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.js
CHANGED
|
@@ -18963,6 +18963,7 @@ __export(index_node_exports, {
|
|
|
18963
18963
|
parseSceneDocument: () => parseSceneDocument,
|
|
18964
18964
|
prefix: () => prefix,
|
|
18965
18965
|
registerNodeEnv: () => registerNodeEnv,
|
|
18966
|
+
resolveLegacyBlock: () => resolveLegacyBlock,
|
|
18966
18967
|
sampleEntityAnimation: () => sampleEntityAnimation,
|
|
18967
18968
|
sampleEntityKeyframes: () => sampleEntityKeyframes,
|
|
18968
18969
|
sampleFluid: () => sampleFluid,
|
|
@@ -23945,158 +23946,6 @@ function isBlockObject(obj) {
|
|
|
23945
23946
|
init_FluidGeometry();
|
|
23946
23947
|
init_FluidModelObject();
|
|
23947
23948
|
|
|
23948
|
-
// src/model/multiblock/LitematicaParser.ts
|
|
23949
|
-
init_MineRenderError();
|
|
23950
|
-
var LitematicaParser = class {
|
|
23951
|
-
/**
|
|
23952
|
-
* Parses decoded NBT, preserving subregion placement and omitting air.
|
|
23953
|
-
* Later entries in regions replace earlier cells, including with air.
|
|
23954
|
-
* Size encloses all region bounds; positions retain the schematic origin.
|
|
23955
|
-
* Metadata and scheduled ticks are not imported. Named regions can be placed individually.
|
|
23956
|
-
*/
|
|
23957
|
-
static async parse(nbt) {
|
|
23958
|
-
const tags = nbt.value;
|
|
23959
|
-
const version2 = integer2(tags.Version, "Version");
|
|
23960
|
-
if (version2 < 5 || version2 > 7) throw new MineRenderError(`Unsupported Litematica version ${version2}; expected 5, 6, or 7`);
|
|
23961
|
-
const dataVersion = tags.MinecraftDataVersion === void 0 ? void 0 : integer2(tags.MinecraftDataVersion, "MinecraftDataVersion");
|
|
23962
|
-
const blocks = /* @__PURE__ */ new Map();
|
|
23963
|
-
const regions = [];
|
|
23964
|
-
const min = [Infinity, Infinity, Infinity];
|
|
23965
|
-
const max = [-Infinity, -Infinity, -Infinity];
|
|
23966
|
-
for (const [name, tag] of Object.entries(compound(tags.Regions, "Regions"))) {
|
|
23967
|
-
const region = readRegion(compound(tag, `region ${name}`), name, dataVersion, blocks);
|
|
23968
|
-
regions.push(region);
|
|
23969
|
-
for (let axis = 0; axis < 3; axis++) {
|
|
23970
|
-
const start = minimum(region.position[axis], region.signedSize[axis]);
|
|
23971
|
-
min[axis] = Math.min(min[axis], start);
|
|
23972
|
-
max[axis] = Math.max(max[axis], start + region.size[axis]);
|
|
23973
|
-
}
|
|
23974
|
-
}
|
|
23975
|
-
return {
|
|
23976
|
-
size: regions.length ? min.map((value, axis) => max[axis] - value) : [0, 0, 0],
|
|
23977
|
-
blocks: [...blocks.values()],
|
|
23978
|
-
entities: regions.flatMap((region) => region.entities ?? []),
|
|
23979
|
-
dataVersion,
|
|
23980
|
-
regions
|
|
23981
|
-
};
|
|
23982
|
-
}
|
|
23983
|
-
};
|
|
23984
|
-
function readRegion(tags, name, dataVersion, combined) {
|
|
23985
|
-
const position = blockPosition(compound(tags.Position, `region ${name} Position`));
|
|
23986
|
-
const signedSize = blockPosition(compound(tags.Size, `region ${name} Size`));
|
|
23987
|
-
if (signedSize.some((value) => value === 0)) throw new MineRenderError(`Litematica region ${name} dimensions must be nonzero`);
|
|
23988
|
-
const size = signedSize.map(Math.abs);
|
|
23989
|
-
const min = position.map((value, axis) => minimum(value, signedSize[axis]));
|
|
23990
|
-
const [width, height, length] = size;
|
|
23991
|
-
const volume = width * height * length;
|
|
23992
|
-
const palette = compounds(tags.BlockStatePalette, "BlockStatePalette").map(blockState);
|
|
23993
|
-
if (!palette.length) throw new MineRenderError(`Litematica region ${name} has no block palette`);
|
|
23994
|
-
const bits = Math.max(2, Math.ceil(Math.log2(palette.length)));
|
|
23995
|
-
if (!Number.isSafeInteger(volume * bits)) throw new MineRenderError(`Litematica region ${name} volume is too large`);
|
|
23996
|
-
const packed = tags.BlockStates;
|
|
23997
|
-
const expected = Math.ceil(volume * bits / 64);
|
|
23998
|
-
if (packed?.type !== "longArray" || !Array.isArray(packed.value) || packed.value.length !== expected) {
|
|
23999
|
-
throw new MineRenderError(`Litematica region ${name} BlockStates must contain ${expected} longs`);
|
|
24000
|
-
}
|
|
24001
|
-
const words = packed.value.map((pair) => {
|
|
24002
|
-
if (!Array.isArray(pair) || pair.length !== 2 || !pair.every((value) => Number.isInteger(value) && value >= -2147483648 && value <= 4294967295)) {
|
|
24003
|
-
throw new MineRenderError(`Litematica region ${name} has an invalid BlockStates long`);
|
|
24004
|
-
}
|
|
24005
|
-
return [pair[0] >>> 0, pair[1] >>> 0];
|
|
24006
|
-
});
|
|
24007
|
-
const blockEntities = /* @__PURE__ */ new Map();
|
|
24008
|
-
for (const tile of compounds(tags.TileEntities, "TileEntities")) {
|
|
24009
|
-
const pos = blockPosition(tile);
|
|
24010
|
-
if (pos.some((value2, axis) => value2 < 0 || value2 >= size[axis])) {
|
|
24011
|
-
throw new MineRenderError(`Litematica region ${name} block entity is outside its dimensions`);
|
|
24012
|
-
}
|
|
24013
|
-
const index = pos[0] + pos[2] * width + pos[1] * width * length;
|
|
24014
|
-
if (blockEntities.has(index)) throw new MineRenderError(`Duplicate Litematica block entity at index ${index}`);
|
|
24015
|
-
const value = { ...tile };
|
|
24016
|
-
for (const [axis, key] of ["x", "y", "z"].entries()) value[key] = { type: "int", value: pos[axis] + min[axis] };
|
|
24017
|
-
blockEntities.set(index, { type: "compound", value });
|
|
24018
|
-
}
|
|
24019
|
-
const blocks = [];
|
|
24020
|
-
const mask = 2 ** bits - 1;
|
|
24021
|
-
for (let index = 0; index < volume; index++) {
|
|
24022
|
-
const word = Math.floor(index * bits / 64);
|
|
24023
|
-
const shift = index * bits % 64;
|
|
24024
|
-
const [high, low] = words[word];
|
|
24025
|
-
let value = shift < 32 ? low >>> shift : high >>> shift - 32;
|
|
24026
|
-
if (shift < 32 && shift + bits > 32) value |= high << 32 - shift;
|
|
24027
|
-
if (shift + bits > 64) value |= words[word + 1][1] << 64 - shift;
|
|
24028
|
-
const id = (value & mask) >>> 0;
|
|
24029
|
-
const state = palette[id];
|
|
24030
|
-
if (!state) throw new MineRenderError(`Litematica region ${name} palette index ${id} does not exist`);
|
|
24031
|
-
const pos = [
|
|
24032
|
-
index % width + min[0],
|
|
24033
|
-
Math.floor(index / (width * length)) + min[1],
|
|
24034
|
-
Math.floor(index / width) % length + min[2]
|
|
24035
|
-
];
|
|
24036
|
-
const key = pos.join(",");
|
|
24037
|
-
if (["minecraft:air", "minecraft:cave_air", "minecraft:void_air"].includes(state.type)) {
|
|
24038
|
-
combined.delete(key);
|
|
24039
|
-
continue;
|
|
24040
|
-
}
|
|
24041
|
-
const block = { type: state.type, properties: { ...state.properties }, position: pos, nbt: blockEntities.get(index) };
|
|
24042
|
-
blocks.push(block);
|
|
24043
|
-
combined.set(key, block);
|
|
24044
|
-
}
|
|
24045
|
-
const entities = compounds(tags.Entities, "Entities").map((entity) => {
|
|
24046
|
-
const pos = entity.Pos;
|
|
24047
|
-
if (pos?.type !== "list" || pos.value.type !== "double" || pos.value.value.length !== 3 || !pos.value.value.every(Number.isFinite)) {
|
|
24048
|
-
throw new MineRenderError(`Litematica region ${name} entity Pos must contain three doubles`);
|
|
24049
|
-
}
|
|
24050
|
-
const absolute = pos.value.value.map((value2, axis) => value2 + position[axis]);
|
|
24051
|
-
const value = { ...entity, Pos: { type: "list", value: { type: "double", value: [...absolute] } } };
|
|
24052
|
-
return { position: absolute, nbt: { type: "compound", value } };
|
|
24053
|
-
});
|
|
24054
|
-
return { name, position, signedSize, size, blocks, entities, dataVersion };
|
|
24055
|
-
}
|
|
24056
|
-
function minimum(position, size) {
|
|
24057
|
-
return position + Math.min(0, size + 1);
|
|
24058
|
-
}
|
|
24059
|
-
function integer2(tag, name) {
|
|
24060
|
-
if (tag?.type !== "int" || !Number.isInteger(tag.value) || tag.value < -2147483648 || tag.value > 2147483647) {
|
|
24061
|
-
throw new MineRenderError(`Litematica ${name} must be an int`);
|
|
24062
|
-
}
|
|
24063
|
-
return tag.value;
|
|
24064
|
-
}
|
|
24065
|
-
function compound(tag, name) {
|
|
24066
|
-
if (tag?.type !== "compound" || !tag.value || typeof tag.value !== "object" || Array.isArray(tag.value)) {
|
|
24067
|
-
throw new MineRenderError(`Litematica ${name} must be a compound`);
|
|
24068
|
-
}
|
|
24069
|
-
return tag.value;
|
|
24070
|
-
}
|
|
24071
|
-
function blockPosition(tags) {
|
|
24072
|
-
return ["x", "y", "z"].map((axis) => integer2(tags[axis], axis));
|
|
24073
|
-
}
|
|
24074
|
-
function compounds(tag, name) {
|
|
24075
|
-
if (tag === void 0) return [];
|
|
24076
|
-
if (tag.type === "list" && Array.isArray(tag.value.value)) {
|
|
24077
|
-
if (tag.value.value.length === 0) return [];
|
|
24078
|
-
if (tag.value.type === "compound") return tag.value.value;
|
|
24079
|
-
}
|
|
24080
|
-
throw new MineRenderError(`Litematica ${name} must be a compound list`);
|
|
24081
|
-
}
|
|
24082
|
-
function blockState(tags) {
|
|
24083
|
-
const name = tags.Name;
|
|
24084
|
-
if (name?.type !== "string" || !/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(name.value)) {
|
|
24085
|
-
throw new MineRenderError("Litematica palette entry must have a block Name");
|
|
24086
|
-
}
|
|
24087
|
-
const properties = {};
|
|
24088
|
-
if (tags.Properties !== void 0) {
|
|
24089
|
-
for (const [key, tag] of Object.entries(compound(tags.Properties, "block Properties"))) {
|
|
24090
|
-
if (tag?.type !== "string") throw new MineRenderError("Litematica block properties must be strings");
|
|
24091
|
-
Object.defineProperty(properties, key, { value: tag.value, enumerable: true, writable: true, configurable: true });
|
|
24092
|
-
}
|
|
24093
|
-
}
|
|
24094
|
-
return { type: name.value.includes(":") ? name.value : `minecraft:${name.value}`, properties };
|
|
24095
|
-
}
|
|
24096
|
-
|
|
24097
|
-
// src/model/multiblock/SchematicParser.ts
|
|
24098
|
-
init_MineRenderError();
|
|
24099
|
-
|
|
24100
23949
|
// src/model/multiblock/legacyBlocks.json
|
|
24101
23950
|
var legacyBlocks_default = {
|
|
24102
23951
|
__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",
|
|
@@ -24940,12 +24789,20 @@ var legacyBlocks_default = {
|
|
|
24940
24789
|
"131:15": "minecraft:tripwire_hook[powered=true,attached=true,facing=east]",
|
|
24941
24790
|
"132:0": "minecraft:tripwire[disarmed=false,east=false,powered=false,south=false,north=false,west=false,attached=false]",
|
|
24942
24791
|
"132:1": "minecraft:tripwire[disarmed=false,east=false,powered=true,south=false,north=false,west=false,attached=false]",
|
|
24792
|
+
"132:2": "minecraft:tripwire[disarmed=false,east=false,powered=false,south=false,north=false,west=false,attached=false]",
|
|
24793
|
+
"132:3": "minecraft:tripwire[disarmed=false,east=false,powered=true,south=false,north=false,west=false,attached=false]",
|
|
24943
24794
|
"132:4": "minecraft:tripwire[disarmed=false,east=false,powered=false,south=false,north=false,west=false,attached=true]",
|
|
24944
24795
|
"132:5": "minecraft:tripwire[disarmed=false,east=false,powered=true,south=false,north=false,west=false,attached=true]",
|
|
24796
|
+
"132:6": "minecraft:tripwire[disarmed=false,east=false,powered=false,south=false,north=false,west=false,attached=true]",
|
|
24797
|
+
"132:7": "minecraft:tripwire[disarmed=false,east=false,powered=true,south=false,north=false,west=false,attached=true]",
|
|
24945
24798
|
"132:8": "minecraft:tripwire[disarmed=true,east=false,powered=false,south=false,north=false,west=false,attached=false]",
|
|
24946
24799
|
"132:9": "minecraft:tripwire[disarmed=true,east=false,powered=true,south=false,north=false,west=false,attached=false]",
|
|
24800
|
+
"132:10": "minecraft:tripwire[disarmed=true,east=false,powered=false,south=false,north=false,west=false,attached=false]",
|
|
24801
|
+
"132:11": "minecraft:tripwire[disarmed=true,east=false,powered=true,south=false,north=false,west=false,attached=false]",
|
|
24947
24802
|
"132:12": "minecraft:tripwire[disarmed=true,east=false,powered=false,south=false,north=false,west=false,attached=true]",
|
|
24948
24803
|
"132:13": "minecraft:tripwire[disarmed=true,east=false,powered=true,south=false,north=false,west=false,attached=true]",
|
|
24804
|
+
"132:14": "minecraft:tripwire[disarmed=true,east=false,powered=false,south=false,north=false,west=false,attached=true]",
|
|
24805
|
+
"132:15": "minecraft:tripwire[disarmed=true,east=false,powered=true,south=false,north=false,west=false,attached=true]",
|
|
24949
24806
|
"133:0": "minecraft:emerald_block",
|
|
24950
24807
|
"134:0": "minecraft:spruce_stairs[half=bottom,shape=outer_right,facing=east]",
|
|
24951
24808
|
"134:1": "minecraft:spruce_stairs[half=bottom,shape=outer_right,facing=west]",
|
|
@@ -25786,7 +25643,175 @@ var legacyBlocks_default = {
|
|
|
25786
25643
|
}
|
|
25787
25644
|
};
|
|
25788
25645
|
|
|
25646
|
+
// src/model/multiblock/LegacyBlocks.ts
|
|
25647
|
+
function resolveLegacyBlock(id, metadata, customMappings = {}, lenient = false) {
|
|
25648
|
+
const mapping = legacyBlocks_default.blocks;
|
|
25649
|
+
const key = `${id}:${metadata}`;
|
|
25650
|
+
let mapped = customMappings[key] ?? mapping[key];
|
|
25651
|
+
if (!mapped && lenient) mapped = customMappings[`${id}:0`] ?? mapping[`${id}:0`];
|
|
25652
|
+
if (!mapped) return void 0;
|
|
25653
|
+
const [type, state] = mapped.split("[");
|
|
25654
|
+
const properties = {};
|
|
25655
|
+
if (state) {
|
|
25656
|
+
for (const property of state.slice(0, -1).split(",")) {
|
|
25657
|
+
const [key2, value] = property.split("=");
|
|
25658
|
+
properties[key2] = value;
|
|
25659
|
+
}
|
|
25660
|
+
}
|
|
25661
|
+
return { type, properties };
|
|
25662
|
+
}
|
|
25663
|
+
|
|
25664
|
+
// src/model/multiblock/LitematicaParser.ts
|
|
25665
|
+
init_MineRenderError();
|
|
25666
|
+
var LitematicaParser = class {
|
|
25667
|
+
/**
|
|
25668
|
+
* Parses decoded NBT, preserving subregion placement and omitting air.
|
|
25669
|
+
* Later entries in regions replace earlier cells, including with air.
|
|
25670
|
+
* Size encloses all region bounds; positions retain the schematic origin.
|
|
25671
|
+
* Metadata and scheduled ticks are not imported. Named regions can be placed individually.
|
|
25672
|
+
*/
|
|
25673
|
+
static async parse(nbt) {
|
|
25674
|
+
const tags = nbt.value;
|
|
25675
|
+
const version2 = integer2(tags.Version, "Version");
|
|
25676
|
+
if (version2 < 5 || version2 > 7) throw new MineRenderError(`Unsupported Litematica version ${version2}; expected 5, 6, or 7`);
|
|
25677
|
+
const dataVersion = tags.MinecraftDataVersion === void 0 ? void 0 : integer2(tags.MinecraftDataVersion, "MinecraftDataVersion");
|
|
25678
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
25679
|
+
const regions = [];
|
|
25680
|
+
const min = [Infinity, Infinity, Infinity];
|
|
25681
|
+
const max = [-Infinity, -Infinity, -Infinity];
|
|
25682
|
+
for (const [name, tag] of Object.entries(compound(tags.Regions, "Regions"))) {
|
|
25683
|
+
const region = readRegion(compound(tag, `region ${name}`), name, dataVersion, blocks);
|
|
25684
|
+
regions.push(region);
|
|
25685
|
+
for (let axis = 0; axis < 3; axis++) {
|
|
25686
|
+
const start = minimum(region.position[axis], region.signedSize[axis]);
|
|
25687
|
+
min[axis] = Math.min(min[axis], start);
|
|
25688
|
+
max[axis] = Math.max(max[axis], start + region.size[axis]);
|
|
25689
|
+
}
|
|
25690
|
+
}
|
|
25691
|
+
return {
|
|
25692
|
+
size: regions.length ? min.map((value, axis) => max[axis] - value) : [0, 0, 0],
|
|
25693
|
+
blocks: [...blocks.values()],
|
|
25694
|
+
entities: regions.flatMap((region) => region.entities ?? []),
|
|
25695
|
+
dataVersion,
|
|
25696
|
+
regions
|
|
25697
|
+
};
|
|
25698
|
+
}
|
|
25699
|
+
};
|
|
25700
|
+
function readRegion(tags, name, dataVersion, combined) {
|
|
25701
|
+
const position = blockPosition(compound(tags.Position, `region ${name} Position`));
|
|
25702
|
+
const signedSize = blockPosition(compound(tags.Size, `region ${name} Size`));
|
|
25703
|
+
if (signedSize.some((value) => value === 0)) throw new MineRenderError(`Litematica region ${name} dimensions must be nonzero`);
|
|
25704
|
+
const size = signedSize.map(Math.abs);
|
|
25705
|
+
const min = position.map((value, axis) => minimum(value, signedSize[axis]));
|
|
25706
|
+
const [width, height, length] = size;
|
|
25707
|
+
const volume = width * height * length;
|
|
25708
|
+
const palette = compounds(tags.BlockStatePalette, "BlockStatePalette").map(blockState);
|
|
25709
|
+
if (!palette.length) throw new MineRenderError(`Litematica region ${name} has no block palette`);
|
|
25710
|
+
const bits = Math.max(2, Math.ceil(Math.log2(palette.length)));
|
|
25711
|
+
if (!Number.isSafeInteger(volume * bits)) throw new MineRenderError(`Litematica region ${name} volume is too large`);
|
|
25712
|
+
const packed = tags.BlockStates;
|
|
25713
|
+
const expected = Math.ceil(volume * bits / 64);
|
|
25714
|
+
if (packed?.type !== "longArray" || !Array.isArray(packed.value) || packed.value.length !== expected) {
|
|
25715
|
+
throw new MineRenderError(`Litematica region ${name} BlockStates must contain ${expected} longs`);
|
|
25716
|
+
}
|
|
25717
|
+
const words = packed.value.map((pair) => {
|
|
25718
|
+
if (!Array.isArray(pair) || pair.length !== 2 || !pair.every((value) => Number.isInteger(value) && value >= -2147483648 && value <= 4294967295)) {
|
|
25719
|
+
throw new MineRenderError(`Litematica region ${name} has an invalid BlockStates long`);
|
|
25720
|
+
}
|
|
25721
|
+
return [pair[0] >>> 0, pair[1] >>> 0];
|
|
25722
|
+
});
|
|
25723
|
+
const blockEntities = /* @__PURE__ */ new Map();
|
|
25724
|
+
for (const tile of compounds(tags.TileEntities, "TileEntities")) {
|
|
25725
|
+
const pos = blockPosition(tile);
|
|
25726
|
+
if (pos.some((value2, axis) => value2 < 0 || value2 >= size[axis])) {
|
|
25727
|
+
throw new MineRenderError(`Litematica region ${name} block entity is outside its dimensions`);
|
|
25728
|
+
}
|
|
25729
|
+
const index = pos[0] + pos[2] * width + pos[1] * width * length;
|
|
25730
|
+
if (blockEntities.has(index)) throw new MineRenderError(`Duplicate Litematica block entity at index ${index}`);
|
|
25731
|
+
const value = { ...tile };
|
|
25732
|
+
for (const [axis, key] of ["x", "y", "z"].entries()) value[key] = { type: "int", value: pos[axis] + min[axis] };
|
|
25733
|
+
blockEntities.set(index, { type: "compound", value });
|
|
25734
|
+
}
|
|
25735
|
+
const blocks = [];
|
|
25736
|
+
const mask = 2 ** bits - 1;
|
|
25737
|
+
for (let index = 0; index < volume; index++) {
|
|
25738
|
+
const word = Math.floor(index * bits / 64);
|
|
25739
|
+
const shift = index * bits % 64;
|
|
25740
|
+
const [high, low] = words[word];
|
|
25741
|
+
let value = shift < 32 ? low >>> shift : high >>> shift - 32;
|
|
25742
|
+
if (shift < 32 && shift + bits > 32) value |= high << 32 - shift;
|
|
25743
|
+
if (shift + bits > 64) value |= words[word + 1][1] << 64 - shift;
|
|
25744
|
+
const id = (value & mask) >>> 0;
|
|
25745
|
+
const state = palette[id];
|
|
25746
|
+
if (!state) throw new MineRenderError(`Litematica region ${name} palette index ${id} does not exist`);
|
|
25747
|
+
const pos = [
|
|
25748
|
+
index % width + min[0],
|
|
25749
|
+
Math.floor(index / (width * length)) + min[1],
|
|
25750
|
+
Math.floor(index / width) % length + min[2]
|
|
25751
|
+
];
|
|
25752
|
+
const key = pos.join(",");
|
|
25753
|
+
if (["minecraft:air", "minecraft:cave_air", "minecraft:void_air"].includes(state.type)) {
|
|
25754
|
+
combined.delete(key);
|
|
25755
|
+
continue;
|
|
25756
|
+
}
|
|
25757
|
+
const block = { type: state.type, properties: { ...state.properties }, position: pos, nbt: blockEntities.get(index) };
|
|
25758
|
+
blocks.push(block);
|
|
25759
|
+
combined.set(key, block);
|
|
25760
|
+
}
|
|
25761
|
+
const entities = compounds(tags.Entities, "Entities").map((entity) => {
|
|
25762
|
+
const pos = entity.Pos;
|
|
25763
|
+
if (pos?.type !== "list" || pos.value.type !== "double" || pos.value.value.length !== 3 || !pos.value.value.every(Number.isFinite)) {
|
|
25764
|
+
throw new MineRenderError(`Litematica region ${name} entity Pos must contain three doubles`);
|
|
25765
|
+
}
|
|
25766
|
+
const absolute = pos.value.value.map((value2, axis) => value2 + position[axis]);
|
|
25767
|
+
const value = { ...entity, Pos: { type: "list", value: { type: "double", value: [...absolute] } } };
|
|
25768
|
+
return { position: absolute, nbt: { type: "compound", value } };
|
|
25769
|
+
});
|
|
25770
|
+
return { name, position, signedSize, size, blocks, entities, dataVersion };
|
|
25771
|
+
}
|
|
25772
|
+
function minimum(position, size) {
|
|
25773
|
+
return position + Math.min(0, size + 1);
|
|
25774
|
+
}
|
|
25775
|
+
function integer2(tag, name) {
|
|
25776
|
+
if (tag?.type !== "int" || !Number.isInteger(tag.value) || tag.value < -2147483648 || tag.value > 2147483647) {
|
|
25777
|
+
throw new MineRenderError(`Litematica ${name} must be an int`);
|
|
25778
|
+
}
|
|
25779
|
+
return tag.value;
|
|
25780
|
+
}
|
|
25781
|
+
function compound(tag, name) {
|
|
25782
|
+
if (tag?.type !== "compound" || !tag.value || typeof tag.value !== "object" || Array.isArray(tag.value)) {
|
|
25783
|
+
throw new MineRenderError(`Litematica ${name} must be a compound`);
|
|
25784
|
+
}
|
|
25785
|
+
return tag.value;
|
|
25786
|
+
}
|
|
25787
|
+
function blockPosition(tags) {
|
|
25788
|
+
return ["x", "y", "z"].map((axis) => integer2(tags[axis], axis));
|
|
25789
|
+
}
|
|
25790
|
+
function compounds(tag, name) {
|
|
25791
|
+
if (tag === void 0) return [];
|
|
25792
|
+
if (tag.type === "list" && Array.isArray(tag.value.value)) {
|
|
25793
|
+
if (tag.value.value.length === 0) return [];
|
|
25794
|
+
if (tag.value.type === "compound") return tag.value.value;
|
|
25795
|
+
}
|
|
25796
|
+
throw new MineRenderError(`Litematica ${name} must be a compound list`);
|
|
25797
|
+
}
|
|
25798
|
+
function blockState(tags) {
|
|
25799
|
+
const name = tags.Name;
|
|
25800
|
+
if (name?.type !== "string" || !/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(name.value)) {
|
|
25801
|
+
throw new MineRenderError("Litematica palette entry must have a block Name");
|
|
25802
|
+
}
|
|
25803
|
+
const properties = {};
|
|
25804
|
+
if (tags.Properties !== void 0) {
|
|
25805
|
+
for (const [key, tag] of Object.entries(compound(tags.Properties, "block Properties"))) {
|
|
25806
|
+
if (tag?.type !== "string") throw new MineRenderError("Litematica block properties must be strings");
|
|
25807
|
+
Object.defineProperty(properties, key, { value: tag.value, enumerable: true, writable: true, configurable: true });
|
|
25808
|
+
}
|
|
25809
|
+
}
|
|
25810
|
+
return { type: name.value.includes(":") ? name.value : `minecraft:${name.value}`, properties };
|
|
25811
|
+
}
|
|
25812
|
+
|
|
25789
25813
|
// src/model/multiblock/SchematicParser.ts
|
|
25814
|
+
init_MineRenderError();
|
|
25790
25815
|
var SchematicParser = class {
|
|
25791
25816
|
/**
|
|
25792
25817
|
* Parses a legacy schematic using custom mappings before the bundled defaults.
|
|
@@ -25828,35 +25853,21 @@ var SchematicParser = class {
|
|
|
25828
25853
|
tileEntities.set((y * length + z) * width + x, { type: "compound", value: tile });
|
|
25829
25854
|
}
|
|
25830
25855
|
const blocks = [];
|
|
25831
|
-
const mapping = legacyBlocks_default.blocks;
|
|
25832
25856
|
for (let index = 0; index < volume; index++) {
|
|
25833
25857
|
const packed = addBlocks?.value[index >> 1] ?? 0;
|
|
25834
25858
|
const high = packed >> (index & 1) * 4 & 15;
|
|
25835
25859
|
const id = high << 8 | ids.value[index] & 255;
|
|
25836
25860
|
const metadata = data.value[index] & 255;
|
|
25837
|
-
const
|
|
25838
|
-
|
|
25839
|
-
if (!mapped && options.lenient) {
|
|
25840
|
-
mapped = customMappings[`${id}:0`] ?? mapping[`${id}:0`];
|
|
25841
|
-
}
|
|
25842
|
-
if (!mapped) {
|
|
25861
|
+
const block = resolveLegacyBlock(id, metadata, customMappings, options.lenient);
|
|
25862
|
+
if (!block) {
|
|
25843
25863
|
if (options.lenient) {
|
|
25844
25864
|
continue;
|
|
25845
25865
|
}
|
|
25846
25866
|
throw new MineRenderError(`Unsupported legacy block ${id}:${metadata} at schematic index ${index}`);
|
|
25847
25867
|
}
|
|
25848
|
-
|
|
25849
|
-
if (type === "minecraft:air") continue;
|
|
25850
|
-
const properties = {};
|
|
25851
|
-
if (state) {
|
|
25852
|
-
for (const property of state.slice(0, -1).split(",")) {
|
|
25853
|
-
const [key2, value] = property.split("=");
|
|
25854
|
-
properties[key2] = value;
|
|
25855
|
-
}
|
|
25856
|
-
}
|
|
25868
|
+
if (block.type === "minecraft:air") continue;
|
|
25857
25869
|
blocks.push({
|
|
25858
|
-
|
|
25859
|
-
properties,
|
|
25870
|
+
...block,
|
|
25860
25871
|
position: [index % width, Math.floor(index / (width * length)), Math.floor(index / width) % length],
|
|
25861
25872
|
nbt: tileEntities.get(index)
|
|
25862
25873
|
});
|
|
@@ -29238,10 +29249,13 @@ var AnvilParser = class {
|
|
|
29238
29249
|
return this.locations(this.bytes(data)).map(({ x, z }) => ({ x, z }));
|
|
29239
29250
|
}
|
|
29240
29251
|
/** Decodes all stored chunks. Gzip, zlib, LZ4, and uncompressed payloads are supported. */
|
|
29241
|
-
static async parse(data) {
|
|
29252
|
+
static async parse(data, options = {}) {
|
|
29253
|
+
options.signal?.throwIfAborted();
|
|
29242
29254
|
const bytes = this.bytes(data);
|
|
29243
29255
|
const chunks = [];
|
|
29244
|
-
for (const location of this.locations(bytes))
|
|
29256
|
+
for (const location of this.locations(bytes)) {
|
|
29257
|
+
chunks.push(await this.abortable(this.readChunk(bytes, location, options), options.signal));
|
|
29258
|
+
}
|
|
29245
29259
|
return { chunks };
|
|
29246
29260
|
}
|
|
29247
29261
|
/**
|
|
@@ -29250,13 +29264,28 @@ var AnvilParser = class {
|
|
|
29250
29264
|
* @param localZ - Chunk z within the region, from 0 to 31.
|
|
29251
29265
|
* @returns The chunk with absolute coordinates, or `undefined` if the region has no chunk there.
|
|
29252
29266
|
*/
|
|
29253
|
-
static async parseChunk(data, localX, localZ) {
|
|
29267
|
+
static async parseChunk(data, localX, localZ, options = {}) {
|
|
29268
|
+
options.signal?.throwIfAborted();
|
|
29254
29269
|
if (![localX, localZ].every((value) => Number.isInteger(value) && value >= 0 && value < 32)) {
|
|
29255
29270
|
throw new RangeError("Region-local chunk coordinates must be integers from 0 to 31");
|
|
29256
29271
|
}
|
|
29257
29272
|
const bytes = this.bytes(data);
|
|
29258
29273
|
const location = this.locations(bytes).find(({ x, z }) => x === localX && z === localZ);
|
|
29259
|
-
return location ? this.readChunk(bytes, location) : void 0;
|
|
29274
|
+
return location ? this.abortable(this.readChunk(bytes, location, options), options.signal) : void 0;
|
|
29275
|
+
}
|
|
29276
|
+
static async abortable(promise, signal) {
|
|
29277
|
+
if (!signal) return promise;
|
|
29278
|
+
let abort;
|
|
29279
|
+
const cancelled = new Promise((_, reject) => {
|
|
29280
|
+
abort = () => reject(signal.reason);
|
|
29281
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
29282
|
+
if (signal.aborted) abort();
|
|
29283
|
+
});
|
|
29284
|
+
try {
|
|
29285
|
+
return await Promise.race([promise, cancelled]);
|
|
29286
|
+
} finally {
|
|
29287
|
+
signal.removeEventListener("abort", abort);
|
|
29288
|
+
}
|
|
29260
29289
|
}
|
|
29261
29290
|
static bytes(data) {
|
|
29262
29291
|
return data instanceof Uint8Array ? data : new Uint8Array(data);
|
|
@@ -29282,20 +29311,41 @@ var AnvilParser = class {
|
|
|
29282
29311
|
}
|
|
29283
29312
|
return locations;
|
|
29284
29313
|
}
|
|
29285
|
-
static async readChunk(data, location) {
|
|
29286
|
-
|
|
29287
|
-
|
|
29314
|
+
static async readChunk(data, location, options) {
|
|
29315
|
+
options.signal?.throwIfAborted();
|
|
29316
|
+
const format2 = data[location.offset + 4];
|
|
29317
|
+
const external = (format2 & 128) !== 0;
|
|
29318
|
+
const compression = format2 & 127;
|
|
29288
29319
|
if (compression !== 1 && compression !== 2 && compression !== 3 && compression !== 4) {
|
|
29289
29320
|
throw new MineRenderError(`Unsupported Anvil compression ${compression}`);
|
|
29290
29321
|
}
|
|
29291
29322
|
let payload = data.subarray(location.offset + 5, location.offset + 4 + location.length);
|
|
29323
|
+
let expected;
|
|
29324
|
+
if (external) {
|
|
29325
|
+
if (!options.region || !options.readExternalChunk) {
|
|
29326
|
+
throw new MineRenderError("External Anvil .mcc chunks require region coordinates and readExternalChunk");
|
|
29327
|
+
}
|
|
29328
|
+
if (![options.region.x, options.region.z].every(Number.isSafeInteger)) {
|
|
29329
|
+
throw new RangeError("Anvil region coordinates must be safe integers");
|
|
29330
|
+
}
|
|
29331
|
+
expected = { x: options.region.x * 32 + location.x, z: options.region.z * 32 + location.z };
|
|
29332
|
+
if (![expected.x, expected.z].every(Number.isSafeInteger)) {
|
|
29333
|
+
throw new RangeError("External Anvil chunk coordinates must be safe integers");
|
|
29334
|
+
}
|
|
29335
|
+
const externalData = await options.readExternalChunk(expected.x, expected.z, options.signal);
|
|
29336
|
+
options.signal?.throwIfAborted();
|
|
29337
|
+
if (externalData === void 0) throw new MineRenderError(`External Anvil chunk c.${expected.x}.${expected.z}.mcc is missing`);
|
|
29338
|
+
payload = this.bytes(externalData);
|
|
29339
|
+
}
|
|
29292
29340
|
if (compression === 4) {
|
|
29293
29341
|
payload = decodeAnvilLz4(payload);
|
|
29294
29342
|
} else if (compression !== 3) {
|
|
29295
29343
|
const stream = new Blob([payload]).stream().pipeThrough(new DecompressionStream(compression === 1 ? "gzip" : "deflate"));
|
|
29296
29344
|
payload = new Uint8Array(await new Response(stream).arrayBuffer());
|
|
29297
29345
|
}
|
|
29346
|
+
options.signal?.throwIfAborted();
|
|
29298
29347
|
const nbt = await NBTHelper.fromBuffer(payload, "big");
|
|
29348
|
+
options.signal?.throwIfAborted();
|
|
29299
29349
|
const root = nbt.value;
|
|
29300
29350
|
const level = root.Level?.type === "compound" ? root.Level.value : root;
|
|
29301
29351
|
const dataVersion = root.DataVersion?.type === "int" ? root.DataVersion.value : void 0;
|
|
@@ -29308,28 +29358,37 @@ var AnvilParser = class {
|
|
|
29308
29358
|
if ((chunk.x % 32 + 32) % 32 !== location.x || (chunk.z % 32 + 32) % 32 !== location.z) {
|
|
29309
29359
|
throw new MineRenderError("Anvil chunk coordinates do not match its region location");
|
|
29310
29360
|
}
|
|
29361
|
+
if (expected && (chunk.x !== expected.x || chunk.z !== expected.z)) {
|
|
29362
|
+
throw new MineRenderError(`Anvil chunk coordinates do not match external file c.${expected.x}.${expected.z}.mcc`);
|
|
29363
|
+
}
|
|
29364
|
+
const numericStates = /* @__PURE__ */ new Map();
|
|
29311
29365
|
for (const section of this.compounds(level.sections ?? level.Sections, "sections")) {
|
|
29312
29366
|
const y = this.integer(section.Y, "section Y");
|
|
29367
|
+
let data2;
|
|
29313
29368
|
if (section.Blocks || section.Data || section.Add) {
|
|
29314
|
-
|
|
29315
|
-
|
|
29316
|
-
|
|
29317
|
-
|
|
29318
|
-
}
|
|
29319
|
-
|
|
29320
|
-
|
|
29321
|
-
|
|
29322
|
-
|
|
29323
|
-
|
|
29324
|
-
const
|
|
29325
|
-
|
|
29326
|
-
|
|
29327
|
-
const
|
|
29328
|
-
|
|
29329
|
-
|
|
29369
|
+
if (section.Palette || section.BlockStates || section.block_states) {
|
|
29370
|
+
throw new MineRenderError(`Anvil section ${y} mixes numeric and paletted block states`);
|
|
29371
|
+
}
|
|
29372
|
+
data2 = this.numericSection(section, y, options, numericStates);
|
|
29373
|
+
} else {
|
|
29374
|
+
if (section.block_states && section.block_states.type !== "compound") {
|
|
29375
|
+
throw new MineRenderError("Anvil block_states must be a compound");
|
|
29376
|
+
}
|
|
29377
|
+
const modern = section.block_states?.type === "compound" ? section.block_states.value : void 0;
|
|
29378
|
+
const paletteTag = modern ? modern.palette : section.Palette;
|
|
29379
|
+
const palette = paletteTag?.type === "list" && paletteTag.value.type === "string" ? paletteTag.value.value : this.compounds(paletteTag, "block palette");
|
|
29380
|
+
data2 = new ChunkData();
|
|
29381
|
+
if (palette.length) {
|
|
29382
|
+
const states = palette.map((entry) => this.block(entry));
|
|
29383
|
+
const indices = this.unpack(modern ? modern.data : section.BlockStates, states.length, !!modern, dataVersion);
|
|
29384
|
+
for (let index = 0; index < 4096; index++) {
|
|
29385
|
+
const state = states[indices[index]];
|
|
29386
|
+
if (!state) throw new MineRenderError(`Anvil palette index ${indices[index]} is out of range`);
|
|
29387
|
+
if (!ChunkData.isAir(state)) data2.set(index, state);
|
|
29388
|
+
}
|
|
29389
|
+
} else if (modern || section.Palette || section.BlockStates) {
|
|
29390
|
+
throw new MineRenderError("Anvil section has block states without a palette");
|
|
29330
29391
|
}
|
|
29331
|
-
} else if (modern || section.Palette || section.BlockStates) {
|
|
29332
|
-
throw new MineRenderError("Anvil section has block states without a palette");
|
|
29333
29392
|
}
|
|
29334
29393
|
if (chunk.sections.some((section2) => section2.y === y)) throw new MineRenderError(`Duplicate Anvil section Y ${y}`);
|
|
29335
29394
|
chunk.sections.push({ y, data: data2 });
|
|
@@ -29357,6 +29416,33 @@ var AnvilParser = class {
|
|
|
29357
29416
|
}
|
|
29358
29417
|
return chunk;
|
|
29359
29418
|
}
|
|
29419
|
+
static numericSection(section, y, options, states) {
|
|
29420
|
+
const bytes = (name, length, optional = false) => {
|
|
29421
|
+
const tag = section[name];
|
|
29422
|
+
if (!tag && optional) return void 0;
|
|
29423
|
+
if (tag?.type !== "byteArray" || !Array.isArray(tag.value) || tag.value.length !== length) {
|
|
29424
|
+
throw new MineRenderError(`Anvil section ${y} ${name} must contain ${length} bytes`);
|
|
29425
|
+
}
|
|
29426
|
+
return tag.value;
|
|
29427
|
+
};
|
|
29428
|
+
const ids = bytes("Blocks", 4096);
|
|
29429
|
+
const metadata = bytes("Data", 2048, true);
|
|
29430
|
+
const extra = bytes("Add", 2048, true);
|
|
29431
|
+
const data = new ChunkData();
|
|
29432
|
+
for (let index = 0; index < 4096; index++) {
|
|
29433
|
+
const shift = (index & 1) * 4;
|
|
29434
|
+
const id = ids[index] & 255 | ((extra?.[index >> 1] ?? 0) >> shift & 15) << 8;
|
|
29435
|
+
const value = (metadata?.[index >> 1] ?? 0) >> shift & 15;
|
|
29436
|
+
const key = id * 16 + value;
|
|
29437
|
+
if (!states.has(key)) states.set(key, resolveLegacyBlock(id, value, options.legacyMappings, options.lenient));
|
|
29438
|
+
const block = states.get(key);
|
|
29439
|
+
if (!block && !options.lenient) {
|
|
29440
|
+
throw new MineRenderError(`Unsupported legacy block ${id}:${value} at Anvil section ${y} index ${index}`);
|
|
29441
|
+
}
|
|
29442
|
+
if (block && !ChunkData.isAir(block)) data.set(index, block);
|
|
29443
|
+
}
|
|
29444
|
+
return data;
|
|
29445
|
+
}
|
|
29360
29446
|
static compounds(tag, name) {
|
|
29361
29447
|
if (!tag) return [];
|
|
29362
29448
|
if (tag.type !== "list" || tag.value.type !== "compound" && tag.value.value.length !== 0) {
|
|
@@ -29439,6 +29525,7 @@ var AnvilWorldSource = class {
|
|
|
29439
29525
|
this.cachedBytes = 0;
|
|
29440
29526
|
this.maxCachedRegions = options.maxCachedRegions ?? 4;
|
|
29441
29527
|
this.maxCachedBytes = options.maxCachedBytes ?? 64 * 1024 * 1024;
|
|
29528
|
+
this.parseOptions = { readExternalChunk: options.readExternalChunk, legacyMappings: options.legacyMappings, lenient: options.lenient };
|
|
29442
29529
|
for (const [name, value] of Object.entries({ maxCachedRegions: this.maxCachedRegions, maxCachedBytes: this.maxCachedBytes })) {
|
|
29443
29530
|
if (!Number.isSafeInteger(value) || value < 0) throw new RangeError(`${name} must be a nonnegative safe integer`);
|
|
29444
29531
|
}
|
|
@@ -29452,7 +29539,11 @@ var AnvilWorldSource = class {
|
|
|
29452
29539
|
const data = await this.getRegion(regionX, regionZ, signal);
|
|
29453
29540
|
signal?.throwIfAborted();
|
|
29454
29541
|
if (!data) return void 0;
|
|
29455
|
-
const chunk = await
|
|
29542
|
+
const chunk = await AnvilParser.parseChunk(data, x - regionX * 32, z - regionZ * 32, {
|
|
29543
|
+
...this.parseOptions,
|
|
29544
|
+
region: { x: regionX, z: regionZ },
|
|
29545
|
+
signal
|
|
29546
|
+
});
|
|
29456
29547
|
signal?.throwIfAborted();
|
|
29457
29548
|
if (chunk && (chunk.x !== x || chunk.z !== z)) {
|
|
29458
29549
|
throw new MineRenderError(`Anvil chunk coordinates ${chunk.x},${chunk.z} do not match requested column ${x},${z}`);
|
|
@@ -29483,7 +29574,7 @@ var AnvilWorldSource = class {
|
|
|
29483
29574
|
settled: false,
|
|
29484
29575
|
promise: Promise.resolve().then(() => {
|
|
29485
29576
|
controller.signal.throwIfAborted();
|
|
29486
|
-
return this.readRegion(x, z, controller.signal);
|
|
29577
|
+
return abortable2(this.readRegion(x, z, controller.signal), controller.signal);
|
|
29487
29578
|
}).then((data) => {
|
|
29488
29579
|
controller.signal.throwIfAborted();
|
|
29489
29580
|
let bytes = data instanceof Uint8Array ? data : data === void 0 ? void 0 : new Uint8Array(data);
|
|
@@ -30832,6 +30923,7 @@ var WorldStreamer = class {
|
|
|
30832
30923
|
parseSceneDocument,
|
|
30833
30924
|
prefix,
|
|
30834
30925
|
registerNodeEnv,
|
|
30926
|
+
resolveLegacyBlock,
|
|
30835
30927
|
sampleEntityAnimation,
|
|
30836
30928
|
sampleEntityKeyframes,
|
|
30837
30929
|
sampleFluid,
|