minerender 2.1.0-alpha.140 → 2.1.0-alpha.142

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.
@@ -1261,6 +1261,12 @@ declare function sampleEntityAnimation(animation: EntityAnimation, time: number,
1261
1261
 
1262
1262
  /** Loads entity and block-entity geometry, texture selections, and animation clips from the dataset. */
1263
1263
  declare class Entities {
1264
+ /**
1265
+ * Resolves `root` (default: the active version). Uses the exact dataset when published or when the
1266
+ * version is not a release; otherwise selects the nearest published release, like {@link MineRenderData.resolve}.
1267
+ * Textures keep the requested root.
1268
+ */
1269
+ static getDatasetRoot(root?: string): Promise<Maybe<string>>;
1264
1270
  /** Lists model paths in the `minecraft` namespace, including subdirectories and excluding `.json`. */
1265
1271
  static getEntityList(): Promise<string[]>;
1266
1272
  private static getModelFile;
@@ -2758,6 +2764,7 @@ declare class Caching {
2758
2764
  static readonly entityTextureCache: AsyncLoadingCache<CacheKey, AssetKey>;
2759
2765
  static readonly entityAnimationCache: AsyncLoadingCache<CacheKey, EntityAnimationFile>;
2760
2766
  static readonly blockEntityIndexCache: AsyncLoadingCache<CacheKey, BlockEntityIndex>;
2767
+ static readonly entityDatasetVersionCache: AsyncLoadingCache<CacheKey, string>;
2761
2768
  static readonly unihexCache: AsyncLoadingCache<CacheKey, UnihexFile>;
2762
2769
  static readonly fontCache: AsyncLoadingCache<CacheKey, BitmapFont>;
2763
2770
  static readonly bannerPatternCache: AsyncLoadingCache<CacheKey, BannerPattern>;
@@ -1261,6 +1261,12 @@ declare function sampleEntityAnimation(animation: EntityAnimation, time: number,
1261
1261
 
1262
1262
  /** Loads entity and block-entity geometry, texture selections, and animation clips from the dataset. */
1263
1263
  declare class Entities {
1264
+ /**
1265
+ * Resolves `root` (default: the active version). Uses the exact dataset when published or when the
1266
+ * version is not a release; otherwise selects the nearest published release, like {@link MineRenderData.resolve}.
1267
+ * Textures keep the requested root.
1268
+ */
1269
+ static getDatasetRoot(root?: string): Promise<Maybe<string>>;
1264
1270
  /** Lists model paths in the `minecraft` namespace, including subdirectories and excluding `.json`. */
1265
1271
  static getEntityList(): Promise<string[]>;
1266
1272
  private static getModelFile;
@@ -2758,6 +2764,7 @@ declare class Caching {
2758
2764
  static readonly entityTextureCache: AsyncLoadingCache<CacheKey, AssetKey>;
2759
2765
  static readonly entityAnimationCache: AsyncLoadingCache<CacheKey, EntityAnimationFile>;
2760
2766
  static readonly blockEntityIndexCache: AsyncLoadingCache<CacheKey, BlockEntityIndex>;
2767
+ static readonly entityDatasetVersionCache: AsyncLoadingCache<CacheKey, string>;
2761
2768
  static readonly unihexCache: AsyncLoadingCache<CacheKey, UnihexFile>;
2762
2769
  static readonly fontCache: AsyncLoadingCache<CacheKey, BitmapFont>;
2763
2770
  static readonly bannerPatternCache: AsyncLoadingCache<CacheKey, BannerPattern>;
@@ -310,6 +310,9 @@ var init_Caching = __esm({
310
310
  static {
311
311
  this.blockEntityIndexCache = _Caching.createAssetCache();
312
312
  }
313
+ static {
314
+ this.entityDatasetVersionCache = _Caching.createAssetCache();
315
+ }
313
316
  static {
314
317
  this.unihexCache = _Caching.createAssetCache();
315
318
  }
@@ -354,6 +357,7 @@ var init_Caching = __esm({
354
357
  this.entityTextureCache,
355
358
  this.entityAnimationCache,
356
359
  this.blockEntityIndexCache,
360
+ this.entityDatasetVersionCache,
357
361
  this.fontCache,
358
362
  this.unihexCache,
359
363
  this.bannerPatternCache,
@@ -3332,23 +3336,39 @@ var init_MinecraftCubeTexture = __esm({
3332
3336
  }
3333
3337
  });
3334
3338
 
3339
+ // src/assets/_versions/DatasetVersions.ts
3340
+ var parseRelease, compare2, nearestRelease;
3341
+ var init_DatasetVersions = __esm({
3342
+ "src/assets/_versions/DatasetVersions.ts"() {
3343
+ parseRelease = (value) => /^\d+\.\d+(?:\.\d+)?$/.test(value) ? value.split(".").map(Number) : void 0;
3344
+ compare2 = (left, right) => {
3345
+ for (let i = 0; i < 3; i++) {
3346
+ const difference = (left[i] ?? 0) - (right[i] ?? 0);
3347
+ if (difference) return difference;
3348
+ }
3349
+ return 0;
3350
+ };
3351
+ nearestRelease = (requestedVersion, published) => {
3352
+ const requested = parseRelease(requestedVersion);
3353
+ if (!requested) return void 0;
3354
+ const versions = published.map((name) => ({ name, number: parseRelease(name) })).filter((entry) => !!entry.number).sort((left, right) => compare2(left.number, right.number));
3355
+ const sameLine = versions.filter((entry) => entry.number[0] === requested[0] && entry.number[1] === requested[1]);
3356
+ const selected = sameLine.sort((left, right) => Math.abs((left.number[2] ?? 0) - (requested[2] ?? 0)) - Math.abs((right.number[2] ?? 0) - (requested[2] ?? 0)) || compare2(left.number, right.number))[0] ?? versions.filter((entry) => compare2(entry.number, requested) <= 0).pop() ?? versions[0];
3357
+ return selected?.name;
3358
+ };
3359
+ }
3360
+ });
3361
+
3335
3362
  // src/assets/MineRenderData.ts
3336
- var object2, release, compare2, MineRenderData;
3363
+ var object2, MineRenderData;
3337
3364
  var init_MineRenderData = __esm({
3338
3365
  "src/assets/MineRenderData.ts"() {
3366
+ init_DatasetVersions();
3339
3367
  init_AssetKey();
3340
3368
  init_AssetLoader();
3341
3369
  init_AssetParsers();
3342
3370
  init_Caching();
3343
3371
  object2 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
3344
- release = (value) => /^\d+\.\d+(?:\.\d+)?$/.test(value) ? value.split(".").map(Number) : void 0;
3345
- compare2 = (left, right) => {
3346
- for (let i = 0; i < 3; i++) {
3347
- const difference = (left[i] ?? 0) - (right[i] ?? 0);
3348
- if (difference) return difference;
3349
- }
3350
- return 0;
3351
- };
3352
3372
  MineRenderData = class {
3353
3373
  /** Loads one registry. Missing files and invalid data reject the request. */
3354
3374
  static async get(dataset, root) {
@@ -3378,9 +3398,9 @@ var init_MineRenderData = __esm({
3378
3398
  const scope = AssetLoader.persistentScope;
3379
3399
  return this.cached(`resolve:${requestRoot === void 0 ? "sources" : "explicit"}:${root}`, scope, defaultRoot, async () => {
3380
3400
  const requestedVersion = root.slice(root.lastIndexOf("/") + 1);
3381
- let manifest = await this.manifest(root, requestRoot !== void 0 && release(requestedVersion) ? requestedVersion : void 0, requestRoot);
3401
+ let manifest = await this.manifest(root, requestRoot !== void 0 && parseRelease(requestedVersion) ? requestedVersion : void 0, requestRoot);
3382
3402
  if (manifest) return { requestedVersion, version: manifest.minecraftVersion, root, manifest };
3383
- const requested = release(requestedVersion);
3403
+ const requested = parseRelease(requestedVersion);
3384
3404
  if (!requested) {
3385
3405
  throw new Error(`No MineRender data is published for ${requestedVersion}; automatic fallback requires a release`);
3386
3406
  }
@@ -3392,14 +3412,12 @@ var init_MineRenderData = __esm({
3392
3412
  }
3393
3413
  return value.versions;
3394
3414
  });
3395
- const versions = index.map((entry) => ({ name: entry.name, number: release(entry.name) })).filter((entry) => !!entry.number).sort((left, right) => compare2(left.number, right.number));
3396
- const sameLine = versions.filter((entry) => entry.number[0] === requested[0] && entry.number[1] === requested[1]);
3397
- const selected = sameLine.sort((left, right) => Math.abs((left.number[2] ?? 0) - (requested[2] ?? 0)) - Math.abs((right.number[2] ?? 0) - (requested[2] ?? 0)) || compare2(left.number, right.number))[0] ?? versions.filter((entry) => compare2(entry.number, requested) <= 0).pop() ?? versions[0];
3415
+ const selected = nearestRelease(requestedVersion, index.map((entry) => entry.name));
3398
3416
  if (!selected) throw new Error(`No compatible MineRender data is published for ${requestedVersion}`);
3399
- const resolvedRoot = `${base}/${selected.name}`;
3400
- manifest = await this.manifest(resolvedRoot, selected.name, resolvedRoot);
3401
- if (!manifest) throw new Error(`Missing MineRender manifest for published version ${selected.name}`);
3402
- return { requestedVersion, version: selected.name, root: resolvedRoot, manifest };
3417
+ const resolvedRoot = `${base}/${selected}`;
3418
+ manifest = await this.manifest(resolvedRoot, selected, resolvedRoot);
3419
+ if (!manifest) throw new Error(`Missing MineRender manifest for published version ${selected}`);
3420
+ return { requestedVersion, version: selected, root: resolvedRoot, manifest };
3403
3421
  });
3404
3422
  }
3405
3423
  static async manifest(root, expectedVersion, requestRoot) {
@@ -4521,118 +4539,12 @@ var init_Biomes = __esm({
4521
4539
  }
4522
4540
  });
4523
4541
 
4524
- // src/assets/BlockEntities.ts
4525
- var import_three9, log9, BlockEntities;
4526
- var init_BlockEntities = __esm({
4527
- "src/assets/BlockEntities.ts"() {
4528
- import_three9 = require("three");
4529
- init_AssetKey();
4530
- init_AssetLoader();
4531
- init_source();
4532
- init_Caching();
4533
- init_log();
4534
- log9 = logger("BlockEntities");
4535
- BlockEntities = class {
4536
- /**
4537
- * Loads the block index of a version. Versions and sources without one yield an empty index, and so does
4538
- * a failed load: the index is optional, and blocks must not retry or fail with it one by one. The empty
4539
- * result is cached like any other; `Caching.clear()` or `AssetLoader.setVersion()` forces a new attempt.
4540
- */
4541
- static async getIndex(root) {
4542
- const key = new AssetKey("entity-models", "blocks", void 0, void 0, void 0, ".json", root);
4543
- key.rootType = void 0;
4544
- const index = await Caching.blockEntityIndexCache.get(key.serialize(), async () => {
4545
- try {
4546
- return await AssetLoader.get(key, "json" /* JSON */) ?? {};
4547
- } catch (error) {
4548
- log9.warn("Could not load the block entity index; block entities are drawn as plain block models", error);
4549
- return {};
4550
- }
4551
- });
4552
- return index ?? {};
4553
- }
4554
- /** The index entry of a block ID such as `minecraft:chest`. */
4555
- static entry(index, block) {
4556
- const entry = Object.prototype.hasOwnProperty.call(index, block) ? index[block] : void 0;
4557
- return Array.isArray(entry?.parts) ? entry : void 0;
4558
- }
4559
- /** Selects the parts and placement for a block ID such as `minecraft:chest`; undefined when nothing is drawn. */
4560
- static resolve(index, block, state = {}) {
4561
- const entry = this.entry(index, block);
4562
- if (!entry) return void 0;
4563
- const value = (property) => String(state[property]).toLowerCase();
4564
- const parts = entry.parts.filter((part) => Object.entries(part.when ?? {}).every(([property, values]) => String(values).split("|").includes(value(property))));
4565
- if (!parts.length) return void 0;
4566
- let rotation = 0;
4567
- if (entry.rotation) {
4568
- const selected = value(entry.rotation.property);
4569
- rotation = "degrees" in entry.rotation ? entry.rotation.degrees[selected] : Number(selected) * entry.rotation.step;
4570
- if (!Number.isFinite(rotation)) rotation = 0;
4571
- }
4572
- return { parts, rotation, translation: entry.translation ?? [0, 0, 0] };
4573
- }
4574
- /** Placement in block space (0..16 per axis), applied on top of the model's own transform. */
4575
- static matrix(resolved, matrix = new import_three9.Matrix4()) {
4576
- return matrix.makeTranslation(8, 0, 8).multiply(new import_three9.Matrix4().makeRotationY(resolved.rotation * Math.PI / 180)).multiply(new import_three9.Matrix4().makeTranslation(resolved.translation[0] - 8, resolved.translation[1], resolved.translation[2] - 8));
4577
- }
4578
- };
4579
- }
4580
- });
4581
-
4582
- // src/assets/DecoratedPots.ts
4583
- var POTTERY_SHERDS, DecoratedPots;
4584
- var init_DecoratedPots = __esm({
4585
- "src/assets/DecoratedPots.ts"() {
4586
- init_AssetKey();
4587
- POTTERY_SHERDS = Object.freeze([
4588
- "angler",
4589
- "archer",
4590
- "arms_up",
4591
- "blade",
4592
- "brewer",
4593
- "burn",
4594
- "danger",
4595
- "explorer",
4596
- "flow",
4597
- "friend",
4598
- "guster",
4599
- "heart",
4600
- "heartbreak",
4601
- "howl",
4602
- "miner",
4603
- "mourner",
4604
- "plenty",
4605
- "prize",
4606
- "scrape",
4607
- "sheaf",
4608
- "shelter",
4609
- "skull",
4610
- "snort"
4611
- ].map((pattern2) => `minecraft:${pattern2}_pottery_sherd`));
4612
- DecoratedPots = class {
4613
- /** Reads up to four item IDs in back, left, right, front order; missing and unmapped items use plain sides. */
4614
- static getSideTextures(value, root) {
4615
- const items = value === void 0 ? [] : value;
4616
- if (!Array.isArray(items) || items.length > 4) throw new Error("pot_decorations must be an array of at most four item IDs");
4617
- const textures = items.map((item) => {
4618
- if (!isResourceLocation(item)) throw new Error("Pot decorations must be item identifiers");
4619
- const id = item.includes(":") ? item : `minecraft:${item}`;
4620
- return POTTERY_SHERDS.includes(id) ? id.slice("minecraft:".length).replace(/_sherd$/, "_pattern") : "decorated_pot_side";
4621
- });
4622
- return Object.fromEntries(["back", "left", "right", "front"].map((side, index) => [
4623
- side,
4624
- new AssetKey("minecraft", `decorated_pot/${textures[index] ?? "decorated_pot_side"}`, "textures", "entity", "assets", ".png", root)
4625
- ]));
4626
- }
4627
- };
4628
- }
4629
- });
4630
-
4631
4542
  // src/assets/Entities.ts
4632
4543
  var Entities;
4633
4544
  var init_Entities = __esm({
4634
4545
  "src/assets/Entities.ts"() {
4635
4546
  init_AssetKey();
4547
+ init_DatasetVersions();
4636
4548
  init_AssetLoader();
4637
4549
  init_source();
4638
4550
  init_Caching();
@@ -4640,11 +4552,36 @@ var init_Entities = __esm({
4640
4552
  init_MineRenderError();
4641
4553
  init_ModelTextures();
4642
4554
  Entities = class {
4555
+ /**
4556
+ * Resolves `root` (default: the active version). Uses the exact dataset when published or when the
4557
+ * version is not a release; otherwise selects the nearest published release, like {@link MineRenderData.resolve}.
4558
+ * Textures keep the requested root.
4559
+ */
4560
+ static async getDatasetRoot(root) {
4561
+ const requestRoot = (root ?? AssetLoader.ROOT).replace(/\/+$/, "");
4562
+ const requestedVersion = requestRoot.slice(requestRoot.lastIndexOf("/") + 1);
4563
+ const base = requestRoot.slice(0, requestRoot.lastIndexOf("/"));
4564
+ const selected = await Caching.entityDatasetVersionCache.get(root ?? `sources:${AssetLoader.ROOT}`, async () => {
4565
+ const key = new AssetKey("entity-models", "version", void 0, void 0, void 0, ".json", root);
4566
+ key.rootType = void 0;
4567
+ if (!parseRelease(requestedVersion) || await AssetLoader.get(key, "json" /* JSON */) !== void 0) return requestedVersion;
4568
+ const indexKey = new AssetKey("entity-models", "versions", void 0, void 0, void 0, ".json", base);
4569
+ indexKey.rootType = void 0;
4570
+ const index = await AssetLoader.get(indexKey, "json" /* JSON */);
4571
+ if (index === void 0) return requestedVersion;
4572
+ if (!index || typeof index !== "object" || Array.isArray(index) || !Array.isArray(index.versions) || !index.versions.every((entry) => entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.name === "string")) {
4573
+ throw new MineRenderError(`Invalid entity model version index at ${base}/entity-models/versions.json`);
4574
+ }
4575
+ return nearestRelease(requestedVersion, index.versions.map((entry) => entry.name)) ?? requestedVersion;
4576
+ });
4577
+ return selected === requestedVersion ? root : `${base}/${selected}`;
4578
+ }
4643
4579
  /** Lists model paths in the `minecraft` namespace, including subdirectories and excluding `.json`. */
4644
4580
  static async getEntityList() {
4581
+ const root = await this.getDatasetRoot();
4645
4582
  const collect = async (path) => {
4646
4583
  const prefix2 = path ? `${path}/` : "";
4647
- const key = new AssetKey(DEFAULT_NAMESPACE, `${prefix2}_list`, void 0, void 0, "entity-models", ".json");
4584
+ const key = new AssetKey(DEFAULT_NAMESPACE, `${prefix2}_list`, void 0, void 0, "entity-models", ".json", root);
4648
4585
  const list2 = await Caching.listAssetCache.get(key.serialize(), () => AssetLoader.get(key, "list" /* LIST */));
4649
4586
  if (!list2) return [];
4650
4587
  const files = list2.files.filter((file) => file.endsWith(".json") && file !== "_list.json").map((file) => `${prefix2}${file.slice(0, -5)}`);
@@ -4655,7 +4592,7 @@ var init_Entities = __esm({
4655
4592
  }
4656
4593
  static async getModelFile(modelKey) {
4657
4594
  const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
4658
- const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models", ".json", isAssetKey(modelKey) ? modelKey.root : void 0);
4595
+ const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models", ".json", await this.getDatasetRoot(isAssetKey(modelKey) ? modelKey.root : void 0));
4659
4596
  return Caching.entityModelCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
4660
4597
  }
4661
4598
  /** Returns selectable geometry-layer names, or an empty list when the model is missing. */
@@ -4716,7 +4653,7 @@ var init_Entities = __esm({
4716
4653
  */
4717
4654
  static async getAnimations(modelKey) {
4718
4655
  const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
4719
- const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models/animations", ".json", isAssetKey(modelKey) ? modelKey.root : void 0);
4656
+ const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models/animations", ".json", await this.getDatasetRoot(isAssetKey(modelKey) ? modelKey.root : void 0));
4720
4657
  const file = await Caching.entityAnimationCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
4721
4658
  return file?.animations;
4722
4659
  }
@@ -4758,6 +4695,115 @@ var init_Entities = __esm({
4758
4695
  }
4759
4696
  });
4760
4697
 
4698
+ // src/assets/BlockEntities.ts
4699
+ var import_three9, log9, BlockEntities;
4700
+ var init_BlockEntities = __esm({
4701
+ "src/assets/BlockEntities.ts"() {
4702
+ import_three9 = require("three");
4703
+ init_AssetKey();
4704
+ init_Entities();
4705
+ init_AssetLoader();
4706
+ init_source();
4707
+ init_Caching();
4708
+ init_log();
4709
+ log9 = logger("BlockEntities");
4710
+ BlockEntities = class {
4711
+ /**
4712
+ * Loads the block index of a version. Versions and sources without one yield an empty index, and so does
4713
+ * a failed load: the index is optional, and blocks must not retry or fail with it one by one. The empty
4714
+ * result is cached like any other; `Caching.clear()` or `AssetLoader.setVersion()` forces a new attempt.
4715
+ */
4716
+ static async getIndex(root) {
4717
+ const key = new AssetKey("entity-models", "blocks", void 0, void 0, void 0, ".json", root);
4718
+ key.rootType = void 0;
4719
+ const index = await Caching.blockEntityIndexCache.get(key.serialize(), async () => {
4720
+ try {
4721
+ key.root = await Entities.getDatasetRoot(root);
4722
+ return await AssetLoader.get(key, "json" /* JSON */) ?? {};
4723
+ } catch (error) {
4724
+ log9.warn("Could not load the block entity index; block entities are drawn as plain block models", error);
4725
+ return {};
4726
+ }
4727
+ });
4728
+ return index ?? {};
4729
+ }
4730
+ /** The index entry of a block ID such as `minecraft:chest`. */
4731
+ static entry(index, block) {
4732
+ const entry = Object.prototype.hasOwnProperty.call(index, block) ? index[block] : void 0;
4733
+ return Array.isArray(entry?.parts) ? entry : void 0;
4734
+ }
4735
+ /** Selects the parts and placement for a block ID such as `minecraft:chest`; undefined when nothing is drawn. */
4736
+ static resolve(index, block, state = {}) {
4737
+ const entry = this.entry(index, block);
4738
+ if (!entry) return void 0;
4739
+ const value = (property) => String(state[property]).toLowerCase();
4740
+ const parts = entry.parts.filter((part) => Object.entries(part.when ?? {}).every(([property, values]) => String(values).split("|").includes(value(property))));
4741
+ if (!parts.length) return void 0;
4742
+ let rotation = 0;
4743
+ if (entry.rotation) {
4744
+ const selected = value(entry.rotation.property);
4745
+ rotation = "degrees" in entry.rotation ? entry.rotation.degrees[selected] : Number(selected) * entry.rotation.step;
4746
+ if (!Number.isFinite(rotation)) rotation = 0;
4747
+ }
4748
+ return { parts, rotation, translation: entry.translation ?? [0, 0, 0] };
4749
+ }
4750
+ /** Placement in block space (0..16 per axis), applied on top of the model's own transform. */
4751
+ static matrix(resolved, matrix = new import_three9.Matrix4()) {
4752
+ return matrix.makeTranslation(8, 0, 8).multiply(new import_three9.Matrix4().makeRotationY(resolved.rotation * Math.PI / 180)).multiply(new import_three9.Matrix4().makeTranslation(resolved.translation[0] - 8, resolved.translation[1], resolved.translation[2] - 8));
4753
+ }
4754
+ };
4755
+ }
4756
+ });
4757
+
4758
+ // src/assets/DecoratedPots.ts
4759
+ var POTTERY_SHERDS, DecoratedPots;
4760
+ var init_DecoratedPots = __esm({
4761
+ "src/assets/DecoratedPots.ts"() {
4762
+ init_AssetKey();
4763
+ POTTERY_SHERDS = Object.freeze([
4764
+ "angler",
4765
+ "archer",
4766
+ "arms_up",
4767
+ "blade",
4768
+ "brewer",
4769
+ "burn",
4770
+ "danger",
4771
+ "explorer",
4772
+ "flow",
4773
+ "friend",
4774
+ "guster",
4775
+ "heart",
4776
+ "heartbreak",
4777
+ "howl",
4778
+ "miner",
4779
+ "mourner",
4780
+ "plenty",
4781
+ "prize",
4782
+ "scrape",
4783
+ "sheaf",
4784
+ "shelter",
4785
+ "skull",
4786
+ "snort"
4787
+ ].map((pattern2) => `minecraft:${pattern2}_pottery_sherd`));
4788
+ DecoratedPots = class {
4789
+ /** Reads up to four item IDs in back, left, right, front order; missing and unmapped items use plain sides. */
4790
+ static getSideTextures(value, root) {
4791
+ const items = value === void 0 ? [] : value;
4792
+ if (!Array.isArray(items) || items.length > 4) throw new Error("pot_decorations must be an array of at most four item IDs");
4793
+ const textures = items.map((item) => {
4794
+ if (!isResourceLocation(item)) throw new Error("Pot decorations must be item identifiers");
4795
+ const id = item.includes(":") ? item : `minecraft:${item}`;
4796
+ return POTTERY_SHERDS.includes(id) ? id.slice("minecraft:".length).replace(/_sherd$/, "_pattern") : "decorated_pot_side";
4797
+ });
4798
+ return Object.fromEntries(["back", "left", "right", "front"].map((side, index) => [
4799
+ side,
4800
+ new AssetKey("minecraft", `decorated_pot/${textures[index] ?? "decorated_pot_side"}`, "textures", "entity", "assets", ".png", root)
4801
+ ]));
4802
+ }
4803
+ };
4804
+ }
4805
+ });
4806
+
4761
4807
  // src/assets/_fonts/Unihex.ts
4762
4808
  var Unihex_exports = {};
4763
4809
  __export(Unihex_exports, {
@@ -18003,7 +18049,7 @@ var AnvilWorldSource = class {
18003
18049
  read.waiters++;
18004
18050
  return new Promise((resolve, reject) => {
18005
18051
  let finished = false;
18006
- const release2 = () => {
18052
+ const release = () => {
18007
18053
  if (finished) return false;
18008
18054
  finished = true;
18009
18055
  signal?.removeEventListener("abort", abort);
@@ -18011,7 +18057,7 @@ var AnvilWorldSource = class {
18011
18057
  return true;
18012
18058
  };
18013
18059
  const abort = () => {
18014
- if (!release2()) return;
18060
+ if (!release()) return;
18015
18061
  if (!read.waiters && !read.settled) {
18016
18062
  if (this.pending.get(key) === read) this.pending.delete(key);
18017
18063
  read.controller.abort(signal.reason);
@@ -18021,10 +18067,10 @@ var AnvilWorldSource = class {
18021
18067
  signal?.addEventListener("abort", abort, { once: true });
18022
18068
  read.promise.then(
18023
18069
  (data) => {
18024
- if (release2()) resolve(data);
18070
+ if (release()) resolve(data);
18025
18071
  },
18026
18072
  (error) => {
18027
- if (release2()) reject(error);
18073
+ if (release()) reject(error);
18028
18074
  }
18029
18075
  );
18030
18076
  if (signal?.aborted) abort();
@@ -19633,6 +19679,9 @@ var SectionWorker = class _SectionWorker {
19633
19679
  };
19634
19680
 
19635
19681
  // src/world/SectionMesh.ts
19682
+ var BLOCK_SIZE = 16;
19683
+ var QUAD_VERTICES = 4;
19684
+ var QUAD_INDICES = 6;
19636
19685
  var templateCache = /* @__PURE__ */ new WeakMap();
19637
19686
  var WATER_TINT = new import_three46.Color(4159204).toArray();
19638
19687
  function toInput(entries, maxAtlasSize, fluids, lighting) {
@@ -19669,8 +19718,8 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19669
19718
  if (!data) {
19670
19719
  const indices = template.geometry.getIndex();
19671
19720
  const position = template.geometry.getAttribute("position");
19672
- const quads = indices.count / 6;
19673
- if (indices.count % 6 !== 0 || position.count !== quads * 4) {
19721
+ const quads = indices.count / QUAD_INDICES;
19722
+ if (indices.count % QUAD_INDICES !== 0 || position.count !== quads * QUAD_VERTICES) {
19674
19723
  throw new RangeError("Section template geometry must hold four vertices and six indices per quad");
19675
19724
  }
19676
19725
  data = {
@@ -19680,8 +19729,8 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19680
19729
  uvs: new Float32Array(quads * 8),
19681
19730
  uvBounds: new Float32Array(quads * 16),
19682
19731
  colors: new Float32Array(quads * 12),
19683
- shade: new Float32Array(quads * 4),
19684
- indices: new Uint16Array(quads * 6),
19732
+ shade: new Float32Array(quads * QUAD_VERTICES),
19733
+ indices: new Uint16Array(quads * QUAD_INDICES),
19685
19734
  cullFaces: new Uint8Array(template.cullFaces),
19686
19735
  biomeTint: template.biomeTint ? new Uint8Array(template.biomeTint) : void 0,
19687
19736
  atlas: 0,
@@ -19693,7 +19742,7 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19693
19742
  const bounds = template.geometry.getAttribute("uvBounds");
19694
19743
  const color2 = template.geometry.getAttribute("color");
19695
19744
  const shade = template.geometry.getAttribute("shade");
19696
- for (let vertex = 0; vertex < quads * 4; vertex++) {
19745
+ for (let vertex = 0; vertex < quads * QUAD_VERTICES; vertex++) {
19697
19746
  data.positions.set([position.getX(vertex), position.getY(vertex), position.getZ(vertex)], vertex * 3);
19698
19747
  data.normals.set([normal.getX(vertex), normal.getY(vertex), normal.getZ(vertex)], vertex * 3);
19699
19748
  data.uvs.set([uv.getX(vertex), uv.getY(vertex)], vertex * 2);
@@ -19706,7 +19755,7 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19706
19755
  data.colors.set([color2?.getX(vertex) ?? 1, color2?.getY(vertex) ?? 1, color2?.getZ(vertex) ?? 1], vertex * 3);
19707
19756
  data.shade[vertex] = shade?.getX(vertex) ?? 1;
19708
19757
  }
19709
- for (let i2 = 0; i2 < data.indices.length; i2++) data.indices[i2] = indices.getX(i2) - 4 * Math.floor(i2 / 6);
19758
+ for (let i2 = 0; i2 < data.indices.length; i2++) data.indices[i2] = indices.getX(i2) - QUAD_VERTICES * Math.floor(i2 / QUAD_INDICES);
19710
19759
  templateCache.set(template, data);
19711
19760
  }
19712
19761
  id = input.templateData.length;
@@ -19739,6 +19788,54 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19739
19788
  }
19740
19789
  return { input, atlases: atlases2 };
19741
19790
  }
19791
+ function sortTransparentQuads(mesh) {
19792
+ const index = mesh.geometry.getIndex();
19793
+ if (index.count <= QUAD_INDICES) return;
19794
+ index.setUsage(import_three46.DynamicDrawUsage);
19795
+ const count = index.count / QUAD_INDICES;
19796
+ let source, centers, depths, order;
19797
+ const view = new import_three46.Matrix4(), localCamera = new import_three46.Vector3(), key = new import_three46.Vector3(), previous = new import_three46.Vector3();
19798
+ let previousOrthographic;
19799
+ mesh.onBeforeRender = (_renderer, _scene, camera) => {
19800
+ localCamera.setFromMatrixPosition(camera.matrixWorld).applyMatrix4(view.copy(mesh.matrixWorld).invert());
19801
+ const orthographic = !!camera.isOrthographicCamera;
19802
+ if (orthographic) {
19803
+ view.multiplyMatrices(camera.matrixWorldInverse, mesh.matrixWorld);
19804
+ key.set(view.elements[2], view.elements[6], view.elements[10]);
19805
+ } else {
19806
+ key.copy(localCamera).divideScalar(BLOCK_SIZE).floor();
19807
+ }
19808
+ if (orthographic === previousOrthographic && key.equals(previous)) return;
19809
+ previous.copy(key);
19810
+ previousOrthographic = orthographic;
19811
+ if (!centers) {
19812
+ source = new Uint32Array(index.array);
19813
+ centers = new Float32Array(count * 3);
19814
+ depths = new Float32Array(count);
19815
+ order = Uint32Array.from({ length: count }, (_, quad) => quad);
19816
+ const positions = mesh.geometry.getAttribute("position");
19817
+ for (let quad = 0; quad < count; quad++) {
19818
+ for (let axis = 0; axis < 3; axis++) {
19819
+ let center = 0;
19820
+ for (let corner = 0; corner < QUAD_VERTICES; corner++) center += positions.getComponent(quad * QUAD_VERTICES + corner, axis);
19821
+ centers[quad * 3 + axis] = center / QUAD_VERTICES;
19822
+ }
19823
+ }
19824
+ }
19825
+ for (let quad = 0; quad < count; quad++) {
19826
+ const x = centers[quad * 3], y = centers[quad * 3 + 1], z = centers[quad * 3 + 2];
19827
+ depths[quad] = orthographic ? key.x * x + key.y * y + key.z * z : -((x - localCamera.x) ** 2 + (y - localCamera.y) ** 2 + (z - localCamera.z) ** 2);
19828
+ }
19829
+ order.sort((a, b) => depths[a] - depths[b] || a - b);
19830
+ if (!order.some((quad, offset) => index.array[offset * QUAD_INDICES] !== source[quad * QUAD_INDICES])) return;
19831
+ for (let offset = 0; offset < count; offset++) {
19832
+ for (let indexOffset = 0; indexOffset < QUAD_INDICES; indexOffset++) {
19833
+ index.array[offset * QUAD_INDICES + indexOffset] = source[order[offset] * QUAD_INDICES + indexOffset];
19834
+ }
19835
+ }
19836
+ index.needsUpdate = true;
19837
+ };
19838
+ }
19742
19839
  var SectionMesh = class _SectionMesh extends import_three46.Group {
19743
19840
  constructor() {
19744
19841
  super(...arguments);
@@ -19748,7 +19845,11 @@ var SectionMesh = class _SectionMesh extends import_three46.Group {
19748
19845
  /** Returns the owning section cell for a triangle hit on this mesh's merged geometry. */
19749
19846
  getBlockIndex(hit) {
19750
19847
  if (hit.faceIndex === void 0 || hit.faceIndex === null) return void 0;
19751
- return this.blockIndices.get(hit.object)?.[Math.floor(hit.faceIndex / 2)];
19848
+ const mesh = hit.object;
19849
+ const owners = this.blockIndices.get(mesh);
19850
+ if (!owners) return void 0;
19851
+ const vertex = mesh.geometry.getIndex().getX(hit.faceIndex * 3);
19852
+ return owners[Math.floor(vertex / QUAD_VERTICES)];
19752
19853
  }
19753
19854
  /** Builds section-local meshes from visible quads. `maxAtlasSize` limits each atlas dimension in pixels. */
19754
19855
  static build(entries, maxAtlasSize = 2048, fluids, lighting) {
@@ -19784,13 +19885,14 @@ var SectionMesh = class _SectionMesh extends import_three46.Group {
19784
19885
  const material = Materials.createShadedCanvasMaterial(canvas, transparent, false, true);
19785
19886
  material.side = import_three46.FrontSide;
19786
19887
  if (page.layer === 2) {
19888
+ material.depthWrite = !material.transparent;
19787
19889
  material.side = import_three46.DoubleSide;
19788
19890
  material.forceSinglePass = true;
19789
- material.depthWrite = !material.transparent;
19790
19891
  }
19791
19892
  material.vertexColors = true;
19792
19893
  const texture = material.uniforms?.map?.value ?? material.map;
19793
19894
  const mesh = new import_three46.Mesh(geometry, material);
19895
+ if (transparent) sortTransparentQuads(mesh);
19794
19896
  section.add(mesh);
19795
19897
  section.blockIndices.set(mesh, page.blockIndices);
19796
19898
  const unsubscribe = [];