minerender 2.1.0-alpha.92 → 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.
@@ -18872,6 +18872,7 @@ __export(index_browser_exports, {
18872
18872
  prefix: () => prefix,
18873
18873
  probeImageSize: () => probeImageSize,
18874
18874
  registerBrowserEnv: () => registerBrowserEnv,
18875
+ resolveLegacyBlock: () => resolveLegacyBlock,
18875
18876
  sampleEntityAnimation: () => sampleEntityAnimation,
18876
18877
  sampleEntityKeyframes: () => sampleEntityKeyframes,
18877
18878
  sampleFluid: () => sampleFluid,
@@ -23900,158 +23901,6 @@ function isBlockObject(obj) {
23900
23901
  init_FluidGeometry();
23901
23902
  init_FluidModelObject();
23902
23903
 
23903
- // src/model/multiblock/LitematicaParser.ts
23904
- init_MineRenderError();
23905
- var LitematicaParser = class {
23906
- /**
23907
- * Parses decoded NBT, preserving subregion placement and omitting air.
23908
- * Later entries in regions replace earlier cells, including with air.
23909
- * Size encloses all region bounds; positions retain the schematic origin.
23910
- * Metadata and scheduled ticks are not imported. Named regions can be placed individually.
23911
- */
23912
- static async parse(nbt) {
23913
- const tags = nbt.value;
23914
- const version2 = integer2(tags.Version, "Version");
23915
- if (version2 < 5 || version2 > 7) throw new MineRenderError(`Unsupported Litematica version ${version2}; expected 5, 6, or 7`);
23916
- const dataVersion = tags.MinecraftDataVersion === void 0 ? void 0 : integer2(tags.MinecraftDataVersion, "MinecraftDataVersion");
23917
- const blocks = /* @__PURE__ */ new Map();
23918
- const regions = [];
23919
- const min = [Infinity, Infinity, Infinity];
23920
- const max = [-Infinity, -Infinity, -Infinity];
23921
- for (const [name, tag] of Object.entries(compound(tags.Regions, "Regions"))) {
23922
- const region = readRegion(compound(tag, `region ${name}`), name, dataVersion, blocks);
23923
- regions.push(region);
23924
- for (let axis = 0; axis < 3; axis++) {
23925
- const start = minimum(region.position[axis], region.signedSize[axis]);
23926
- min[axis] = Math.min(min[axis], start);
23927
- max[axis] = Math.max(max[axis], start + region.size[axis]);
23928
- }
23929
- }
23930
- return {
23931
- size: regions.length ? min.map((value, axis) => max[axis] - value) : [0, 0, 0],
23932
- blocks: [...blocks.values()],
23933
- entities: regions.flatMap((region) => region.entities ?? []),
23934
- dataVersion,
23935
- regions
23936
- };
23937
- }
23938
- };
23939
- function readRegion(tags, name, dataVersion, combined) {
23940
- const position = blockPosition(compound(tags.Position, `region ${name} Position`));
23941
- const signedSize = blockPosition(compound(tags.Size, `region ${name} Size`));
23942
- if (signedSize.some((value) => value === 0)) throw new MineRenderError(`Litematica region ${name} dimensions must be nonzero`);
23943
- const size = signedSize.map(Math.abs);
23944
- const min = position.map((value, axis) => minimum(value, signedSize[axis]));
23945
- const [width, height, length] = size;
23946
- const volume = width * height * length;
23947
- const palette = compounds(tags.BlockStatePalette, "BlockStatePalette").map(blockState);
23948
- if (!palette.length) throw new MineRenderError(`Litematica region ${name} has no block palette`);
23949
- const bits = Math.max(2, Math.ceil(Math.log2(palette.length)));
23950
- if (!Number.isSafeInteger(volume * bits)) throw new MineRenderError(`Litematica region ${name} volume is too large`);
23951
- const packed = tags.BlockStates;
23952
- const expected = Math.ceil(volume * bits / 64);
23953
- if (packed?.type !== "longArray" || !Array.isArray(packed.value) || packed.value.length !== expected) {
23954
- throw new MineRenderError(`Litematica region ${name} BlockStates must contain ${expected} longs`);
23955
- }
23956
- const words = packed.value.map((pair) => {
23957
- if (!Array.isArray(pair) || pair.length !== 2 || !pair.every((value) => Number.isInteger(value) && value >= -2147483648 && value <= 4294967295)) {
23958
- throw new MineRenderError(`Litematica region ${name} has an invalid BlockStates long`);
23959
- }
23960
- return [pair[0] >>> 0, pair[1] >>> 0];
23961
- });
23962
- const blockEntities = /* @__PURE__ */ new Map();
23963
- for (const tile of compounds(tags.TileEntities, "TileEntities")) {
23964
- const pos = blockPosition(tile);
23965
- if (pos.some((value2, axis) => value2 < 0 || value2 >= size[axis])) {
23966
- throw new MineRenderError(`Litematica region ${name} block entity is outside its dimensions`);
23967
- }
23968
- const index = pos[0] + pos[2] * width + pos[1] * width * length;
23969
- if (blockEntities.has(index)) throw new MineRenderError(`Duplicate Litematica block entity at index ${index}`);
23970
- const value = { ...tile };
23971
- for (const [axis, key] of ["x", "y", "z"].entries()) value[key] = { type: "int", value: pos[axis] + min[axis] };
23972
- blockEntities.set(index, { type: "compound", value });
23973
- }
23974
- const blocks = [];
23975
- const mask = 2 ** bits - 1;
23976
- for (let index = 0; index < volume; index++) {
23977
- const word = Math.floor(index * bits / 64);
23978
- const shift = index * bits % 64;
23979
- const [high, low] = words[word];
23980
- let value = shift < 32 ? low >>> shift : high >>> shift - 32;
23981
- if (shift < 32 && shift + bits > 32) value |= high << 32 - shift;
23982
- if (shift + bits > 64) value |= words[word + 1][1] << 64 - shift;
23983
- const id = (value & mask) >>> 0;
23984
- const state = palette[id];
23985
- if (!state) throw new MineRenderError(`Litematica region ${name} palette index ${id} does not exist`);
23986
- const pos = [
23987
- index % width + min[0],
23988
- Math.floor(index / (width * length)) + min[1],
23989
- Math.floor(index / width) % length + min[2]
23990
- ];
23991
- const key = pos.join(",");
23992
- if (["minecraft:air", "minecraft:cave_air", "minecraft:void_air"].includes(state.type)) {
23993
- combined.delete(key);
23994
- continue;
23995
- }
23996
- const block = { type: state.type, properties: { ...state.properties }, position: pos, nbt: blockEntities.get(index) };
23997
- blocks.push(block);
23998
- combined.set(key, block);
23999
- }
24000
- const entities = compounds(tags.Entities, "Entities").map((entity) => {
24001
- const pos = entity.Pos;
24002
- if (pos?.type !== "list" || pos.value.type !== "double" || pos.value.value.length !== 3 || !pos.value.value.every(Number.isFinite)) {
24003
- throw new MineRenderError(`Litematica region ${name} entity Pos must contain three doubles`);
24004
- }
24005
- const absolute = pos.value.value.map((value2, axis) => value2 + position[axis]);
24006
- const value = { ...entity, Pos: { type: "list", value: { type: "double", value: [...absolute] } } };
24007
- return { position: absolute, nbt: { type: "compound", value } };
24008
- });
24009
- return { name, position, signedSize, size, blocks, entities, dataVersion };
24010
- }
24011
- function minimum(position, size) {
24012
- return position + Math.min(0, size + 1);
24013
- }
24014
- function integer2(tag, name) {
24015
- if (tag?.type !== "int" || !Number.isInteger(tag.value) || tag.value < -2147483648 || tag.value > 2147483647) {
24016
- throw new MineRenderError(`Litematica ${name} must be an int`);
24017
- }
24018
- return tag.value;
24019
- }
24020
- function compound(tag, name) {
24021
- if (tag?.type !== "compound" || !tag.value || typeof tag.value !== "object" || Array.isArray(tag.value)) {
24022
- throw new MineRenderError(`Litematica ${name} must be a compound`);
24023
- }
24024
- return tag.value;
24025
- }
24026
- function blockPosition(tags) {
24027
- return ["x", "y", "z"].map((axis) => integer2(tags[axis], axis));
24028
- }
24029
- function compounds(tag, name) {
24030
- if (tag === void 0) return [];
24031
- if (tag.type === "list" && Array.isArray(tag.value.value)) {
24032
- if (tag.value.value.length === 0) return [];
24033
- if (tag.value.type === "compound") return tag.value.value;
24034
- }
24035
- throw new MineRenderError(`Litematica ${name} must be a compound list`);
24036
- }
24037
- function blockState(tags) {
24038
- const name = tags.Name;
24039
- if (name?.type !== "string" || !/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(name.value)) {
24040
- throw new MineRenderError("Litematica palette entry must have a block Name");
24041
- }
24042
- const properties = {};
24043
- if (tags.Properties !== void 0) {
24044
- for (const [key, tag] of Object.entries(compound(tags.Properties, "block Properties"))) {
24045
- if (tag?.type !== "string") throw new MineRenderError("Litematica block properties must be strings");
24046
- Object.defineProperty(properties, key, { value: tag.value, enumerable: true, writable: true, configurable: true });
24047
- }
24048
- }
24049
- return { type: name.value.includes(":") ? name.value : `minecraft:${name.value}`, properties };
24050
- }
24051
-
24052
- // src/model/multiblock/SchematicParser.ts
24053
- init_MineRenderError();
24054
-
24055
23904
  // src/model/multiblock/legacyBlocks.json
24056
23905
  var legacyBlocks_default = {
24057
23906
  __comment: "Block mappings from res/idsToNames.json (WorldEdit legacy.json); block names and cauldrons updated for Minecraft 1.21.11. Source: https://github.com/sk89q/WorldEdit/blob/master/worldedit-core/src/main/resources/com/sk89q/worldedit/world/registry/legacy.json",
@@ -24895,12 +24744,20 @@ var legacyBlocks_default = {
24895
24744
  "131:15": "minecraft:tripwire_hook[powered=true,attached=true,facing=east]",
24896
24745
  "132:0": "minecraft:tripwire[disarmed=false,east=false,powered=false,south=false,north=false,west=false,attached=false]",
24897
24746
  "132:1": "minecraft:tripwire[disarmed=false,east=false,powered=true,south=false,north=false,west=false,attached=false]",
24747
+ "132:2": "minecraft:tripwire[disarmed=false,east=false,powered=false,south=false,north=false,west=false,attached=false]",
24748
+ "132:3": "minecraft:tripwire[disarmed=false,east=false,powered=true,south=false,north=false,west=false,attached=false]",
24898
24749
  "132:4": "minecraft:tripwire[disarmed=false,east=false,powered=false,south=false,north=false,west=false,attached=true]",
24899
24750
  "132:5": "minecraft:tripwire[disarmed=false,east=false,powered=true,south=false,north=false,west=false,attached=true]",
24751
+ "132:6": "minecraft:tripwire[disarmed=false,east=false,powered=false,south=false,north=false,west=false,attached=true]",
24752
+ "132:7": "minecraft:tripwire[disarmed=false,east=false,powered=true,south=false,north=false,west=false,attached=true]",
24900
24753
  "132:8": "minecraft:tripwire[disarmed=true,east=false,powered=false,south=false,north=false,west=false,attached=false]",
24901
24754
  "132:9": "minecraft:tripwire[disarmed=true,east=false,powered=true,south=false,north=false,west=false,attached=false]",
24755
+ "132:10": "minecraft:tripwire[disarmed=true,east=false,powered=false,south=false,north=false,west=false,attached=false]",
24756
+ "132:11": "minecraft:tripwire[disarmed=true,east=false,powered=true,south=false,north=false,west=false,attached=false]",
24902
24757
  "132:12": "minecraft:tripwire[disarmed=true,east=false,powered=false,south=false,north=false,west=false,attached=true]",
24903
24758
  "132:13": "minecraft:tripwire[disarmed=true,east=false,powered=true,south=false,north=false,west=false,attached=true]",
24759
+ "132:14": "minecraft:tripwire[disarmed=true,east=false,powered=false,south=false,north=false,west=false,attached=true]",
24760
+ "132:15": "minecraft:tripwire[disarmed=true,east=false,powered=true,south=false,north=false,west=false,attached=true]",
24904
24761
  "133:0": "minecraft:emerald_block",
24905
24762
  "134:0": "minecraft:spruce_stairs[half=bottom,shape=outer_right,facing=east]",
24906
24763
  "134:1": "minecraft:spruce_stairs[half=bottom,shape=outer_right,facing=west]",
@@ -25741,7 +25598,175 @@ var legacyBlocks_default = {
25741
25598
  }
25742
25599
  };
25743
25600
 
25601
+ // src/model/multiblock/LegacyBlocks.ts
25602
+ function resolveLegacyBlock(id, metadata, customMappings = {}, lenient = false) {
25603
+ const mapping = legacyBlocks_default.blocks;
25604
+ const key = `${id}:${metadata}`;
25605
+ let mapped = customMappings[key] ?? mapping[key];
25606
+ if (!mapped && lenient) mapped = customMappings[`${id}:0`] ?? mapping[`${id}:0`];
25607
+ if (!mapped) return void 0;
25608
+ const [type, state] = mapped.split("[");
25609
+ const properties = {};
25610
+ if (state) {
25611
+ for (const property of state.slice(0, -1).split(",")) {
25612
+ const [key2, value] = property.split("=");
25613
+ properties[key2] = value;
25614
+ }
25615
+ }
25616
+ return { type, properties };
25617
+ }
25618
+
25619
+ // src/model/multiblock/LitematicaParser.ts
25620
+ init_MineRenderError();
25621
+ var LitematicaParser = class {
25622
+ /**
25623
+ * Parses decoded NBT, preserving subregion placement and omitting air.
25624
+ * Later entries in regions replace earlier cells, including with air.
25625
+ * Size encloses all region bounds; positions retain the schematic origin.
25626
+ * Metadata and scheduled ticks are not imported. Named regions can be placed individually.
25627
+ */
25628
+ static async parse(nbt) {
25629
+ const tags = nbt.value;
25630
+ const version2 = integer2(tags.Version, "Version");
25631
+ if (version2 < 5 || version2 > 7) throw new MineRenderError(`Unsupported Litematica version ${version2}; expected 5, 6, or 7`);
25632
+ const dataVersion = tags.MinecraftDataVersion === void 0 ? void 0 : integer2(tags.MinecraftDataVersion, "MinecraftDataVersion");
25633
+ const blocks = /* @__PURE__ */ new Map();
25634
+ const regions = [];
25635
+ const min = [Infinity, Infinity, Infinity];
25636
+ const max = [-Infinity, -Infinity, -Infinity];
25637
+ for (const [name, tag] of Object.entries(compound(tags.Regions, "Regions"))) {
25638
+ const region = readRegion(compound(tag, `region ${name}`), name, dataVersion, blocks);
25639
+ regions.push(region);
25640
+ for (let axis = 0; axis < 3; axis++) {
25641
+ const start = minimum(region.position[axis], region.signedSize[axis]);
25642
+ min[axis] = Math.min(min[axis], start);
25643
+ max[axis] = Math.max(max[axis], start + region.size[axis]);
25644
+ }
25645
+ }
25646
+ return {
25647
+ size: regions.length ? min.map((value, axis) => max[axis] - value) : [0, 0, 0],
25648
+ blocks: [...blocks.values()],
25649
+ entities: regions.flatMap((region) => region.entities ?? []),
25650
+ dataVersion,
25651
+ regions
25652
+ };
25653
+ }
25654
+ };
25655
+ function readRegion(tags, name, dataVersion, combined) {
25656
+ const position = blockPosition(compound(tags.Position, `region ${name} Position`));
25657
+ const signedSize = blockPosition(compound(tags.Size, `region ${name} Size`));
25658
+ if (signedSize.some((value) => value === 0)) throw new MineRenderError(`Litematica region ${name} dimensions must be nonzero`);
25659
+ const size = signedSize.map(Math.abs);
25660
+ const min = position.map((value, axis) => minimum(value, signedSize[axis]));
25661
+ const [width, height, length] = size;
25662
+ const volume = width * height * length;
25663
+ const palette = compounds(tags.BlockStatePalette, "BlockStatePalette").map(blockState);
25664
+ if (!palette.length) throw new MineRenderError(`Litematica region ${name} has no block palette`);
25665
+ const bits = Math.max(2, Math.ceil(Math.log2(palette.length)));
25666
+ if (!Number.isSafeInteger(volume * bits)) throw new MineRenderError(`Litematica region ${name} volume is too large`);
25667
+ const packed = tags.BlockStates;
25668
+ const expected = Math.ceil(volume * bits / 64);
25669
+ if (packed?.type !== "longArray" || !Array.isArray(packed.value) || packed.value.length !== expected) {
25670
+ throw new MineRenderError(`Litematica region ${name} BlockStates must contain ${expected} longs`);
25671
+ }
25672
+ const words = packed.value.map((pair) => {
25673
+ if (!Array.isArray(pair) || pair.length !== 2 || !pair.every((value) => Number.isInteger(value) && value >= -2147483648 && value <= 4294967295)) {
25674
+ throw new MineRenderError(`Litematica region ${name} has an invalid BlockStates long`);
25675
+ }
25676
+ return [pair[0] >>> 0, pair[1] >>> 0];
25677
+ });
25678
+ const blockEntities = /* @__PURE__ */ new Map();
25679
+ for (const tile of compounds(tags.TileEntities, "TileEntities")) {
25680
+ const pos = blockPosition(tile);
25681
+ if (pos.some((value2, axis) => value2 < 0 || value2 >= size[axis])) {
25682
+ throw new MineRenderError(`Litematica region ${name} block entity is outside its dimensions`);
25683
+ }
25684
+ const index = pos[0] + pos[2] * width + pos[1] * width * length;
25685
+ if (blockEntities.has(index)) throw new MineRenderError(`Duplicate Litematica block entity at index ${index}`);
25686
+ const value = { ...tile };
25687
+ for (const [axis, key] of ["x", "y", "z"].entries()) value[key] = { type: "int", value: pos[axis] + min[axis] };
25688
+ blockEntities.set(index, { type: "compound", value });
25689
+ }
25690
+ const blocks = [];
25691
+ const mask = 2 ** bits - 1;
25692
+ for (let index = 0; index < volume; index++) {
25693
+ const word = Math.floor(index * bits / 64);
25694
+ const shift = index * bits % 64;
25695
+ const [high, low] = words[word];
25696
+ let value = shift < 32 ? low >>> shift : high >>> shift - 32;
25697
+ if (shift < 32 && shift + bits > 32) value |= high << 32 - shift;
25698
+ if (shift + bits > 64) value |= words[word + 1][1] << 64 - shift;
25699
+ const id = (value & mask) >>> 0;
25700
+ const state = palette[id];
25701
+ if (!state) throw new MineRenderError(`Litematica region ${name} palette index ${id} does not exist`);
25702
+ const pos = [
25703
+ index % width + min[0],
25704
+ Math.floor(index / (width * length)) + min[1],
25705
+ Math.floor(index / width) % length + min[2]
25706
+ ];
25707
+ const key = pos.join(",");
25708
+ if (["minecraft:air", "minecraft:cave_air", "minecraft:void_air"].includes(state.type)) {
25709
+ combined.delete(key);
25710
+ continue;
25711
+ }
25712
+ const block = { type: state.type, properties: { ...state.properties }, position: pos, nbt: blockEntities.get(index) };
25713
+ blocks.push(block);
25714
+ combined.set(key, block);
25715
+ }
25716
+ const entities = compounds(tags.Entities, "Entities").map((entity) => {
25717
+ const pos = entity.Pos;
25718
+ if (pos?.type !== "list" || pos.value.type !== "double" || pos.value.value.length !== 3 || !pos.value.value.every(Number.isFinite)) {
25719
+ throw new MineRenderError(`Litematica region ${name} entity Pos must contain three doubles`);
25720
+ }
25721
+ const absolute = pos.value.value.map((value2, axis) => value2 + position[axis]);
25722
+ const value = { ...entity, Pos: { type: "list", value: { type: "double", value: [...absolute] } } };
25723
+ return { position: absolute, nbt: { type: "compound", value } };
25724
+ });
25725
+ return { name, position, signedSize, size, blocks, entities, dataVersion };
25726
+ }
25727
+ function minimum(position, size) {
25728
+ return position + Math.min(0, size + 1);
25729
+ }
25730
+ function integer2(tag, name) {
25731
+ if (tag?.type !== "int" || !Number.isInteger(tag.value) || tag.value < -2147483648 || tag.value > 2147483647) {
25732
+ throw new MineRenderError(`Litematica ${name} must be an int`);
25733
+ }
25734
+ return tag.value;
25735
+ }
25736
+ function compound(tag, name) {
25737
+ if (tag?.type !== "compound" || !tag.value || typeof tag.value !== "object" || Array.isArray(tag.value)) {
25738
+ throw new MineRenderError(`Litematica ${name} must be a compound`);
25739
+ }
25740
+ return tag.value;
25741
+ }
25742
+ function blockPosition(tags) {
25743
+ return ["x", "y", "z"].map((axis) => integer2(tags[axis], axis));
25744
+ }
25745
+ function compounds(tag, name) {
25746
+ if (tag === void 0) return [];
25747
+ if (tag.type === "list" && Array.isArray(tag.value.value)) {
25748
+ if (tag.value.value.length === 0) return [];
25749
+ if (tag.value.type === "compound") return tag.value.value;
25750
+ }
25751
+ throw new MineRenderError(`Litematica ${name} must be a compound list`);
25752
+ }
25753
+ function blockState(tags) {
25754
+ const name = tags.Name;
25755
+ if (name?.type !== "string" || !/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(name.value)) {
25756
+ throw new MineRenderError("Litematica palette entry must have a block Name");
25757
+ }
25758
+ const properties = {};
25759
+ if (tags.Properties !== void 0) {
25760
+ for (const [key, tag] of Object.entries(compound(tags.Properties, "block Properties"))) {
25761
+ if (tag?.type !== "string") throw new MineRenderError("Litematica block properties must be strings");
25762
+ Object.defineProperty(properties, key, { value: tag.value, enumerable: true, writable: true, configurable: true });
25763
+ }
25764
+ }
25765
+ return { type: name.value.includes(":") ? name.value : `minecraft:${name.value}`, properties };
25766
+ }
25767
+
25744
25768
  // src/model/multiblock/SchematicParser.ts
25769
+ init_MineRenderError();
25745
25770
  var SchematicParser = class {
25746
25771
  /**
25747
25772
  * Parses a legacy schematic using custom mappings before the bundled defaults.
@@ -25783,35 +25808,21 @@ var SchematicParser = class {
25783
25808
  tileEntities.set((y * length + z) * width + x, { type: "compound", value: tile });
25784
25809
  }
25785
25810
  const blocks = [];
25786
- const mapping = legacyBlocks_default.blocks;
25787
25811
  for (let index = 0; index < volume; index++) {
25788
25812
  const packed = addBlocks?.value[index >> 1] ?? 0;
25789
25813
  const high = packed >> (index & 1) * 4 & 15;
25790
25814
  const id = high << 8 | ids.value[index] & 255;
25791
25815
  const metadata = data.value[index] & 255;
25792
- const key = `${id}:${metadata}`;
25793
- let mapped = customMappings[key] ?? mapping[key];
25794
- if (!mapped && options.lenient) {
25795
- mapped = customMappings[`${id}:0`] ?? mapping[`${id}:0`];
25796
- }
25797
- if (!mapped) {
25816
+ const block = resolveLegacyBlock(id, metadata, customMappings, options.lenient);
25817
+ if (!block) {
25798
25818
  if (options.lenient) {
25799
25819
  continue;
25800
25820
  }
25801
25821
  throw new MineRenderError(`Unsupported legacy block ${id}:${metadata} at schematic index ${index}`);
25802
25822
  }
25803
- const [type, state] = mapped.split("[");
25804
- if (type === "minecraft:air") continue;
25805
- const properties = {};
25806
- if (state) {
25807
- for (const property of state.slice(0, -1).split(",")) {
25808
- const [key2, value] = property.split("=");
25809
- properties[key2] = value;
25810
- }
25811
- }
25823
+ if (block.type === "minecraft:air") continue;
25812
25824
  blocks.push({
25813
- type,
25814
- properties,
25825
+ ...block,
25815
25826
  position: [index % width, Math.floor(index / (width * length)), Math.floor(index / width) % length],
25816
25827
  nbt: tileEntities.get(index)
25817
25828
  });
@@ -29303,28 +29314,34 @@ var AnvilParser = class {
29303
29314
  if (expected && (chunk.x !== expected.x || chunk.z !== expected.z)) {
29304
29315
  throw new MineRenderError(`Anvil chunk coordinates do not match external file c.${expected.x}.${expected.z}.mcc`);
29305
29316
  }
29317
+ const numericStates = /* @__PURE__ */ new Map();
29306
29318
  for (const section of this.compounds(level.sections ?? level.Sections, "sections")) {
29307
29319
  const y = this.integer(section.Y, "section Y");
29320
+ let data2;
29308
29321
  if (section.Blocks || section.Data || section.Add) {
29309
- throw new MineRenderError("Numeric Anvil block IDs before Minecraft 1.13 are not supported");
29310
- }
29311
- if (section.block_states && section.block_states.type !== "compound") {
29312
- throw new MineRenderError("Anvil block_states must be a compound");
29313
- }
29314
- const modern = section.block_states?.type === "compound" ? section.block_states.value : void 0;
29315
- const paletteTag = modern ? modern.palette : section.Palette;
29316
- const palette = paletteTag?.type === "list" && paletteTag.value.type === "string" ? paletteTag.value.value : this.compounds(paletteTag, "block palette");
29317
- const data2 = new ChunkData();
29318
- if (palette.length) {
29319
- const states = palette.map((entry) => this.block(entry));
29320
- const indices = this.unpack(modern ? modern.data : section.BlockStates, states.length, !!modern, dataVersion);
29321
- for (let index = 0; index < 4096; index++) {
29322
- const state = states[indices[index]];
29323
- if (!state) throw new MineRenderError(`Anvil palette index ${indices[index]} is out of range`);
29324
- if (!ChunkData.isAir(state)) data2.set(index, state);
29322
+ if (section.Palette || section.BlockStates || section.block_states) {
29323
+ throw new MineRenderError(`Anvil section ${y} mixes numeric and paletted block states`);
29324
+ }
29325
+ data2 = this.numericSection(section, y, options, numericStates);
29326
+ } else {
29327
+ if (section.block_states && section.block_states.type !== "compound") {
29328
+ throw new MineRenderError("Anvil block_states must be a compound");
29329
+ }
29330
+ const modern = section.block_states?.type === "compound" ? section.block_states.value : void 0;
29331
+ const paletteTag = modern ? modern.palette : section.Palette;
29332
+ const palette = paletteTag?.type === "list" && paletteTag.value.type === "string" ? paletteTag.value.value : this.compounds(paletteTag, "block palette");
29333
+ data2 = new ChunkData();
29334
+ if (palette.length) {
29335
+ const states = palette.map((entry) => this.block(entry));
29336
+ const indices = this.unpack(modern ? modern.data : section.BlockStates, states.length, !!modern, dataVersion);
29337
+ for (let index = 0; index < 4096; index++) {
29338
+ const state = states[indices[index]];
29339
+ if (!state) throw new MineRenderError(`Anvil palette index ${indices[index]} is out of range`);
29340
+ if (!ChunkData.isAir(state)) data2.set(index, state);
29341
+ }
29342
+ } else if (modern || section.Palette || section.BlockStates) {
29343
+ throw new MineRenderError("Anvil section has block states without a palette");
29325
29344
  }
29326
- } else if (modern || section.Palette || section.BlockStates) {
29327
- throw new MineRenderError("Anvil section has block states without a palette");
29328
29345
  }
29329
29346
  if (chunk.sections.some((section2) => section2.y === y)) throw new MineRenderError(`Duplicate Anvil section Y ${y}`);
29330
29347
  chunk.sections.push({ y, data: data2 });
@@ -29352,6 +29369,33 @@ var AnvilParser = class {
29352
29369
  }
29353
29370
  return chunk;
29354
29371
  }
29372
+ static numericSection(section, y, options, states) {
29373
+ const bytes = (name, length, optional = false) => {
29374
+ const tag = section[name];
29375
+ if (!tag && optional) return void 0;
29376
+ if (tag?.type !== "byteArray" || !Array.isArray(tag.value) || tag.value.length !== length) {
29377
+ throw new MineRenderError(`Anvil section ${y} ${name} must contain ${length} bytes`);
29378
+ }
29379
+ return tag.value;
29380
+ };
29381
+ const ids = bytes("Blocks", 4096);
29382
+ const metadata = bytes("Data", 2048, true);
29383
+ const extra = bytes("Add", 2048, true);
29384
+ const data = new ChunkData();
29385
+ for (let index = 0; index < 4096; index++) {
29386
+ const shift = (index & 1) * 4;
29387
+ const id = ids[index] & 255 | ((extra?.[index >> 1] ?? 0) >> shift & 15) << 8;
29388
+ const value = (metadata?.[index >> 1] ?? 0) >> shift & 15;
29389
+ const key = id * 16 + value;
29390
+ if (!states.has(key)) states.set(key, resolveLegacyBlock(id, value, options.legacyMappings, options.lenient));
29391
+ const block = states.get(key);
29392
+ if (!block && !options.lenient) {
29393
+ throw new MineRenderError(`Unsupported legacy block ${id}:${value} at Anvil section ${y} index ${index}`);
29394
+ }
29395
+ if (block && !ChunkData.isAir(block)) data.set(index, block);
29396
+ }
29397
+ return data;
29398
+ }
29355
29399
  static compounds(tag, name) {
29356
29400
  if (!tag) return [];
29357
29401
  if (tag.type !== "list" || tag.value.type !== "compound" && tag.value.value.length !== 0) {
@@ -29434,7 +29478,7 @@ var AnvilWorldSource = class {
29434
29478
  this.cachedBytes = 0;
29435
29479
  this.maxCachedRegions = options.maxCachedRegions ?? 4;
29436
29480
  this.maxCachedBytes = options.maxCachedBytes ?? 64 * 1024 * 1024;
29437
- this.readExternalChunk = options.readExternalChunk;
29481
+ this.parseOptions = { readExternalChunk: options.readExternalChunk, legacyMappings: options.legacyMappings, lenient: options.lenient };
29438
29482
  for (const [name, value] of Object.entries({ maxCachedRegions: this.maxCachedRegions, maxCachedBytes: this.maxCachedBytes })) {
29439
29483
  if (!Number.isSafeInteger(value) || value < 0) throw new RangeError(`${name} must be a nonnegative safe integer`);
29440
29484
  }
@@ -29449,8 +29493,8 @@ var AnvilWorldSource = class {
29449
29493
  signal?.throwIfAborted();
29450
29494
  if (!data) return void 0;
29451
29495
  const chunk = await AnvilParser.parseChunk(data, x - regionX * 32, z - regionZ * 32, {
29496
+ ...this.parseOptions,
29452
29497
  region: { x: regionX, z: regionZ },
29453
- readExternalChunk: this.readExternalChunk,
29454
29498
  signal
29455
29499
  });
29456
29500
  signal?.throwIfAborted();