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/bundle.js CHANGED
@@ -106662,6 +106662,7 @@ ${shader.fragmentShader}`.replace(
106662
106662
  prefix: () => prefix,
106663
106663
  probeImageSize: () => probeImageSize,
106664
106664
  registerBrowserEnv: () => registerBrowserEnv,
106665
+ resolveLegacyBlock: () => resolveLegacyBlock,
106665
106666
  sampleEntityAnimation: () => sampleEntityAnimation,
106666
106667
  sampleEntityKeyframes: () => sampleEntityKeyframes,
106667
106668
  sampleFluid: () => sampleFluid,
@@ -111945,168 +111946,10 @@ property list uchar int vertex_index
111945
111946
  init_buffer2();
111946
111947
  init_process2();
111947
111948
 
111948
- // src/model/multiblock/LitematicaParser.ts
111949
+ // src/model/multiblock/LegacyBlocks.ts
111949
111950
  init_dirname();
111950
111951
  init_buffer2();
111951
111952
  init_process2();
111952
- init_MineRenderError();
111953
- var LitematicaParser = class {
111954
- /**
111955
- * Parses decoded NBT, preserving subregion placement and omitting air.
111956
- * Later entries in regions replace earlier cells, including with air.
111957
- * Size encloses all region bounds; positions retain the schematic origin.
111958
- * Metadata and scheduled ticks are not imported. Named regions can be placed individually.
111959
- */
111960
- static async parse(nbt) {
111961
- const tags = nbt.value;
111962
- const version3 = integer2(tags.Version, "Version");
111963
- if (version3 < 5 || version3 > 7) throw new MineRenderError(`Unsupported Litematica version ${version3}; expected 5, 6, or 7`);
111964
- const dataVersion = tags.MinecraftDataVersion === void 0 ? void 0 : integer2(tags.MinecraftDataVersion, "MinecraftDataVersion");
111965
- const blocks = /* @__PURE__ */ new Map();
111966
- const regions = [];
111967
- const min = [Infinity, Infinity, Infinity];
111968
- const max2 = [-Infinity, -Infinity, -Infinity];
111969
- for (const [name2, tag] of Object.entries(compound(tags.Regions, "Regions"))) {
111970
- const region = readRegion(compound(tag, `region ${name2}`), name2, dataVersion, blocks);
111971
- regions.push(region);
111972
- for (let axis = 0; axis < 3; axis++) {
111973
- const start = minimum(region.position[axis], region.signedSize[axis]);
111974
- min[axis] = Math.min(min[axis], start);
111975
- max2[axis] = Math.max(max2[axis], start + region.size[axis]);
111976
- }
111977
- }
111978
- return {
111979
- size: regions.length ? min.map((value, axis) => max2[axis] - value) : [0, 0, 0],
111980
- blocks: [...blocks.values()],
111981
- entities: regions.flatMap((region) => region.entities ?? []),
111982
- dataVersion,
111983
- regions
111984
- };
111985
- }
111986
- };
111987
- function readRegion(tags, name2, dataVersion, combined) {
111988
- const position = blockPosition(compound(tags.Position, `region ${name2} Position`));
111989
- const signedSize = blockPosition(compound(tags.Size, `region ${name2} Size`));
111990
- if (signedSize.some((value) => value === 0)) throw new MineRenderError(`Litematica region ${name2} dimensions must be nonzero`);
111991
- const size = signedSize.map(Math.abs);
111992
- const min = position.map((value, axis) => minimum(value, signedSize[axis]));
111993
- const [width, height, length] = size;
111994
- const volume = width * height * length;
111995
- const palette = compounds(tags.BlockStatePalette, "BlockStatePalette").map(blockState);
111996
- if (!palette.length) throw new MineRenderError(`Litematica region ${name2} has no block palette`);
111997
- const bits2 = Math.max(2, Math.ceil(Math.log2(palette.length)));
111998
- if (!Number.isSafeInteger(volume * bits2)) throw new MineRenderError(`Litematica region ${name2} volume is too large`);
111999
- const packed = tags.BlockStates;
112000
- const expected = Math.ceil(volume * bits2 / 64);
112001
- if (packed?.type !== "longArray" || !Array.isArray(packed.value) || packed.value.length !== expected) {
112002
- throw new MineRenderError(`Litematica region ${name2} BlockStates must contain ${expected} longs`);
112003
- }
112004
- const words = packed.value.map((pair) => {
112005
- if (!Array.isArray(pair) || pair.length !== 2 || !pair.every((value) => Number.isInteger(value) && value >= -2147483648 && value <= 4294967295)) {
112006
- throw new MineRenderError(`Litematica region ${name2} has an invalid BlockStates long`);
112007
- }
112008
- return [pair[0] >>> 0, pair[1] >>> 0];
112009
- });
112010
- const blockEntities = /* @__PURE__ */ new Map();
112011
- for (const tile of compounds(tags.TileEntities, "TileEntities")) {
112012
- const pos = blockPosition(tile);
112013
- if (pos.some((value2, axis) => value2 < 0 || value2 >= size[axis])) {
112014
- throw new MineRenderError(`Litematica region ${name2} block entity is outside its dimensions`);
112015
- }
112016
- const index = pos[0] + pos[2] * width + pos[1] * width * length;
112017
- if (blockEntities.has(index)) throw new MineRenderError(`Duplicate Litematica block entity at index ${index}`);
112018
- const value = { ...tile };
112019
- for (const [axis, key] of ["x", "y", "z"].entries()) value[key] = { type: "int", value: pos[axis] + min[axis] };
112020
- blockEntities.set(index, { type: "compound", value });
112021
- }
112022
- const blocks = [];
112023
- const mask = 2 ** bits2 - 1;
112024
- for (let index = 0; index < volume; index++) {
112025
- const word = Math.floor(index * bits2 / 64);
112026
- const shift = index * bits2 % 64;
112027
- const [high, low] = words[word];
112028
- let value = shift < 32 ? low >>> shift : high >>> shift - 32;
112029
- if (shift < 32 && shift + bits2 > 32) value |= high << 32 - shift;
112030
- if (shift + bits2 > 64) value |= words[word + 1][1] << 64 - shift;
112031
- const id = (value & mask) >>> 0;
112032
- const state = palette[id];
112033
- if (!state) throw new MineRenderError(`Litematica region ${name2} palette index ${id} does not exist`);
112034
- const pos = [
112035
- index % width + min[0],
112036
- Math.floor(index / (width * length)) + min[1],
112037
- Math.floor(index / width) % length + min[2]
112038
- ];
112039
- const key = pos.join(",");
112040
- if (["minecraft:air", "minecraft:cave_air", "minecraft:void_air"].includes(state.type)) {
112041
- combined.delete(key);
112042
- continue;
112043
- }
112044
- const block = { type: state.type, properties: { ...state.properties }, position: pos, nbt: blockEntities.get(index) };
112045
- blocks.push(block);
112046
- combined.set(key, block);
112047
- }
112048
- const entities = compounds(tags.Entities, "Entities").map((entity) => {
112049
- const pos = entity.Pos;
112050
- if (pos?.type !== "list" || pos.value.type !== "double" || pos.value.value.length !== 3 || !pos.value.value.every(Number.isFinite)) {
112051
- throw new MineRenderError(`Litematica region ${name2} entity Pos must contain three doubles`);
112052
- }
112053
- const absolute = pos.value.value.map((value2, axis) => value2 + position[axis]);
112054
- const value = { ...entity, Pos: { type: "list", value: { type: "double", value: [...absolute] } } };
112055
- return { position: absolute, nbt: { type: "compound", value } };
112056
- });
112057
- return { name: name2, position, signedSize, size, blocks, entities, dataVersion };
112058
- }
112059
- function minimum(position, size) {
112060
- return position + Math.min(0, size + 1);
112061
- }
112062
- function integer2(tag, name2) {
112063
- if (tag?.type !== "int" || !Number.isInteger(tag.value) || tag.value < -2147483648 || tag.value > 2147483647) {
112064
- throw new MineRenderError(`Litematica ${name2} must be an int`);
112065
- }
112066
- return tag.value;
112067
- }
112068
- function compound(tag, name2) {
112069
- if (tag?.type !== "compound" || !tag.value || typeof tag.value !== "object" || Array.isArray(tag.value)) {
112070
- throw new MineRenderError(`Litematica ${name2} must be a compound`);
112071
- }
112072
- return tag.value;
112073
- }
112074
- function blockPosition(tags) {
112075
- return ["x", "y", "z"].map((axis) => integer2(tags[axis], axis));
112076
- }
112077
- function compounds(tag, name2) {
112078
- if (tag === void 0) return [];
112079
- if (tag.type === "list" && Array.isArray(tag.value.value)) {
112080
- if (tag.value.value.length === 0) return [];
112081
- if (tag.value.type === "compound") return tag.value.value;
112082
- }
112083
- throw new MineRenderError(`Litematica ${name2} must be a compound list`);
112084
- }
112085
- function blockState(tags) {
112086
- const name2 = tags.Name;
112087
- if (name2?.type !== "string" || !/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(name2.value)) {
112088
- throw new MineRenderError("Litematica palette entry must have a block Name");
112089
- }
112090
- const properties = {};
112091
- if (tags.Properties !== void 0) {
112092
- for (const [key, tag] of Object.entries(compound(tags.Properties, "block Properties"))) {
112093
- if (tag?.type !== "string") throw new MineRenderError("Litematica block properties must be strings");
112094
- Object.defineProperty(properties, key, { value: tag.value, enumerable: true, writable: true, configurable: true });
112095
- }
112096
- }
112097
- return { type: name2.value.includes(":") ? name2.value : `minecraft:${name2.value}`, properties };
112098
- }
112099
-
112100
- // src/model/multiblock/MultiBlockStructure.ts
112101
- init_dirname();
112102
- init_buffer2();
112103
- init_process2();
112104
-
112105
- // src/model/multiblock/SchematicParser.ts
112106
- init_dirname();
112107
- init_buffer2();
112108
- init_process2();
112109
- init_MineRenderError();
112110
111953
 
112111
111954
  // src/model/multiblock/legacyBlocks.json
112112
111955
  var legacyBlocks_default = {
@@ -112951,12 +112794,20 @@ property list uchar int vertex_index
112951
112794
  "131:15": "minecraft:tripwire_hook[powered=true,attached=true,facing=east]",
112952
112795
  "132:0": "minecraft:tripwire[disarmed=false,east=false,powered=false,south=false,north=false,west=false,attached=false]",
112953
112796
  "132:1": "minecraft:tripwire[disarmed=false,east=false,powered=true,south=false,north=false,west=false,attached=false]",
112797
+ "132:2": "minecraft:tripwire[disarmed=false,east=false,powered=false,south=false,north=false,west=false,attached=false]",
112798
+ "132:3": "minecraft:tripwire[disarmed=false,east=false,powered=true,south=false,north=false,west=false,attached=false]",
112954
112799
  "132:4": "minecraft:tripwire[disarmed=false,east=false,powered=false,south=false,north=false,west=false,attached=true]",
112955
112800
  "132:5": "minecraft:tripwire[disarmed=false,east=false,powered=true,south=false,north=false,west=false,attached=true]",
112801
+ "132:6": "minecraft:tripwire[disarmed=false,east=false,powered=false,south=false,north=false,west=false,attached=true]",
112802
+ "132:7": "minecraft:tripwire[disarmed=false,east=false,powered=true,south=false,north=false,west=false,attached=true]",
112956
112803
  "132:8": "minecraft:tripwire[disarmed=true,east=false,powered=false,south=false,north=false,west=false,attached=false]",
112957
112804
  "132:9": "minecraft:tripwire[disarmed=true,east=false,powered=true,south=false,north=false,west=false,attached=false]",
112805
+ "132:10": "minecraft:tripwire[disarmed=true,east=false,powered=false,south=false,north=false,west=false,attached=false]",
112806
+ "132:11": "minecraft:tripwire[disarmed=true,east=false,powered=true,south=false,north=false,west=false,attached=false]",
112958
112807
  "132:12": "minecraft:tripwire[disarmed=true,east=false,powered=false,south=false,north=false,west=false,attached=true]",
112959
112808
  "132:13": "minecraft:tripwire[disarmed=true,east=false,powered=true,south=false,north=false,west=false,attached=true]",
112809
+ "132:14": "minecraft:tripwire[disarmed=true,east=false,powered=false,south=false,north=false,west=false,attached=true]",
112810
+ "132:15": "minecraft:tripwire[disarmed=true,east=false,powered=true,south=false,north=false,west=false,attached=true]",
112960
112811
  "133:0": "minecraft:emerald_block",
112961
112812
  "134:0": "minecraft:spruce_stairs[half=bottom,shape=outer_right,facing=east]",
112962
112813
  "134:1": "minecraft:spruce_stairs[half=bottom,shape=outer_right,facing=west]",
@@ -113797,7 +113648,186 @@ property list uchar int vertex_index
113797
113648
  }
113798
113649
  };
113799
113650
 
113651
+ // src/model/multiblock/LegacyBlocks.ts
113652
+ function resolveLegacyBlock(id, metadata, customMappings = {}, lenient = false) {
113653
+ const mapping = legacyBlocks_default.blocks;
113654
+ const key = `${id}:${metadata}`;
113655
+ let mapped = customMappings[key] ?? mapping[key];
113656
+ if (!mapped && lenient) mapped = customMappings[`${id}:0`] ?? mapping[`${id}:0`];
113657
+ if (!mapped) return void 0;
113658
+ const [type, state] = mapped.split("[");
113659
+ const properties = {};
113660
+ if (state) {
113661
+ for (const property of state.slice(0, -1).split(",")) {
113662
+ const [key2, value] = property.split("=");
113663
+ properties[key2] = value;
113664
+ }
113665
+ }
113666
+ return { type, properties };
113667
+ }
113668
+
113669
+ // src/model/multiblock/LitematicaParser.ts
113670
+ init_dirname();
113671
+ init_buffer2();
113672
+ init_process2();
113673
+ init_MineRenderError();
113674
+ var LitematicaParser = class {
113675
+ /**
113676
+ * Parses decoded NBT, preserving subregion placement and omitting air.
113677
+ * Later entries in regions replace earlier cells, including with air.
113678
+ * Size encloses all region bounds; positions retain the schematic origin.
113679
+ * Metadata and scheduled ticks are not imported. Named regions can be placed individually.
113680
+ */
113681
+ static async parse(nbt) {
113682
+ const tags = nbt.value;
113683
+ const version3 = integer2(tags.Version, "Version");
113684
+ if (version3 < 5 || version3 > 7) throw new MineRenderError(`Unsupported Litematica version ${version3}; expected 5, 6, or 7`);
113685
+ const dataVersion = tags.MinecraftDataVersion === void 0 ? void 0 : integer2(tags.MinecraftDataVersion, "MinecraftDataVersion");
113686
+ const blocks = /* @__PURE__ */ new Map();
113687
+ const regions = [];
113688
+ const min = [Infinity, Infinity, Infinity];
113689
+ const max2 = [-Infinity, -Infinity, -Infinity];
113690
+ for (const [name2, tag] of Object.entries(compound(tags.Regions, "Regions"))) {
113691
+ const region = readRegion(compound(tag, `region ${name2}`), name2, dataVersion, blocks);
113692
+ regions.push(region);
113693
+ for (let axis = 0; axis < 3; axis++) {
113694
+ const start = minimum(region.position[axis], region.signedSize[axis]);
113695
+ min[axis] = Math.min(min[axis], start);
113696
+ max2[axis] = Math.max(max2[axis], start + region.size[axis]);
113697
+ }
113698
+ }
113699
+ return {
113700
+ size: regions.length ? min.map((value, axis) => max2[axis] - value) : [0, 0, 0],
113701
+ blocks: [...blocks.values()],
113702
+ entities: regions.flatMap((region) => region.entities ?? []),
113703
+ dataVersion,
113704
+ regions
113705
+ };
113706
+ }
113707
+ };
113708
+ function readRegion(tags, name2, dataVersion, combined) {
113709
+ const position = blockPosition(compound(tags.Position, `region ${name2} Position`));
113710
+ const signedSize = blockPosition(compound(tags.Size, `region ${name2} Size`));
113711
+ if (signedSize.some((value) => value === 0)) throw new MineRenderError(`Litematica region ${name2} dimensions must be nonzero`);
113712
+ const size = signedSize.map(Math.abs);
113713
+ const min = position.map((value, axis) => minimum(value, signedSize[axis]));
113714
+ const [width, height, length] = size;
113715
+ const volume = width * height * length;
113716
+ const palette = compounds(tags.BlockStatePalette, "BlockStatePalette").map(blockState);
113717
+ if (!palette.length) throw new MineRenderError(`Litematica region ${name2} has no block palette`);
113718
+ const bits2 = Math.max(2, Math.ceil(Math.log2(palette.length)));
113719
+ if (!Number.isSafeInteger(volume * bits2)) throw new MineRenderError(`Litematica region ${name2} volume is too large`);
113720
+ const packed = tags.BlockStates;
113721
+ const expected = Math.ceil(volume * bits2 / 64);
113722
+ if (packed?.type !== "longArray" || !Array.isArray(packed.value) || packed.value.length !== expected) {
113723
+ throw new MineRenderError(`Litematica region ${name2} BlockStates must contain ${expected} longs`);
113724
+ }
113725
+ const words = packed.value.map((pair) => {
113726
+ if (!Array.isArray(pair) || pair.length !== 2 || !pair.every((value) => Number.isInteger(value) && value >= -2147483648 && value <= 4294967295)) {
113727
+ throw new MineRenderError(`Litematica region ${name2} has an invalid BlockStates long`);
113728
+ }
113729
+ return [pair[0] >>> 0, pair[1] >>> 0];
113730
+ });
113731
+ const blockEntities = /* @__PURE__ */ new Map();
113732
+ for (const tile of compounds(tags.TileEntities, "TileEntities")) {
113733
+ const pos = blockPosition(tile);
113734
+ if (pos.some((value2, axis) => value2 < 0 || value2 >= size[axis])) {
113735
+ throw new MineRenderError(`Litematica region ${name2} block entity is outside its dimensions`);
113736
+ }
113737
+ const index = pos[0] + pos[2] * width + pos[1] * width * length;
113738
+ if (blockEntities.has(index)) throw new MineRenderError(`Duplicate Litematica block entity at index ${index}`);
113739
+ const value = { ...tile };
113740
+ for (const [axis, key] of ["x", "y", "z"].entries()) value[key] = { type: "int", value: pos[axis] + min[axis] };
113741
+ blockEntities.set(index, { type: "compound", value });
113742
+ }
113743
+ const blocks = [];
113744
+ const mask = 2 ** bits2 - 1;
113745
+ for (let index = 0; index < volume; index++) {
113746
+ const word = Math.floor(index * bits2 / 64);
113747
+ const shift = index * bits2 % 64;
113748
+ const [high, low] = words[word];
113749
+ let value = shift < 32 ? low >>> shift : high >>> shift - 32;
113750
+ if (shift < 32 && shift + bits2 > 32) value |= high << 32 - shift;
113751
+ if (shift + bits2 > 64) value |= words[word + 1][1] << 64 - shift;
113752
+ const id = (value & mask) >>> 0;
113753
+ const state = palette[id];
113754
+ if (!state) throw new MineRenderError(`Litematica region ${name2} palette index ${id} does not exist`);
113755
+ const pos = [
113756
+ index % width + min[0],
113757
+ Math.floor(index / (width * length)) + min[1],
113758
+ Math.floor(index / width) % length + min[2]
113759
+ ];
113760
+ const key = pos.join(",");
113761
+ if (["minecraft:air", "minecraft:cave_air", "minecraft:void_air"].includes(state.type)) {
113762
+ combined.delete(key);
113763
+ continue;
113764
+ }
113765
+ const block = { type: state.type, properties: { ...state.properties }, position: pos, nbt: blockEntities.get(index) };
113766
+ blocks.push(block);
113767
+ combined.set(key, block);
113768
+ }
113769
+ const entities = compounds(tags.Entities, "Entities").map((entity) => {
113770
+ const pos = entity.Pos;
113771
+ if (pos?.type !== "list" || pos.value.type !== "double" || pos.value.value.length !== 3 || !pos.value.value.every(Number.isFinite)) {
113772
+ throw new MineRenderError(`Litematica region ${name2} entity Pos must contain three doubles`);
113773
+ }
113774
+ const absolute = pos.value.value.map((value2, axis) => value2 + position[axis]);
113775
+ const value = { ...entity, Pos: { type: "list", value: { type: "double", value: [...absolute] } } };
113776
+ return { position: absolute, nbt: { type: "compound", value } };
113777
+ });
113778
+ return { name: name2, position, signedSize, size, blocks, entities, dataVersion };
113779
+ }
113780
+ function minimum(position, size) {
113781
+ return position + Math.min(0, size + 1);
113782
+ }
113783
+ function integer2(tag, name2) {
113784
+ if (tag?.type !== "int" || !Number.isInteger(tag.value) || tag.value < -2147483648 || tag.value > 2147483647) {
113785
+ throw new MineRenderError(`Litematica ${name2} must be an int`);
113786
+ }
113787
+ return tag.value;
113788
+ }
113789
+ function compound(tag, name2) {
113790
+ if (tag?.type !== "compound" || !tag.value || typeof tag.value !== "object" || Array.isArray(tag.value)) {
113791
+ throw new MineRenderError(`Litematica ${name2} must be a compound`);
113792
+ }
113793
+ return tag.value;
113794
+ }
113795
+ function blockPosition(tags) {
113796
+ return ["x", "y", "z"].map((axis) => integer2(tags[axis], axis));
113797
+ }
113798
+ function compounds(tag, name2) {
113799
+ if (tag === void 0) return [];
113800
+ if (tag.type === "list" && Array.isArray(tag.value.value)) {
113801
+ if (tag.value.value.length === 0) return [];
113802
+ if (tag.value.type === "compound") return tag.value.value;
113803
+ }
113804
+ throw new MineRenderError(`Litematica ${name2} must be a compound list`);
113805
+ }
113806
+ function blockState(tags) {
113807
+ const name2 = tags.Name;
113808
+ if (name2?.type !== "string" || !/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(name2.value)) {
113809
+ throw new MineRenderError("Litematica palette entry must have a block Name");
113810
+ }
113811
+ const properties = {};
113812
+ if (tags.Properties !== void 0) {
113813
+ for (const [key, tag] of Object.entries(compound(tags.Properties, "block Properties"))) {
113814
+ if (tag?.type !== "string") throw new MineRenderError("Litematica block properties must be strings");
113815
+ Object.defineProperty(properties, key, { value: tag.value, enumerable: true, writable: true, configurable: true });
113816
+ }
113817
+ }
113818
+ return { type: name2.value.includes(":") ? name2.value : `minecraft:${name2.value}`, properties };
113819
+ }
113820
+
113821
+ // src/model/multiblock/MultiBlockStructure.ts
113822
+ init_dirname();
113823
+ init_buffer2();
113824
+ init_process2();
113825
+
113800
113826
  // src/model/multiblock/SchematicParser.ts
113827
+ init_dirname();
113828
+ init_buffer2();
113829
+ init_process2();
113830
+ init_MineRenderError();
113801
113831
  var SchematicParser = class {
113802
113832
  /**
113803
113833
  * Parses a legacy schematic using custom mappings before the bundled defaults.
@@ -113839,35 +113869,21 @@ property list uchar int vertex_index
113839
113869
  tileEntities.set((y5 * length + z4) * width + x4, { type: "compound", value: tile });
113840
113870
  }
113841
113871
  const blocks = [];
113842
- const mapping = legacyBlocks_default.blocks;
113843
113872
  for (let index = 0; index < volume; index++) {
113844
113873
  const packed = addBlocks?.value[index >> 1] ?? 0;
113845
113874
  const high = packed >> (index & 1) * 4 & 15;
113846
113875
  const id = high << 8 | ids.value[index] & 255;
113847
113876
  const metadata = data.value[index] & 255;
113848
- const key = `${id}:${metadata}`;
113849
- let mapped = customMappings[key] ?? mapping[key];
113850
- if (!mapped && options.lenient) {
113851
- mapped = customMappings[`${id}:0`] ?? mapping[`${id}:0`];
113852
- }
113853
- if (!mapped) {
113877
+ const block = resolveLegacyBlock(id, metadata, customMappings, options.lenient);
113878
+ if (!block) {
113854
113879
  if (options.lenient) {
113855
113880
  continue;
113856
113881
  }
113857
113882
  throw new MineRenderError(`Unsupported legacy block ${id}:${metadata} at schematic index ${index}`);
113858
113883
  }
113859
- const [type, state] = mapped.split("[");
113860
- if (type === "minecraft:air") continue;
113861
- const properties = {};
113862
- if (state) {
113863
- for (const property of state.slice(0, -1).split(",")) {
113864
- const [key2, value] = property.split("=");
113865
- properties[key2] = value;
113866
- }
113867
- }
113884
+ if (block.type === "minecraft:air") continue;
113868
113885
  blocks.push({
113869
- type,
113870
- properties,
113886
+ ...block,
113871
113887
  position: [index % width, Math.floor(index / (width * length)), Math.floor(index / width) % length],
113872
113888
  nbt: tileEntities.get(index)
113873
113889
  });
@@ -119319,10 +119335,13 @@ gl_FragDepth=readDepth(vUv);
119319
119335
  return this.locations(this.bytes(data)).map(({ x: x4, z: z4 }) => ({ x: x4, z: z4 }));
119320
119336
  }
119321
119337
  /** Decodes all stored chunks. Gzip, zlib, LZ4, and uncompressed payloads are supported. */
119322
- static async parse(data) {
119338
+ static async parse(data, options = {}) {
119339
+ options.signal?.throwIfAborted();
119323
119340
  const bytes = this.bytes(data);
119324
119341
  const chunks = [];
119325
- for (const location of this.locations(bytes)) chunks.push(await this.readChunk(bytes, location));
119342
+ for (const location of this.locations(bytes)) {
119343
+ chunks.push(await this.abortable(this.readChunk(bytes, location, options), options.signal));
119344
+ }
119326
119345
  return { chunks };
119327
119346
  }
119328
119347
  /**
@@ -119331,13 +119350,28 @@ gl_FragDepth=readDepth(vUv);
119331
119350
  * @param localZ - Chunk z within the region, from 0 to 31.
119332
119351
  * @returns The chunk with absolute coordinates, or `undefined` if the region has no chunk there.
119333
119352
  */
119334
- static async parseChunk(data, localX, localZ) {
119353
+ static async parseChunk(data, localX, localZ, options = {}) {
119354
+ options.signal?.throwIfAborted();
119335
119355
  if (![localX, localZ].every((value) => Number.isInteger(value) && value >= 0 && value < 32)) {
119336
119356
  throw new RangeError("Region-local chunk coordinates must be integers from 0 to 31");
119337
119357
  }
119338
119358
  const bytes = this.bytes(data);
119339
119359
  const location = this.locations(bytes).find(({ x: x4, z: z4 }) => x4 === localX && z4 === localZ);
119340
- return location ? this.readChunk(bytes, location) : void 0;
119360
+ return location ? this.abortable(this.readChunk(bytes, location, options), options.signal) : void 0;
119361
+ }
119362
+ static async abortable(promise, signal) {
119363
+ if (!signal) return promise;
119364
+ let abort2;
119365
+ const cancelled = new Promise((_4, reject) => {
119366
+ abort2 = () => reject(signal.reason);
119367
+ signal.addEventListener("abort", abort2, { once: true });
119368
+ if (signal.aborted) abort2();
119369
+ });
119370
+ try {
119371
+ return await Promise.race([promise, cancelled]);
119372
+ } finally {
119373
+ signal.removeEventListener("abort", abort2);
119374
+ }
119341
119375
  }
119342
119376
  static bytes(data) {
119343
119377
  return data instanceof Uint8Array ? data : new Uint8Array(data);
@@ -119363,20 +119397,41 @@ gl_FragDepth=readDepth(vUv);
119363
119397
  }
119364
119398
  return locations;
119365
119399
  }
119366
- static async readChunk(data, location) {
119367
- const compression = data[location.offset + 4];
119368
- if (compression & 128) throw new MineRenderError("External Anvil .mcc chunks are not supported");
119400
+ static async readChunk(data, location, options) {
119401
+ options.signal?.throwIfAborted();
119402
+ const format3 = data[location.offset + 4];
119403
+ const external = (format3 & 128) !== 0;
119404
+ const compression = format3 & 127;
119369
119405
  if (compression !== 1 && compression !== 2 && compression !== 3 && compression !== 4) {
119370
119406
  throw new MineRenderError(`Unsupported Anvil compression ${compression}`);
119371
119407
  }
119372
119408
  let payload = data.subarray(location.offset + 5, location.offset + 4 + location.length);
119409
+ let expected;
119410
+ if (external) {
119411
+ if (!options.region || !options.readExternalChunk) {
119412
+ throw new MineRenderError("External Anvil .mcc chunks require region coordinates and readExternalChunk");
119413
+ }
119414
+ if (![options.region.x, options.region.z].every(Number.isSafeInteger)) {
119415
+ throw new RangeError("Anvil region coordinates must be safe integers");
119416
+ }
119417
+ expected = { x: options.region.x * 32 + location.x, z: options.region.z * 32 + location.z };
119418
+ if (![expected.x, expected.z].every(Number.isSafeInteger)) {
119419
+ throw new RangeError("External Anvil chunk coordinates must be safe integers");
119420
+ }
119421
+ const externalData = await options.readExternalChunk(expected.x, expected.z, options.signal);
119422
+ options.signal?.throwIfAborted();
119423
+ if (externalData === void 0) throw new MineRenderError(`External Anvil chunk c.${expected.x}.${expected.z}.mcc is missing`);
119424
+ payload = this.bytes(externalData);
119425
+ }
119373
119426
  if (compression === 4) {
119374
119427
  payload = decodeAnvilLz4(payload);
119375
119428
  } else if (compression !== 3) {
119376
119429
  const stream = new Blob([payload]).stream().pipeThrough(new DecompressionStream(compression === 1 ? "gzip" : "deflate"));
119377
119430
  payload = new Uint8Array(await new Response(stream).arrayBuffer());
119378
119431
  }
119432
+ options.signal?.throwIfAborted();
119379
119433
  const nbt = await NBTHelper.fromBuffer(payload, "big");
119434
+ options.signal?.throwIfAborted();
119380
119435
  const root = nbt.value;
119381
119436
  const level = root.Level?.type === "compound" ? root.Level.value : root;
119382
119437
  const dataVersion = root.DataVersion?.type === "int" ? root.DataVersion.value : void 0;
@@ -119389,28 +119444,37 @@ gl_FragDepth=readDepth(vUv);
119389
119444
  if ((chunk.x % 32 + 32) % 32 !== location.x || (chunk.z % 32 + 32) % 32 !== location.z) {
119390
119445
  throw new MineRenderError("Anvil chunk coordinates do not match its region location");
119391
119446
  }
119447
+ if (expected && (chunk.x !== expected.x || chunk.z !== expected.z)) {
119448
+ throw new MineRenderError(`Anvil chunk coordinates do not match external file c.${expected.x}.${expected.z}.mcc`);
119449
+ }
119450
+ const numericStates = /* @__PURE__ */ new Map();
119392
119451
  for (const section of this.compounds(level.sections ?? level.Sections, "sections")) {
119393
119452
  const y5 = this.integer(section.Y, "section Y");
119453
+ let data2;
119394
119454
  if (section.Blocks || section.Data || section.Add) {
119395
- throw new MineRenderError("Numeric Anvil block IDs before Minecraft 1.13 are not supported");
119396
- }
119397
- if (section.block_states && section.block_states.type !== "compound") {
119398
- throw new MineRenderError("Anvil block_states must be a compound");
119399
- }
119400
- const modern = section.block_states?.type === "compound" ? section.block_states.value : void 0;
119401
- const paletteTag = modern ? modern.palette : section.Palette;
119402
- const palette = paletteTag?.type === "list" && paletteTag.value.type === "string" ? paletteTag.value.value : this.compounds(paletteTag, "block palette");
119403
- const data2 = new ChunkData();
119404
- if (palette.length) {
119405
- const states = palette.map((entry) => this.block(entry));
119406
- const indices = this.unpack(modern ? modern.data : section.BlockStates, states.length, !!modern, dataVersion);
119407
- for (let index = 0; index < 4096; index++) {
119408
- const state = states[indices[index]];
119409
- if (!state) throw new MineRenderError(`Anvil palette index ${indices[index]} is out of range`);
119410
- if (!ChunkData.isAir(state)) data2.set(index, state);
119455
+ if (section.Palette || section.BlockStates || section.block_states) {
119456
+ throw new MineRenderError(`Anvil section ${y5} mixes numeric and paletted block states`);
119457
+ }
119458
+ data2 = this.numericSection(section, y5, options, numericStates);
119459
+ } else {
119460
+ if (section.block_states && section.block_states.type !== "compound") {
119461
+ throw new MineRenderError("Anvil block_states must be a compound");
119462
+ }
119463
+ const modern = section.block_states?.type === "compound" ? section.block_states.value : void 0;
119464
+ const paletteTag = modern ? modern.palette : section.Palette;
119465
+ const palette = paletteTag?.type === "list" && paletteTag.value.type === "string" ? paletteTag.value.value : this.compounds(paletteTag, "block palette");
119466
+ data2 = new ChunkData();
119467
+ if (palette.length) {
119468
+ const states = palette.map((entry) => this.block(entry));
119469
+ const indices = this.unpack(modern ? modern.data : section.BlockStates, states.length, !!modern, dataVersion);
119470
+ for (let index = 0; index < 4096; index++) {
119471
+ const state = states[indices[index]];
119472
+ if (!state) throw new MineRenderError(`Anvil palette index ${indices[index]} is out of range`);
119473
+ if (!ChunkData.isAir(state)) data2.set(index, state);
119474
+ }
119475
+ } else if (modern || section.Palette || section.BlockStates) {
119476
+ throw new MineRenderError("Anvil section has block states without a palette");
119411
119477
  }
119412
- } else if (modern || section.Palette || section.BlockStates) {
119413
- throw new MineRenderError("Anvil section has block states without a palette");
119414
119478
  }
119415
119479
  if (chunk.sections.some((section2) => section2.y === y5)) throw new MineRenderError(`Duplicate Anvil section Y ${y5}`);
119416
119480
  chunk.sections.push({ y: y5, data: data2 });
@@ -119438,6 +119502,33 @@ gl_FragDepth=readDepth(vUv);
119438
119502
  }
119439
119503
  return chunk;
119440
119504
  }
119505
+ static numericSection(section, y5, options, states) {
119506
+ const bytes = (name2, length, optional = false) => {
119507
+ const tag = section[name2];
119508
+ if (!tag && optional) return void 0;
119509
+ if (tag?.type !== "byteArray" || !Array.isArray(tag.value) || tag.value.length !== length) {
119510
+ throw new MineRenderError(`Anvil section ${y5} ${name2} must contain ${length} bytes`);
119511
+ }
119512
+ return tag.value;
119513
+ };
119514
+ const ids = bytes("Blocks", 4096);
119515
+ const metadata = bytes("Data", 2048, true);
119516
+ const extra = bytes("Add", 2048, true);
119517
+ const data = new ChunkData();
119518
+ for (let index = 0; index < 4096; index++) {
119519
+ const shift = (index & 1) * 4;
119520
+ const id = ids[index] & 255 | ((extra?.[index >> 1] ?? 0) >> shift & 15) << 8;
119521
+ const value = (metadata?.[index >> 1] ?? 0) >> shift & 15;
119522
+ const key = id * 16 + value;
119523
+ if (!states.has(key)) states.set(key, resolveLegacyBlock(id, value, options.legacyMappings, options.lenient));
119524
+ const block = states.get(key);
119525
+ if (!block && !options.lenient) {
119526
+ throw new MineRenderError(`Unsupported legacy block ${id}:${value} at Anvil section ${y5} index ${index}`);
119527
+ }
119528
+ if (block && !ChunkData.isAir(block)) data.set(index, block);
119529
+ }
119530
+ return data;
119531
+ }
119441
119532
  static compounds(tag, name2) {
119442
119533
  if (!tag) return [];
119443
119534
  if (tag.type !== "list" || tag.value.type !== "compound" && tag.value.value.length !== 0) {
@@ -119523,6 +119614,7 @@ gl_FragDepth=readDepth(vUv);
119523
119614
  this.cachedBytes = 0;
119524
119615
  this.maxCachedRegions = options.maxCachedRegions ?? 4;
119525
119616
  this.maxCachedBytes = options.maxCachedBytes ?? 64 * 1024 * 1024;
119617
+ this.parseOptions = { readExternalChunk: options.readExternalChunk, legacyMappings: options.legacyMappings, lenient: options.lenient };
119526
119618
  for (const [name2, value] of Object.entries({ maxCachedRegions: this.maxCachedRegions, maxCachedBytes: this.maxCachedBytes })) {
119527
119619
  if (!Number.isSafeInteger(value) || value < 0) throw new RangeError(`${name2} must be a nonnegative safe integer`);
119528
119620
  }
@@ -119536,7 +119628,11 @@ gl_FragDepth=readDepth(vUv);
119536
119628
  const data = await this.getRegion(regionX, regionZ, signal);
119537
119629
  signal?.throwIfAborted();
119538
119630
  if (!data) return void 0;
119539
- const chunk = await abortable2(AnvilParser.parseChunk(data, x4 - regionX * 32, z4 - regionZ * 32), signal);
119631
+ const chunk = await AnvilParser.parseChunk(data, x4 - regionX * 32, z4 - regionZ * 32, {
119632
+ ...this.parseOptions,
119633
+ region: { x: regionX, z: regionZ },
119634
+ signal
119635
+ });
119540
119636
  signal?.throwIfAborted();
119541
119637
  if (chunk && (chunk.x !== x4 || chunk.z !== z4)) {
119542
119638
  throw new MineRenderError(`Anvil chunk coordinates ${chunk.x},${chunk.z} do not match requested column ${x4},${z4}`);
@@ -119567,7 +119663,7 @@ gl_FragDepth=readDepth(vUv);
119567
119663
  settled: false,
119568
119664
  promise: Promise.resolve().then(() => {
119569
119665
  controller.signal.throwIfAborted();
119570
- return this.readRegion(x4, z4, controller.signal);
119666
+ return abortable2(this.readRegion(x4, z4, controller.signal), controller.signal);
119571
119667
  }).then((data) => {
119572
119668
  controller.signal.throwIfAborted();
119573
119669
  let bytes = data instanceof Uint8Array ? data : data === void 0 ? void 0 : new Uint8Array(data);