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.mjs
CHANGED
|
@@ -23749,158 +23749,6 @@ function isBlockObject(obj) {
|
|
|
23749
23749
|
init_FluidGeometry();
|
|
23750
23750
|
init_FluidModelObject();
|
|
23751
23751
|
|
|
23752
|
-
// src/model/multiblock/LitematicaParser.ts
|
|
23753
|
-
init_MineRenderError();
|
|
23754
|
-
var LitematicaParser = class {
|
|
23755
|
-
/**
|
|
23756
|
-
* Parses decoded NBT, preserving subregion placement and omitting air.
|
|
23757
|
-
* Later entries in regions replace earlier cells, including with air.
|
|
23758
|
-
* Size encloses all region bounds; positions retain the schematic origin.
|
|
23759
|
-
* Metadata and scheduled ticks are not imported. Named regions can be placed individually.
|
|
23760
|
-
*/
|
|
23761
|
-
static async parse(nbt) {
|
|
23762
|
-
const tags = nbt.value;
|
|
23763
|
-
const version2 = integer2(tags.Version, "Version");
|
|
23764
|
-
if (version2 < 5 || version2 > 7) throw new MineRenderError(`Unsupported Litematica version ${version2}; expected 5, 6, or 7`);
|
|
23765
|
-
const dataVersion = tags.MinecraftDataVersion === void 0 ? void 0 : integer2(tags.MinecraftDataVersion, "MinecraftDataVersion");
|
|
23766
|
-
const blocks = /* @__PURE__ */ new Map();
|
|
23767
|
-
const regions = [];
|
|
23768
|
-
const min = [Infinity, Infinity, Infinity];
|
|
23769
|
-
const max = [-Infinity, -Infinity, -Infinity];
|
|
23770
|
-
for (const [name, tag] of Object.entries(compound(tags.Regions, "Regions"))) {
|
|
23771
|
-
const region = readRegion(compound(tag, `region ${name}`), name, dataVersion, blocks);
|
|
23772
|
-
regions.push(region);
|
|
23773
|
-
for (let axis = 0; axis < 3; axis++) {
|
|
23774
|
-
const start = minimum(region.position[axis], region.signedSize[axis]);
|
|
23775
|
-
min[axis] = Math.min(min[axis], start);
|
|
23776
|
-
max[axis] = Math.max(max[axis], start + region.size[axis]);
|
|
23777
|
-
}
|
|
23778
|
-
}
|
|
23779
|
-
return {
|
|
23780
|
-
size: regions.length ? min.map((value, axis) => max[axis] - value) : [0, 0, 0],
|
|
23781
|
-
blocks: [...blocks.values()],
|
|
23782
|
-
entities: regions.flatMap((region) => region.entities ?? []),
|
|
23783
|
-
dataVersion,
|
|
23784
|
-
regions
|
|
23785
|
-
};
|
|
23786
|
-
}
|
|
23787
|
-
};
|
|
23788
|
-
function readRegion(tags, name, dataVersion, combined) {
|
|
23789
|
-
const position = blockPosition(compound(tags.Position, `region ${name} Position`));
|
|
23790
|
-
const signedSize = blockPosition(compound(tags.Size, `region ${name} Size`));
|
|
23791
|
-
if (signedSize.some((value) => value === 0)) throw new MineRenderError(`Litematica region ${name} dimensions must be nonzero`);
|
|
23792
|
-
const size = signedSize.map(Math.abs);
|
|
23793
|
-
const min = position.map((value, axis) => minimum(value, signedSize[axis]));
|
|
23794
|
-
const [width, height, length] = size;
|
|
23795
|
-
const volume = width * height * length;
|
|
23796
|
-
const palette = compounds(tags.BlockStatePalette, "BlockStatePalette").map(blockState);
|
|
23797
|
-
if (!palette.length) throw new MineRenderError(`Litematica region ${name} has no block palette`);
|
|
23798
|
-
const bits = Math.max(2, Math.ceil(Math.log2(palette.length)));
|
|
23799
|
-
if (!Number.isSafeInteger(volume * bits)) throw new MineRenderError(`Litematica region ${name} volume is too large`);
|
|
23800
|
-
const packed = tags.BlockStates;
|
|
23801
|
-
const expected = Math.ceil(volume * bits / 64);
|
|
23802
|
-
if (packed?.type !== "longArray" || !Array.isArray(packed.value) || packed.value.length !== expected) {
|
|
23803
|
-
throw new MineRenderError(`Litematica region ${name} BlockStates must contain ${expected} longs`);
|
|
23804
|
-
}
|
|
23805
|
-
const words = packed.value.map((pair) => {
|
|
23806
|
-
if (!Array.isArray(pair) || pair.length !== 2 || !pair.every((value) => Number.isInteger(value) && value >= -2147483648 && value <= 4294967295)) {
|
|
23807
|
-
throw new MineRenderError(`Litematica region ${name} has an invalid BlockStates long`);
|
|
23808
|
-
}
|
|
23809
|
-
return [pair[0] >>> 0, pair[1] >>> 0];
|
|
23810
|
-
});
|
|
23811
|
-
const blockEntities = /* @__PURE__ */ new Map();
|
|
23812
|
-
for (const tile of compounds(tags.TileEntities, "TileEntities")) {
|
|
23813
|
-
const pos = blockPosition(tile);
|
|
23814
|
-
if (pos.some((value2, axis) => value2 < 0 || value2 >= size[axis])) {
|
|
23815
|
-
throw new MineRenderError(`Litematica region ${name} block entity is outside its dimensions`);
|
|
23816
|
-
}
|
|
23817
|
-
const index = pos[0] + pos[2] * width + pos[1] * width * length;
|
|
23818
|
-
if (blockEntities.has(index)) throw new MineRenderError(`Duplicate Litematica block entity at index ${index}`);
|
|
23819
|
-
const value = { ...tile };
|
|
23820
|
-
for (const [axis, key] of ["x", "y", "z"].entries()) value[key] = { type: "int", value: pos[axis] + min[axis] };
|
|
23821
|
-
blockEntities.set(index, { type: "compound", value });
|
|
23822
|
-
}
|
|
23823
|
-
const blocks = [];
|
|
23824
|
-
const mask = 2 ** bits - 1;
|
|
23825
|
-
for (let index = 0; index < volume; index++) {
|
|
23826
|
-
const word = Math.floor(index * bits / 64);
|
|
23827
|
-
const shift = index * bits % 64;
|
|
23828
|
-
const [high, low] = words[word];
|
|
23829
|
-
let value = shift < 32 ? low >>> shift : high >>> shift - 32;
|
|
23830
|
-
if (shift < 32 && shift + bits > 32) value |= high << 32 - shift;
|
|
23831
|
-
if (shift + bits > 64) value |= words[word + 1][1] << 64 - shift;
|
|
23832
|
-
const id = (value & mask) >>> 0;
|
|
23833
|
-
const state = palette[id];
|
|
23834
|
-
if (!state) throw new MineRenderError(`Litematica region ${name} palette index ${id} does not exist`);
|
|
23835
|
-
const pos = [
|
|
23836
|
-
index % width + min[0],
|
|
23837
|
-
Math.floor(index / (width * length)) + min[1],
|
|
23838
|
-
Math.floor(index / width) % length + min[2]
|
|
23839
|
-
];
|
|
23840
|
-
const key = pos.join(",");
|
|
23841
|
-
if (["minecraft:air", "minecraft:cave_air", "minecraft:void_air"].includes(state.type)) {
|
|
23842
|
-
combined.delete(key);
|
|
23843
|
-
continue;
|
|
23844
|
-
}
|
|
23845
|
-
const block = { type: state.type, properties: { ...state.properties }, position: pos, nbt: blockEntities.get(index) };
|
|
23846
|
-
blocks.push(block);
|
|
23847
|
-
combined.set(key, block);
|
|
23848
|
-
}
|
|
23849
|
-
const entities = compounds(tags.Entities, "Entities").map((entity) => {
|
|
23850
|
-
const pos = entity.Pos;
|
|
23851
|
-
if (pos?.type !== "list" || pos.value.type !== "double" || pos.value.value.length !== 3 || !pos.value.value.every(Number.isFinite)) {
|
|
23852
|
-
throw new MineRenderError(`Litematica region ${name} entity Pos must contain three doubles`);
|
|
23853
|
-
}
|
|
23854
|
-
const absolute = pos.value.value.map((value2, axis) => value2 + position[axis]);
|
|
23855
|
-
const value = { ...entity, Pos: { type: "list", value: { type: "double", value: [...absolute] } } };
|
|
23856
|
-
return { position: absolute, nbt: { type: "compound", value } };
|
|
23857
|
-
});
|
|
23858
|
-
return { name, position, signedSize, size, blocks, entities, dataVersion };
|
|
23859
|
-
}
|
|
23860
|
-
function minimum(position, size) {
|
|
23861
|
-
return position + Math.min(0, size + 1);
|
|
23862
|
-
}
|
|
23863
|
-
function integer2(tag, name) {
|
|
23864
|
-
if (tag?.type !== "int" || !Number.isInteger(tag.value) || tag.value < -2147483648 || tag.value > 2147483647) {
|
|
23865
|
-
throw new MineRenderError(`Litematica ${name} must be an int`);
|
|
23866
|
-
}
|
|
23867
|
-
return tag.value;
|
|
23868
|
-
}
|
|
23869
|
-
function compound(tag, name) {
|
|
23870
|
-
if (tag?.type !== "compound" || !tag.value || typeof tag.value !== "object" || Array.isArray(tag.value)) {
|
|
23871
|
-
throw new MineRenderError(`Litematica ${name} must be a compound`);
|
|
23872
|
-
}
|
|
23873
|
-
return tag.value;
|
|
23874
|
-
}
|
|
23875
|
-
function blockPosition(tags) {
|
|
23876
|
-
return ["x", "y", "z"].map((axis) => integer2(tags[axis], axis));
|
|
23877
|
-
}
|
|
23878
|
-
function compounds(tag, name) {
|
|
23879
|
-
if (tag === void 0) return [];
|
|
23880
|
-
if (tag.type === "list" && Array.isArray(tag.value.value)) {
|
|
23881
|
-
if (tag.value.value.length === 0) return [];
|
|
23882
|
-
if (tag.value.type === "compound") return tag.value.value;
|
|
23883
|
-
}
|
|
23884
|
-
throw new MineRenderError(`Litematica ${name} must be a compound list`);
|
|
23885
|
-
}
|
|
23886
|
-
function blockState(tags) {
|
|
23887
|
-
const name = tags.Name;
|
|
23888
|
-
if (name?.type !== "string" || !/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(name.value)) {
|
|
23889
|
-
throw new MineRenderError("Litematica palette entry must have a block Name");
|
|
23890
|
-
}
|
|
23891
|
-
const properties = {};
|
|
23892
|
-
if (tags.Properties !== void 0) {
|
|
23893
|
-
for (const [key, tag] of Object.entries(compound(tags.Properties, "block Properties"))) {
|
|
23894
|
-
if (tag?.type !== "string") throw new MineRenderError("Litematica block properties must be strings");
|
|
23895
|
-
Object.defineProperty(properties, key, { value: tag.value, enumerable: true, writable: true, configurable: true });
|
|
23896
|
-
}
|
|
23897
|
-
}
|
|
23898
|
-
return { type: name.value.includes(":") ? name.value : `minecraft:${name.value}`, properties };
|
|
23899
|
-
}
|
|
23900
|
-
|
|
23901
|
-
// src/model/multiblock/SchematicParser.ts
|
|
23902
|
-
init_MineRenderError();
|
|
23903
|
-
|
|
23904
23752
|
// src/model/multiblock/legacyBlocks.json
|
|
23905
23753
|
var legacyBlocks_default = {
|
|
23906
23754
|
__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",
|
|
@@ -24744,12 +24592,20 @@ var legacyBlocks_default = {
|
|
|
24744
24592
|
"131:15": "minecraft:tripwire_hook[powered=true,attached=true,facing=east]",
|
|
24745
24593
|
"132:0": "minecraft:tripwire[disarmed=false,east=false,powered=false,south=false,north=false,west=false,attached=false]",
|
|
24746
24594
|
"132:1": "minecraft:tripwire[disarmed=false,east=false,powered=true,south=false,north=false,west=false,attached=false]",
|
|
24595
|
+
"132:2": "minecraft:tripwire[disarmed=false,east=false,powered=false,south=false,north=false,west=false,attached=false]",
|
|
24596
|
+
"132:3": "minecraft:tripwire[disarmed=false,east=false,powered=true,south=false,north=false,west=false,attached=false]",
|
|
24747
24597
|
"132:4": "minecraft:tripwire[disarmed=false,east=false,powered=false,south=false,north=false,west=false,attached=true]",
|
|
24748
24598
|
"132:5": "minecraft:tripwire[disarmed=false,east=false,powered=true,south=false,north=false,west=false,attached=true]",
|
|
24599
|
+
"132:6": "minecraft:tripwire[disarmed=false,east=false,powered=false,south=false,north=false,west=false,attached=true]",
|
|
24600
|
+
"132:7": "minecraft:tripwire[disarmed=false,east=false,powered=true,south=false,north=false,west=false,attached=true]",
|
|
24749
24601
|
"132:8": "minecraft:tripwire[disarmed=true,east=false,powered=false,south=false,north=false,west=false,attached=false]",
|
|
24750
24602
|
"132:9": "minecraft:tripwire[disarmed=true,east=false,powered=true,south=false,north=false,west=false,attached=false]",
|
|
24603
|
+
"132:10": "minecraft:tripwire[disarmed=true,east=false,powered=false,south=false,north=false,west=false,attached=false]",
|
|
24604
|
+
"132:11": "minecraft:tripwire[disarmed=true,east=false,powered=true,south=false,north=false,west=false,attached=false]",
|
|
24751
24605
|
"132:12": "minecraft:tripwire[disarmed=true,east=false,powered=false,south=false,north=false,west=false,attached=true]",
|
|
24752
24606
|
"132:13": "minecraft:tripwire[disarmed=true,east=false,powered=true,south=false,north=false,west=false,attached=true]",
|
|
24607
|
+
"132:14": "minecraft:tripwire[disarmed=true,east=false,powered=false,south=false,north=false,west=false,attached=true]",
|
|
24608
|
+
"132:15": "minecraft:tripwire[disarmed=true,east=false,powered=true,south=false,north=false,west=false,attached=true]",
|
|
24753
24609
|
"133:0": "minecraft:emerald_block",
|
|
24754
24610
|
"134:0": "minecraft:spruce_stairs[half=bottom,shape=outer_right,facing=east]",
|
|
24755
24611
|
"134:1": "minecraft:spruce_stairs[half=bottom,shape=outer_right,facing=west]",
|
|
@@ -25590,7 +25446,175 @@ var legacyBlocks_default = {
|
|
|
25590
25446
|
}
|
|
25591
25447
|
};
|
|
25592
25448
|
|
|
25449
|
+
// src/model/multiblock/LegacyBlocks.ts
|
|
25450
|
+
function resolveLegacyBlock(id, metadata, customMappings = {}, lenient = false) {
|
|
25451
|
+
const mapping = legacyBlocks_default.blocks;
|
|
25452
|
+
const key = `${id}:${metadata}`;
|
|
25453
|
+
let mapped = customMappings[key] ?? mapping[key];
|
|
25454
|
+
if (!mapped && lenient) mapped = customMappings[`${id}:0`] ?? mapping[`${id}:0`];
|
|
25455
|
+
if (!mapped) return void 0;
|
|
25456
|
+
const [type, state] = mapped.split("[");
|
|
25457
|
+
const properties = {};
|
|
25458
|
+
if (state) {
|
|
25459
|
+
for (const property of state.slice(0, -1).split(",")) {
|
|
25460
|
+
const [key2, value] = property.split("=");
|
|
25461
|
+
properties[key2] = value;
|
|
25462
|
+
}
|
|
25463
|
+
}
|
|
25464
|
+
return { type, properties };
|
|
25465
|
+
}
|
|
25466
|
+
|
|
25467
|
+
// src/model/multiblock/LitematicaParser.ts
|
|
25468
|
+
init_MineRenderError();
|
|
25469
|
+
var LitematicaParser = class {
|
|
25470
|
+
/**
|
|
25471
|
+
* Parses decoded NBT, preserving subregion placement and omitting air.
|
|
25472
|
+
* Later entries in regions replace earlier cells, including with air.
|
|
25473
|
+
* Size encloses all region bounds; positions retain the schematic origin.
|
|
25474
|
+
* Metadata and scheduled ticks are not imported. Named regions can be placed individually.
|
|
25475
|
+
*/
|
|
25476
|
+
static async parse(nbt) {
|
|
25477
|
+
const tags = nbt.value;
|
|
25478
|
+
const version2 = integer2(tags.Version, "Version");
|
|
25479
|
+
if (version2 < 5 || version2 > 7) throw new MineRenderError(`Unsupported Litematica version ${version2}; expected 5, 6, or 7`);
|
|
25480
|
+
const dataVersion = tags.MinecraftDataVersion === void 0 ? void 0 : integer2(tags.MinecraftDataVersion, "MinecraftDataVersion");
|
|
25481
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
25482
|
+
const regions = [];
|
|
25483
|
+
const min = [Infinity, Infinity, Infinity];
|
|
25484
|
+
const max = [-Infinity, -Infinity, -Infinity];
|
|
25485
|
+
for (const [name, tag] of Object.entries(compound(tags.Regions, "Regions"))) {
|
|
25486
|
+
const region = readRegion(compound(tag, `region ${name}`), name, dataVersion, blocks);
|
|
25487
|
+
regions.push(region);
|
|
25488
|
+
for (let axis = 0; axis < 3; axis++) {
|
|
25489
|
+
const start = minimum(region.position[axis], region.signedSize[axis]);
|
|
25490
|
+
min[axis] = Math.min(min[axis], start);
|
|
25491
|
+
max[axis] = Math.max(max[axis], start + region.size[axis]);
|
|
25492
|
+
}
|
|
25493
|
+
}
|
|
25494
|
+
return {
|
|
25495
|
+
size: regions.length ? min.map((value, axis) => max[axis] - value) : [0, 0, 0],
|
|
25496
|
+
blocks: [...blocks.values()],
|
|
25497
|
+
entities: regions.flatMap((region) => region.entities ?? []),
|
|
25498
|
+
dataVersion,
|
|
25499
|
+
regions
|
|
25500
|
+
};
|
|
25501
|
+
}
|
|
25502
|
+
};
|
|
25503
|
+
function readRegion(tags, name, dataVersion, combined) {
|
|
25504
|
+
const position = blockPosition(compound(tags.Position, `region ${name} Position`));
|
|
25505
|
+
const signedSize = blockPosition(compound(tags.Size, `region ${name} Size`));
|
|
25506
|
+
if (signedSize.some((value) => value === 0)) throw new MineRenderError(`Litematica region ${name} dimensions must be nonzero`);
|
|
25507
|
+
const size = signedSize.map(Math.abs);
|
|
25508
|
+
const min = position.map((value, axis) => minimum(value, signedSize[axis]));
|
|
25509
|
+
const [width, height, length] = size;
|
|
25510
|
+
const volume = width * height * length;
|
|
25511
|
+
const palette = compounds(tags.BlockStatePalette, "BlockStatePalette").map(blockState);
|
|
25512
|
+
if (!palette.length) throw new MineRenderError(`Litematica region ${name} has no block palette`);
|
|
25513
|
+
const bits = Math.max(2, Math.ceil(Math.log2(palette.length)));
|
|
25514
|
+
if (!Number.isSafeInteger(volume * bits)) throw new MineRenderError(`Litematica region ${name} volume is too large`);
|
|
25515
|
+
const packed = tags.BlockStates;
|
|
25516
|
+
const expected = Math.ceil(volume * bits / 64);
|
|
25517
|
+
if (packed?.type !== "longArray" || !Array.isArray(packed.value) || packed.value.length !== expected) {
|
|
25518
|
+
throw new MineRenderError(`Litematica region ${name} BlockStates must contain ${expected} longs`);
|
|
25519
|
+
}
|
|
25520
|
+
const words = packed.value.map((pair) => {
|
|
25521
|
+
if (!Array.isArray(pair) || pair.length !== 2 || !pair.every((value) => Number.isInteger(value) && value >= -2147483648 && value <= 4294967295)) {
|
|
25522
|
+
throw new MineRenderError(`Litematica region ${name} has an invalid BlockStates long`);
|
|
25523
|
+
}
|
|
25524
|
+
return [pair[0] >>> 0, pair[1] >>> 0];
|
|
25525
|
+
});
|
|
25526
|
+
const blockEntities = /* @__PURE__ */ new Map();
|
|
25527
|
+
for (const tile of compounds(tags.TileEntities, "TileEntities")) {
|
|
25528
|
+
const pos = blockPosition(tile);
|
|
25529
|
+
if (pos.some((value2, axis) => value2 < 0 || value2 >= size[axis])) {
|
|
25530
|
+
throw new MineRenderError(`Litematica region ${name} block entity is outside its dimensions`);
|
|
25531
|
+
}
|
|
25532
|
+
const index = pos[0] + pos[2] * width + pos[1] * width * length;
|
|
25533
|
+
if (blockEntities.has(index)) throw new MineRenderError(`Duplicate Litematica block entity at index ${index}`);
|
|
25534
|
+
const value = { ...tile };
|
|
25535
|
+
for (const [axis, key] of ["x", "y", "z"].entries()) value[key] = { type: "int", value: pos[axis] + min[axis] };
|
|
25536
|
+
blockEntities.set(index, { type: "compound", value });
|
|
25537
|
+
}
|
|
25538
|
+
const blocks = [];
|
|
25539
|
+
const mask = 2 ** bits - 1;
|
|
25540
|
+
for (let index = 0; index < volume; index++) {
|
|
25541
|
+
const word = Math.floor(index * bits / 64);
|
|
25542
|
+
const shift = index * bits % 64;
|
|
25543
|
+
const [high, low] = words[word];
|
|
25544
|
+
let value = shift < 32 ? low >>> shift : high >>> shift - 32;
|
|
25545
|
+
if (shift < 32 && shift + bits > 32) value |= high << 32 - shift;
|
|
25546
|
+
if (shift + bits > 64) value |= words[word + 1][1] << 64 - shift;
|
|
25547
|
+
const id = (value & mask) >>> 0;
|
|
25548
|
+
const state = palette[id];
|
|
25549
|
+
if (!state) throw new MineRenderError(`Litematica region ${name} palette index ${id} does not exist`);
|
|
25550
|
+
const pos = [
|
|
25551
|
+
index % width + min[0],
|
|
25552
|
+
Math.floor(index / (width * length)) + min[1],
|
|
25553
|
+
Math.floor(index / width) % length + min[2]
|
|
25554
|
+
];
|
|
25555
|
+
const key = pos.join(",");
|
|
25556
|
+
if (["minecraft:air", "minecraft:cave_air", "minecraft:void_air"].includes(state.type)) {
|
|
25557
|
+
combined.delete(key);
|
|
25558
|
+
continue;
|
|
25559
|
+
}
|
|
25560
|
+
const block = { type: state.type, properties: { ...state.properties }, position: pos, nbt: blockEntities.get(index) };
|
|
25561
|
+
blocks.push(block);
|
|
25562
|
+
combined.set(key, block);
|
|
25563
|
+
}
|
|
25564
|
+
const entities = compounds(tags.Entities, "Entities").map((entity) => {
|
|
25565
|
+
const pos = entity.Pos;
|
|
25566
|
+
if (pos?.type !== "list" || pos.value.type !== "double" || pos.value.value.length !== 3 || !pos.value.value.every(Number.isFinite)) {
|
|
25567
|
+
throw new MineRenderError(`Litematica region ${name} entity Pos must contain three doubles`);
|
|
25568
|
+
}
|
|
25569
|
+
const absolute = pos.value.value.map((value2, axis) => value2 + position[axis]);
|
|
25570
|
+
const value = { ...entity, Pos: { type: "list", value: { type: "double", value: [...absolute] } } };
|
|
25571
|
+
return { position: absolute, nbt: { type: "compound", value } };
|
|
25572
|
+
});
|
|
25573
|
+
return { name, position, signedSize, size, blocks, entities, dataVersion };
|
|
25574
|
+
}
|
|
25575
|
+
function minimum(position, size) {
|
|
25576
|
+
return position + Math.min(0, size + 1);
|
|
25577
|
+
}
|
|
25578
|
+
function integer2(tag, name) {
|
|
25579
|
+
if (tag?.type !== "int" || !Number.isInteger(tag.value) || tag.value < -2147483648 || tag.value > 2147483647) {
|
|
25580
|
+
throw new MineRenderError(`Litematica ${name} must be an int`);
|
|
25581
|
+
}
|
|
25582
|
+
return tag.value;
|
|
25583
|
+
}
|
|
25584
|
+
function compound(tag, name) {
|
|
25585
|
+
if (tag?.type !== "compound" || !tag.value || typeof tag.value !== "object" || Array.isArray(tag.value)) {
|
|
25586
|
+
throw new MineRenderError(`Litematica ${name} must be a compound`);
|
|
25587
|
+
}
|
|
25588
|
+
return tag.value;
|
|
25589
|
+
}
|
|
25590
|
+
function blockPosition(tags) {
|
|
25591
|
+
return ["x", "y", "z"].map((axis) => integer2(tags[axis], axis));
|
|
25592
|
+
}
|
|
25593
|
+
function compounds(tag, name) {
|
|
25594
|
+
if (tag === void 0) return [];
|
|
25595
|
+
if (tag.type === "list" && Array.isArray(tag.value.value)) {
|
|
25596
|
+
if (tag.value.value.length === 0) return [];
|
|
25597
|
+
if (tag.value.type === "compound") return tag.value.value;
|
|
25598
|
+
}
|
|
25599
|
+
throw new MineRenderError(`Litematica ${name} must be a compound list`);
|
|
25600
|
+
}
|
|
25601
|
+
function blockState(tags) {
|
|
25602
|
+
const name = tags.Name;
|
|
25603
|
+
if (name?.type !== "string" || !/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(name.value)) {
|
|
25604
|
+
throw new MineRenderError("Litematica palette entry must have a block Name");
|
|
25605
|
+
}
|
|
25606
|
+
const properties = {};
|
|
25607
|
+
if (tags.Properties !== void 0) {
|
|
25608
|
+
for (const [key, tag] of Object.entries(compound(tags.Properties, "block Properties"))) {
|
|
25609
|
+
if (tag?.type !== "string") throw new MineRenderError("Litematica block properties must be strings");
|
|
25610
|
+
Object.defineProperty(properties, key, { value: tag.value, enumerable: true, writable: true, configurable: true });
|
|
25611
|
+
}
|
|
25612
|
+
}
|
|
25613
|
+
return { type: name.value.includes(":") ? name.value : `minecraft:${name.value}`, properties };
|
|
25614
|
+
}
|
|
25615
|
+
|
|
25593
25616
|
// src/model/multiblock/SchematicParser.ts
|
|
25617
|
+
init_MineRenderError();
|
|
25594
25618
|
var SchematicParser = class {
|
|
25595
25619
|
/**
|
|
25596
25620
|
* Parses a legacy schematic using custom mappings before the bundled defaults.
|
|
@@ -25632,35 +25656,21 @@ var SchematicParser = class {
|
|
|
25632
25656
|
tileEntities.set((y * length + z) * width + x, { type: "compound", value: tile });
|
|
25633
25657
|
}
|
|
25634
25658
|
const blocks = [];
|
|
25635
|
-
const mapping = legacyBlocks_default.blocks;
|
|
25636
25659
|
for (let index = 0; index < volume; index++) {
|
|
25637
25660
|
const packed = addBlocks?.value[index >> 1] ?? 0;
|
|
25638
25661
|
const high = packed >> (index & 1) * 4 & 15;
|
|
25639
25662
|
const id = high << 8 | ids.value[index] & 255;
|
|
25640
25663
|
const metadata = data.value[index] & 255;
|
|
25641
|
-
const
|
|
25642
|
-
|
|
25643
|
-
if (!mapped && options.lenient) {
|
|
25644
|
-
mapped = customMappings[`${id}:0`] ?? mapping[`${id}:0`];
|
|
25645
|
-
}
|
|
25646
|
-
if (!mapped) {
|
|
25664
|
+
const block = resolveLegacyBlock(id, metadata, customMappings, options.lenient);
|
|
25665
|
+
if (!block) {
|
|
25647
25666
|
if (options.lenient) {
|
|
25648
25667
|
continue;
|
|
25649
25668
|
}
|
|
25650
25669
|
throw new MineRenderError(`Unsupported legacy block ${id}:${metadata} at schematic index ${index}`);
|
|
25651
25670
|
}
|
|
25652
|
-
|
|
25653
|
-
if (type === "minecraft:air") continue;
|
|
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
|
-
}
|
|
25671
|
+
if (block.type === "minecraft:air") continue;
|
|
25661
25672
|
blocks.push({
|
|
25662
|
-
|
|
25663
|
-
properties,
|
|
25673
|
+
...block,
|
|
25664
25674
|
position: [index % width, Math.floor(index / (width * length)), Math.floor(index / width) % length],
|
|
25665
25675
|
nbt: tileEntities.get(index)
|
|
25666
25676
|
});
|
|
@@ -29048,10 +29058,13 @@ var AnvilParser = class {
|
|
|
29048
29058
|
return this.locations(this.bytes(data)).map(({ x, z }) => ({ x, z }));
|
|
29049
29059
|
}
|
|
29050
29060
|
/** Decodes all stored chunks. Gzip, zlib, LZ4, and uncompressed payloads are supported. */
|
|
29051
|
-
static async parse(data) {
|
|
29061
|
+
static async parse(data, options = {}) {
|
|
29062
|
+
options.signal?.throwIfAborted();
|
|
29052
29063
|
const bytes = this.bytes(data);
|
|
29053
29064
|
const chunks = [];
|
|
29054
|
-
for (const location of this.locations(bytes))
|
|
29065
|
+
for (const location of this.locations(bytes)) {
|
|
29066
|
+
chunks.push(await this.abortable(this.readChunk(bytes, location, options), options.signal));
|
|
29067
|
+
}
|
|
29055
29068
|
return { chunks };
|
|
29056
29069
|
}
|
|
29057
29070
|
/**
|
|
@@ -29060,13 +29073,28 @@ var AnvilParser = class {
|
|
|
29060
29073
|
* @param localZ - Chunk z within the region, from 0 to 31.
|
|
29061
29074
|
* @returns The chunk with absolute coordinates, or `undefined` if the region has no chunk there.
|
|
29062
29075
|
*/
|
|
29063
|
-
static async parseChunk(data, localX, localZ) {
|
|
29076
|
+
static async parseChunk(data, localX, localZ, options = {}) {
|
|
29077
|
+
options.signal?.throwIfAborted();
|
|
29064
29078
|
if (![localX, localZ].every((value) => Number.isInteger(value) && value >= 0 && value < 32)) {
|
|
29065
29079
|
throw new RangeError("Region-local chunk coordinates must be integers from 0 to 31");
|
|
29066
29080
|
}
|
|
29067
29081
|
const bytes = this.bytes(data);
|
|
29068
29082
|
const location = this.locations(bytes).find(({ x, z }) => x === localX && z === localZ);
|
|
29069
|
-
return location ? this.readChunk(bytes, location) : void 0;
|
|
29083
|
+
return location ? this.abortable(this.readChunk(bytes, location, options), options.signal) : void 0;
|
|
29084
|
+
}
|
|
29085
|
+
static async abortable(promise, signal) {
|
|
29086
|
+
if (!signal) return promise;
|
|
29087
|
+
let abort;
|
|
29088
|
+
const cancelled = new Promise((_, reject) => {
|
|
29089
|
+
abort = () => reject(signal.reason);
|
|
29090
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
29091
|
+
if (signal.aborted) abort();
|
|
29092
|
+
});
|
|
29093
|
+
try {
|
|
29094
|
+
return await Promise.race([promise, cancelled]);
|
|
29095
|
+
} finally {
|
|
29096
|
+
signal.removeEventListener("abort", abort);
|
|
29097
|
+
}
|
|
29070
29098
|
}
|
|
29071
29099
|
static bytes(data) {
|
|
29072
29100
|
return data instanceof Uint8Array ? data : new Uint8Array(data);
|
|
@@ -29092,20 +29120,41 @@ var AnvilParser = class {
|
|
|
29092
29120
|
}
|
|
29093
29121
|
return locations;
|
|
29094
29122
|
}
|
|
29095
|
-
static async readChunk(data, location) {
|
|
29096
|
-
|
|
29097
|
-
|
|
29123
|
+
static async readChunk(data, location, options) {
|
|
29124
|
+
options.signal?.throwIfAborted();
|
|
29125
|
+
const format2 = data[location.offset + 4];
|
|
29126
|
+
const external = (format2 & 128) !== 0;
|
|
29127
|
+
const compression = format2 & 127;
|
|
29098
29128
|
if (compression !== 1 && compression !== 2 && compression !== 3 && compression !== 4) {
|
|
29099
29129
|
throw new MineRenderError(`Unsupported Anvil compression ${compression}`);
|
|
29100
29130
|
}
|
|
29101
29131
|
let payload = data.subarray(location.offset + 5, location.offset + 4 + location.length);
|
|
29132
|
+
let expected;
|
|
29133
|
+
if (external) {
|
|
29134
|
+
if (!options.region || !options.readExternalChunk) {
|
|
29135
|
+
throw new MineRenderError("External Anvil .mcc chunks require region coordinates and readExternalChunk");
|
|
29136
|
+
}
|
|
29137
|
+
if (![options.region.x, options.region.z].every(Number.isSafeInteger)) {
|
|
29138
|
+
throw new RangeError("Anvil region coordinates must be safe integers");
|
|
29139
|
+
}
|
|
29140
|
+
expected = { x: options.region.x * 32 + location.x, z: options.region.z * 32 + location.z };
|
|
29141
|
+
if (![expected.x, expected.z].every(Number.isSafeInteger)) {
|
|
29142
|
+
throw new RangeError("External Anvil chunk coordinates must be safe integers");
|
|
29143
|
+
}
|
|
29144
|
+
const externalData = await options.readExternalChunk(expected.x, expected.z, options.signal);
|
|
29145
|
+
options.signal?.throwIfAborted();
|
|
29146
|
+
if (externalData === void 0) throw new MineRenderError(`External Anvil chunk c.${expected.x}.${expected.z}.mcc is missing`);
|
|
29147
|
+
payload = this.bytes(externalData);
|
|
29148
|
+
}
|
|
29102
29149
|
if (compression === 4) {
|
|
29103
29150
|
payload = decodeAnvilLz4(payload);
|
|
29104
29151
|
} else if (compression !== 3) {
|
|
29105
29152
|
const stream = new Blob([payload]).stream().pipeThrough(new DecompressionStream(compression === 1 ? "gzip" : "deflate"));
|
|
29106
29153
|
payload = new Uint8Array(await new Response(stream).arrayBuffer());
|
|
29107
29154
|
}
|
|
29155
|
+
options.signal?.throwIfAborted();
|
|
29108
29156
|
const nbt = await NBTHelper.fromBuffer(payload, "big");
|
|
29157
|
+
options.signal?.throwIfAborted();
|
|
29109
29158
|
const root = nbt.value;
|
|
29110
29159
|
const level = root.Level?.type === "compound" ? root.Level.value : root;
|
|
29111
29160
|
const dataVersion = root.DataVersion?.type === "int" ? root.DataVersion.value : void 0;
|
|
@@ -29118,28 +29167,37 @@ var AnvilParser = class {
|
|
|
29118
29167
|
if ((chunk.x % 32 + 32) % 32 !== location.x || (chunk.z % 32 + 32) % 32 !== location.z) {
|
|
29119
29168
|
throw new MineRenderError("Anvil chunk coordinates do not match its region location");
|
|
29120
29169
|
}
|
|
29170
|
+
if (expected && (chunk.x !== expected.x || chunk.z !== expected.z)) {
|
|
29171
|
+
throw new MineRenderError(`Anvil chunk coordinates do not match external file c.${expected.x}.${expected.z}.mcc`);
|
|
29172
|
+
}
|
|
29173
|
+
const numericStates = /* @__PURE__ */ new Map();
|
|
29121
29174
|
for (const section of this.compounds(level.sections ?? level.Sections, "sections")) {
|
|
29122
29175
|
const y = this.integer(section.Y, "section Y");
|
|
29176
|
+
let data2;
|
|
29123
29177
|
if (section.Blocks || section.Data || section.Add) {
|
|
29124
|
-
|
|
29125
|
-
|
|
29126
|
-
|
|
29127
|
-
|
|
29128
|
-
}
|
|
29129
|
-
|
|
29130
|
-
|
|
29131
|
-
|
|
29132
|
-
|
|
29133
|
-
|
|
29134
|
-
const
|
|
29135
|
-
|
|
29136
|
-
|
|
29137
|
-
const
|
|
29138
|
-
|
|
29139
|
-
|
|
29178
|
+
if (section.Palette || section.BlockStates || section.block_states) {
|
|
29179
|
+
throw new MineRenderError(`Anvil section ${y} mixes numeric and paletted block states`);
|
|
29180
|
+
}
|
|
29181
|
+
data2 = this.numericSection(section, y, options, numericStates);
|
|
29182
|
+
} else {
|
|
29183
|
+
if (section.block_states && section.block_states.type !== "compound") {
|
|
29184
|
+
throw new MineRenderError("Anvil block_states must be a compound");
|
|
29185
|
+
}
|
|
29186
|
+
const modern = section.block_states?.type === "compound" ? section.block_states.value : void 0;
|
|
29187
|
+
const paletteTag = modern ? modern.palette : section.Palette;
|
|
29188
|
+
const palette = paletteTag?.type === "list" && paletteTag.value.type === "string" ? paletteTag.value.value : this.compounds(paletteTag, "block palette");
|
|
29189
|
+
data2 = new ChunkData();
|
|
29190
|
+
if (palette.length) {
|
|
29191
|
+
const states = palette.map((entry) => this.block(entry));
|
|
29192
|
+
const indices = this.unpack(modern ? modern.data : section.BlockStates, states.length, !!modern, dataVersion);
|
|
29193
|
+
for (let index = 0; index < 4096; index++) {
|
|
29194
|
+
const state = states[indices[index]];
|
|
29195
|
+
if (!state) throw new MineRenderError(`Anvil palette index ${indices[index]} is out of range`);
|
|
29196
|
+
if (!ChunkData.isAir(state)) data2.set(index, state);
|
|
29197
|
+
}
|
|
29198
|
+
} else if (modern || section.Palette || section.BlockStates) {
|
|
29199
|
+
throw new MineRenderError("Anvil section has block states without a palette");
|
|
29140
29200
|
}
|
|
29141
|
-
} else if (modern || section.Palette || section.BlockStates) {
|
|
29142
|
-
throw new MineRenderError("Anvil section has block states without a palette");
|
|
29143
29201
|
}
|
|
29144
29202
|
if (chunk.sections.some((section2) => section2.y === y)) throw new MineRenderError(`Duplicate Anvil section Y ${y}`);
|
|
29145
29203
|
chunk.sections.push({ y, data: data2 });
|
|
@@ -29167,6 +29225,33 @@ var AnvilParser = class {
|
|
|
29167
29225
|
}
|
|
29168
29226
|
return chunk;
|
|
29169
29227
|
}
|
|
29228
|
+
static numericSection(section, y, options, states) {
|
|
29229
|
+
const bytes = (name, length, optional = false) => {
|
|
29230
|
+
const tag = section[name];
|
|
29231
|
+
if (!tag && optional) return void 0;
|
|
29232
|
+
if (tag?.type !== "byteArray" || !Array.isArray(tag.value) || tag.value.length !== length) {
|
|
29233
|
+
throw new MineRenderError(`Anvil section ${y} ${name} must contain ${length} bytes`);
|
|
29234
|
+
}
|
|
29235
|
+
return tag.value;
|
|
29236
|
+
};
|
|
29237
|
+
const ids = bytes("Blocks", 4096);
|
|
29238
|
+
const metadata = bytes("Data", 2048, true);
|
|
29239
|
+
const extra = bytes("Add", 2048, true);
|
|
29240
|
+
const data = new ChunkData();
|
|
29241
|
+
for (let index = 0; index < 4096; index++) {
|
|
29242
|
+
const shift = (index & 1) * 4;
|
|
29243
|
+
const id = ids[index] & 255 | ((extra?.[index >> 1] ?? 0) >> shift & 15) << 8;
|
|
29244
|
+
const value = (metadata?.[index >> 1] ?? 0) >> shift & 15;
|
|
29245
|
+
const key = id * 16 + value;
|
|
29246
|
+
if (!states.has(key)) states.set(key, resolveLegacyBlock(id, value, options.legacyMappings, options.lenient));
|
|
29247
|
+
const block = states.get(key);
|
|
29248
|
+
if (!block && !options.lenient) {
|
|
29249
|
+
throw new MineRenderError(`Unsupported legacy block ${id}:${value} at Anvil section ${y} index ${index}`);
|
|
29250
|
+
}
|
|
29251
|
+
if (block && !ChunkData.isAir(block)) data.set(index, block);
|
|
29252
|
+
}
|
|
29253
|
+
return data;
|
|
29254
|
+
}
|
|
29170
29255
|
static compounds(tag, name) {
|
|
29171
29256
|
if (!tag) return [];
|
|
29172
29257
|
if (tag.type !== "list" || tag.value.type !== "compound" && tag.value.value.length !== 0) {
|
|
@@ -29249,6 +29334,7 @@ var AnvilWorldSource = class {
|
|
|
29249
29334
|
this.cachedBytes = 0;
|
|
29250
29335
|
this.maxCachedRegions = options.maxCachedRegions ?? 4;
|
|
29251
29336
|
this.maxCachedBytes = options.maxCachedBytes ?? 64 * 1024 * 1024;
|
|
29337
|
+
this.parseOptions = { readExternalChunk: options.readExternalChunk, legacyMappings: options.legacyMappings, lenient: options.lenient };
|
|
29252
29338
|
for (const [name, value] of Object.entries({ maxCachedRegions: this.maxCachedRegions, maxCachedBytes: this.maxCachedBytes })) {
|
|
29253
29339
|
if (!Number.isSafeInteger(value) || value < 0) throw new RangeError(`${name} must be a nonnegative safe integer`);
|
|
29254
29340
|
}
|
|
@@ -29262,7 +29348,11 @@ var AnvilWorldSource = class {
|
|
|
29262
29348
|
const data = await this.getRegion(regionX, regionZ, signal);
|
|
29263
29349
|
signal?.throwIfAborted();
|
|
29264
29350
|
if (!data) return void 0;
|
|
29265
|
-
const chunk = await
|
|
29351
|
+
const chunk = await AnvilParser.parseChunk(data, x - regionX * 32, z - regionZ * 32, {
|
|
29352
|
+
...this.parseOptions,
|
|
29353
|
+
region: { x: regionX, z: regionZ },
|
|
29354
|
+
signal
|
|
29355
|
+
});
|
|
29266
29356
|
signal?.throwIfAborted();
|
|
29267
29357
|
if (chunk && (chunk.x !== x || chunk.z !== z)) {
|
|
29268
29358
|
throw new MineRenderError(`Anvil chunk coordinates ${chunk.x},${chunk.z} do not match requested column ${x},${z}`);
|
|
@@ -29293,7 +29383,7 @@ var AnvilWorldSource = class {
|
|
|
29293
29383
|
settled: false,
|
|
29294
29384
|
promise: Promise.resolve().then(() => {
|
|
29295
29385
|
controller.signal.throwIfAborted();
|
|
29296
|
-
return this.readRegion(x, z, controller.signal);
|
|
29386
|
+
return abortable2(this.readRegion(x, z, controller.signal), controller.signal);
|
|
29297
29387
|
}).then((data) => {
|
|
29298
29388
|
controller.signal.throwIfAborted();
|
|
29299
29389
|
let bytes = data instanceof Uint8Array ? data : data === void 0 ? void 0 : new Uint8Array(data);
|
|
@@ -30641,6 +30731,7 @@ export {
|
|
|
30641
30731
|
prefix,
|
|
30642
30732
|
probeImageSize,
|
|
30643
30733
|
registerBrowserEnv,
|
|
30734
|
+
resolveLegacyBlock,
|
|
30644
30735
|
sampleEntityAnimation,
|
|
30645
30736
|
sampleEntityKeyframes,
|
|
30646
30737
|
sampleFluid,
|