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.
@@ -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 key = `${id}:${metadata}`;
25642
- let mapped = customMappings[key] ?? mapping[key];
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
- const [type, state] = mapped.split("[");
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
- type,
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
  });
@@ -29160,28 +29170,34 @@ var AnvilParser = class {
29160
29170
  if (expected && (chunk.x !== expected.x || chunk.z !== expected.z)) {
29161
29171
  throw new MineRenderError(`Anvil chunk coordinates do not match external file c.${expected.x}.${expected.z}.mcc`);
29162
29172
  }
29173
+ const numericStates = /* @__PURE__ */ new Map();
29163
29174
  for (const section of this.compounds(level.sections ?? level.Sections, "sections")) {
29164
29175
  const y = this.integer(section.Y, "section Y");
29176
+ let data2;
29165
29177
  if (section.Blocks || section.Data || section.Add) {
29166
- throw new MineRenderError("Numeric Anvil block IDs before Minecraft 1.13 are not supported");
29167
- }
29168
- if (section.block_states && section.block_states.type !== "compound") {
29169
- throw new MineRenderError("Anvil block_states must be a compound");
29170
- }
29171
- const modern = section.block_states?.type === "compound" ? section.block_states.value : void 0;
29172
- const paletteTag = modern ? modern.palette : section.Palette;
29173
- const palette = paletteTag?.type === "list" && paletteTag.value.type === "string" ? paletteTag.value.value : this.compounds(paletteTag, "block palette");
29174
- const data2 = new ChunkData();
29175
- if (palette.length) {
29176
- const states = palette.map((entry) => this.block(entry));
29177
- const indices = this.unpack(modern ? modern.data : section.BlockStates, states.length, !!modern, dataVersion);
29178
- for (let index = 0; index < 4096; index++) {
29179
- const state = states[indices[index]];
29180
- if (!state) throw new MineRenderError(`Anvil palette index ${indices[index]} is out of range`);
29181
- if (!ChunkData.isAir(state)) data2.set(index, state);
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");
29182
29200
  }
29183
- } else if (modern || section.Palette || section.BlockStates) {
29184
- throw new MineRenderError("Anvil section has block states without a palette");
29185
29201
  }
29186
29202
  if (chunk.sections.some((section2) => section2.y === y)) throw new MineRenderError(`Duplicate Anvil section Y ${y}`);
29187
29203
  chunk.sections.push({ y, data: data2 });
@@ -29209,6 +29225,33 @@ var AnvilParser = class {
29209
29225
  }
29210
29226
  return chunk;
29211
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
+ }
29212
29255
  static compounds(tag, name) {
29213
29256
  if (!tag) return [];
29214
29257
  if (tag.type !== "list" || tag.value.type !== "compound" && tag.value.value.length !== 0) {
@@ -29291,7 +29334,7 @@ var AnvilWorldSource = class {
29291
29334
  this.cachedBytes = 0;
29292
29335
  this.maxCachedRegions = options.maxCachedRegions ?? 4;
29293
29336
  this.maxCachedBytes = options.maxCachedBytes ?? 64 * 1024 * 1024;
29294
- this.readExternalChunk = options.readExternalChunk;
29337
+ this.parseOptions = { readExternalChunk: options.readExternalChunk, legacyMappings: options.legacyMappings, lenient: options.lenient };
29295
29338
  for (const [name, value] of Object.entries({ maxCachedRegions: this.maxCachedRegions, maxCachedBytes: this.maxCachedBytes })) {
29296
29339
  if (!Number.isSafeInteger(value) || value < 0) throw new RangeError(`${name} must be a nonnegative safe integer`);
29297
29340
  }
@@ -29306,8 +29349,8 @@ var AnvilWorldSource = class {
29306
29349
  signal?.throwIfAborted();
29307
29350
  if (!data) return void 0;
29308
29351
  const chunk = await AnvilParser.parseChunk(data, x - regionX * 32, z - regionZ * 32, {
29352
+ ...this.parseOptions,
29309
29353
  region: { x: regionX, z: regionZ },
29310
- readExternalChunk: this.readExternalChunk,
29311
29354
  signal
29312
29355
  });
29313
29356
  signal?.throwIfAborted();
@@ -30688,6 +30731,7 @@ export {
30688
30731
  prefix,
30689
30732
  probeImageSize,
30690
30733
  registerBrowserEnv,
30734
+ resolveLegacyBlock,
30691
30735
  sampleEntityAnimation,
30692
30736
  sampleEntityKeyframes,
30693
30737
  sampleFluid,