minerender 2.1.0-alpha.140 → 2.1.0-alpha.141

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.
@@ -309,6 +309,9 @@ var init_Caching = __esm({
309
309
  static {
310
310
  this.blockEntityIndexCache = _Caching.createAssetCache();
311
311
  }
312
+ static {
313
+ this.entityDatasetVersionCache = _Caching.createAssetCache();
314
+ }
312
315
  static {
313
316
  this.unihexCache = _Caching.createAssetCache();
314
317
  }
@@ -353,6 +356,7 @@ var init_Caching = __esm({
353
356
  this.entityTextureCache,
354
357
  this.entityAnimationCache,
355
358
  this.blockEntityIndexCache,
359
+ this.entityDatasetVersionCache,
356
360
  this.fontCache,
357
361
  this.unihexCache,
358
362
  this.bannerPatternCache,
@@ -3331,23 +3335,39 @@ var init_MinecraftCubeTexture = __esm({
3331
3335
  }
3332
3336
  });
3333
3337
 
3338
+ // src/assets/_versions/DatasetVersions.ts
3339
+ var parseRelease, compare2, nearestRelease;
3340
+ var init_DatasetVersions = __esm({
3341
+ "src/assets/_versions/DatasetVersions.ts"() {
3342
+ parseRelease = (value) => /^\d+\.\d+(?:\.\d+)?$/.test(value) ? value.split(".").map(Number) : void 0;
3343
+ compare2 = (left, right) => {
3344
+ for (let i = 0; i < 3; i++) {
3345
+ const difference = (left[i] ?? 0) - (right[i] ?? 0);
3346
+ if (difference) return difference;
3347
+ }
3348
+ return 0;
3349
+ };
3350
+ nearestRelease = (requestedVersion, published) => {
3351
+ const requested = parseRelease(requestedVersion);
3352
+ if (!requested) return void 0;
3353
+ const versions = published.map((name) => ({ name, number: parseRelease(name) })).filter((entry) => !!entry.number).sort((left, right) => compare2(left.number, right.number));
3354
+ const sameLine = versions.filter((entry) => entry.number[0] === requested[0] && entry.number[1] === requested[1]);
3355
+ 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];
3356
+ return selected?.name;
3357
+ };
3358
+ }
3359
+ });
3360
+
3334
3361
  // src/assets/MineRenderData.ts
3335
- var object2, release, compare2, MineRenderData;
3362
+ var object2, MineRenderData;
3336
3363
  var init_MineRenderData = __esm({
3337
3364
  "src/assets/MineRenderData.ts"() {
3365
+ init_DatasetVersions();
3338
3366
  init_AssetKey();
3339
3367
  init_AssetLoader();
3340
3368
  init_AssetParsers();
3341
3369
  init_Caching();
3342
3370
  object2 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
3343
- release = (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
3371
  MineRenderData = class {
3352
3372
  /** Loads one registry. Missing files and invalid data reject the request. */
3353
3373
  static async get(dataset, root) {
@@ -3377,9 +3397,9 @@ var init_MineRenderData = __esm({
3377
3397
  const scope = AssetLoader.persistentScope;
3378
3398
  return this.cached(`resolve:${requestRoot === void 0 ? "sources" : "explicit"}:${root}`, scope, defaultRoot, async () => {
3379
3399
  const requestedVersion = root.slice(root.lastIndexOf("/") + 1);
3380
- let manifest = await this.manifest(root, requestRoot !== void 0 && release(requestedVersion) ? requestedVersion : void 0, requestRoot);
3400
+ let manifest = await this.manifest(root, requestRoot !== void 0 && parseRelease(requestedVersion) ? requestedVersion : void 0, requestRoot);
3381
3401
  if (manifest) return { requestedVersion, version: manifest.minecraftVersion, root, manifest };
3382
- const requested = release(requestedVersion);
3402
+ const requested = parseRelease(requestedVersion);
3383
3403
  if (!requested) {
3384
3404
  throw new Error(`No MineRender data is published for ${requestedVersion}; automatic fallback requires a release`);
3385
3405
  }
@@ -3391,14 +3411,12 @@ var init_MineRenderData = __esm({
3391
3411
  }
3392
3412
  return value.versions;
3393
3413
  });
3394
- const versions = index.map((entry) => ({ name: entry.name, number: release(entry.name) })).filter((entry) => !!entry.number).sort((left, right) => compare2(left.number, right.number));
3395
- const sameLine = versions.filter((entry) => entry.number[0] === requested[0] && entry.number[1] === requested[1]);
3396
- 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];
3414
+ const selected = nearestRelease(requestedVersion, index.map((entry) => entry.name));
3397
3415
  if (!selected) throw new Error(`No compatible MineRender data is published for ${requestedVersion}`);
3398
- const resolvedRoot = `${base}/${selected.name}`;
3399
- manifest = await this.manifest(resolvedRoot, selected.name, resolvedRoot);
3400
- if (!manifest) throw new Error(`Missing MineRender manifest for published version ${selected.name}`);
3401
- return { requestedVersion, version: selected.name, root: resolvedRoot, manifest };
3416
+ const resolvedRoot = `${base}/${selected}`;
3417
+ manifest = await this.manifest(resolvedRoot, selected, resolvedRoot);
3418
+ if (!manifest) throw new Error(`Missing MineRender manifest for published version ${selected}`);
3419
+ return { requestedVersion, version: selected, root: resolvedRoot, manifest };
3402
3420
  });
3403
3421
  }
3404
3422
  static async manifest(root, expectedVersion, requestRoot) {
@@ -4520,118 +4538,12 @@ var init_Biomes = __esm({
4520
4538
  }
4521
4539
  });
4522
4540
 
4523
- // src/assets/BlockEntities.ts
4524
- import { Matrix4 as Matrix42 } from "three";
4525
- var log9, BlockEntities;
4526
- var init_BlockEntities = __esm({
4527
- "src/assets/BlockEntities.ts"() {
4528
- init_AssetKey();
4529
- init_AssetLoader();
4530
- init_source();
4531
- init_Caching();
4532
- init_log();
4533
- log9 = logger("BlockEntities");
4534
- BlockEntities = class {
4535
- /**
4536
- * Loads the block index of a version. Versions and sources without one yield an empty index, and so does
4537
- * a failed load: the index is optional, and blocks must not retry or fail with it one by one. The empty
4538
- * result is cached like any other; `Caching.clear()` or `AssetLoader.setVersion()` forces a new attempt.
4539
- */
4540
- static async getIndex(root) {
4541
- const key = new AssetKey("entity-models", "blocks", void 0, void 0, void 0, ".json", root);
4542
- key.rootType = void 0;
4543
- const index = await Caching.blockEntityIndexCache.get(key.serialize(), async () => {
4544
- try {
4545
- return await AssetLoader.get(key, "json" /* JSON */) ?? {};
4546
- } catch (error) {
4547
- log9.warn("Could not load the block entity index; block entities are drawn as plain block models", error);
4548
- return {};
4549
- }
4550
- });
4551
- return index ?? {};
4552
- }
4553
- /** The index entry of a block ID such as `minecraft:chest`. */
4554
- static entry(index, block) {
4555
- const entry = Object.prototype.hasOwnProperty.call(index, block) ? index[block] : void 0;
4556
- return Array.isArray(entry?.parts) ? entry : void 0;
4557
- }
4558
- /** Selects the parts and placement for a block ID such as `minecraft:chest`; undefined when nothing is drawn. */
4559
- static resolve(index, block, state = {}) {
4560
- const entry = this.entry(index, block);
4561
- if (!entry) return void 0;
4562
- const value = (property) => String(state[property]).toLowerCase();
4563
- const parts = entry.parts.filter((part) => Object.entries(part.when ?? {}).every(([property, values]) => String(values).split("|").includes(value(property))));
4564
- if (!parts.length) return void 0;
4565
- let rotation = 0;
4566
- if (entry.rotation) {
4567
- const selected = value(entry.rotation.property);
4568
- rotation = "degrees" in entry.rotation ? entry.rotation.degrees[selected] : Number(selected) * entry.rotation.step;
4569
- if (!Number.isFinite(rotation)) rotation = 0;
4570
- }
4571
- return { parts, rotation, translation: entry.translation ?? [0, 0, 0] };
4572
- }
4573
- /** Placement in block space (0..16 per axis), applied on top of the model's own transform. */
4574
- static matrix(resolved, matrix = new Matrix42()) {
4575
- return matrix.makeTranslation(8, 0, 8).multiply(new Matrix42().makeRotationY(resolved.rotation * Math.PI / 180)).multiply(new Matrix42().makeTranslation(resolved.translation[0] - 8, resolved.translation[1], resolved.translation[2] - 8));
4576
- }
4577
- };
4578
- }
4579
- });
4580
-
4581
- // src/assets/DecoratedPots.ts
4582
- var POTTERY_SHERDS, DecoratedPots;
4583
- var init_DecoratedPots = __esm({
4584
- "src/assets/DecoratedPots.ts"() {
4585
- init_AssetKey();
4586
- POTTERY_SHERDS = Object.freeze([
4587
- "angler",
4588
- "archer",
4589
- "arms_up",
4590
- "blade",
4591
- "brewer",
4592
- "burn",
4593
- "danger",
4594
- "explorer",
4595
- "flow",
4596
- "friend",
4597
- "guster",
4598
- "heart",
4599
- "heartbreak",
4600
- "howl",
4601
- "miner",
4602
- "mourner",
4603
- "plenty",
4604
- "prize",
4605
- "scrape",
4606
- "sheaf",
4607
- "shelter",
4608
- "skull",
4609
- "snort"
4610
- ].map((pattern2) => `minecraft:${pattern2}_pottery_sherd`));
4611
- DecoratedPots = class {
4612
- /** Reads up to four item IDs in back, left, right, front order; missing and unmapped items use plain sides. */
4613
- static getSideTextures(value, root) {
4614
- const items = value === void 0 ? [] : value;
4615
- if (!Array.isArray(items) || items.length > 4) throw new Error("pot_decorations must be an array of at most four item IDs");
4616
- const textures = items.map((item) => {
4617
- if (!isResourceLocation(item)) throw new Error("Pot decorations must be item identifiers");
4618
- const id = item.includes(":") ? item : `minecraft:${item}`;
4619
- return POTTERY_SHERDS.includes(id) ? id.slice("minecraft:".length).replace(/_sherd$/, "_pattern") : "decorated_pot_side";
4620
- });
4621
- return Object.fromEntries(["back", "left", "right", "front"].map((side, index) => [
4622
- side,
4623
- new AssetKey("minecraft", `decorated_pot/${textures[index] ?? "decorated_pot_side"}`, "textures", "entity", "assets", ".png", root)
4624
- ]));
4625
- }
4626
- };
4627
- }
4628
- });
4629
-
4630
4541
  // src/assets/Entities.ts
4631
4542
  var Entities;
4632
4543
  var init_Entities = __esm({
4633
4544
  "src/assets/Entities.ts"() {
4634
4545
  init_AssetKey();
4546
+ init_DatasetVersions();
4635
4547
  init_AssetLoader();
4636
4548
  init_source();
4637
4549
  init_Caching();
@@ -4639,11 +4551,36 @@ var init_Entities = __esm({
4639
4551
  init_MineRenderError();
4640
4552
  init_ModelTextures();
4641
4553
  Entities = class {
4554
+ /**
4555
+ * Resolves `root` (default: the active version). Uses the exact dataset when published or when the
4556
+ * version is not a release; otherwise selects the nearest published release, like {@link MineRenderData.resolve}.
4557
+ * Textures keep the requested root.
4558
+ */
4559
+ static async getDatasetRoot(root) {
4560
+ const requestRoot = (root ?? AssetLoader.ROOT).replace(/\/+$/, "");
4561
+ const requestedVersion = requestRoot.slice(requestRoot.lastIndexOf("/") + 1);
4562
+ const base = requestRoot.slice(0, requestRoot.lastIndexOf("/"));
4563
+ const selected = await Caching.entityDatasetVersionCache.get(root ?? `sources:${AssetLoader.ROOT}`, async () => {
4564
+ const key = new AssetKey("entity-models", "version", void 0, void 0, void 0, ".json", root);
4565
+ key.rootType = void 0;
4566
+ if (!parseRelease(requestedVersion) || await AssetLoader.get(key, "json" /* JSON */) !== void 0) return requestedVersion;
4567
+ const indexKey = new AssetKey("entity-models", "versions", void 0, void 0, void 0, ".json", base);
4568
+ indexKey.rootType = void 0;
4569
+ const index = await AssetLoader.get(indexKey, "json" /* JSON */);
4570
+ if (index === void 0) return requestedVersion;
4571
+ 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")) {
4572
+ throw new MineRenderError(`Invalid entity model version index at ${base}/entity-models/versions.json`);
4573
+ }
4574
+ return nearestRelease(requestedVersion, index.versions.map((entry) => entry.name)) ?? requestedVersion;
4575
+ });
4576
+ return selected === requestedVersion ? root : `${base}/${selected}`;
4577
+ }
4642
4578
  /** Lists model paths in the `minecraft` namespace, including subdirectories and excluding `.json`. */
4643
4579
  static async getEntityList() {
4580
+ const root = await this.getDatasetRoot();
4644
4581
  const collect = async (path) => {
4645
4582
  const prefix2 = path ? `${path}/` : "";
4646
- const key = new AssetKey(DEFAULT_NAMESPACE, `${prefix2}_list`, void 0, void 0, "entity-models", ".json");
4583
+ const key = new AssetKey(DEFAULT_NAMESPACE, `${prefix2}_list`, void 0, void 0, "entity-models", ".json", root);
4647
4584
  const list2 = await Caching.listAssetCache.get(key.serialize(), () => AssetLoader.get(key, "list" /* LIST */));
4648
4585
  if (!list2) return [];
4649
4586
  const files = list2.files.filter((file) => file.endsWith(".json") && file !== "_list.json").map((file) => `${prefix2}${file.slice(0, -5)}`);
@@ -4654,7 +4591,7 @@ var init_Entities = __esm({
4654
4591
  }
4655
4592
  static async getModelFile(modelKey) {
4656
4593
  const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
4657
- const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models", ".json", isAssetKey(modelKey) ? modelKey.root : void 0);
4594
+ const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models", ".json", await this.getDatasetRoot(isAssetKey(modelKey) ? modelKey.root : void 0));
4658
4595
  return Caching.entityModelCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
4659
4596
  }
4660
4597
  /** Returns selectable geometry-layer names, or an empty list when the model is missing. */
@@ -4715,7 +4652,7 @@ var init_Entities = __esm({
4715
4652
  */
4716
4653
  static async getAnimations(modelKey) {
4717
4654
  const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
4718
- const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models/animations", ".json", isAssetKey(modelKey) ? modelKey.root : void 0);
4655
+ const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models/animations", ".json", await this.getDatasetRoot(isAssetKey(modelKey) ? modelKey.root : void 0));
4719
4656
  const file = await Caching.entityAnimationCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
4720
4657
  return file?.animations;
4721
4658
  }
@@ -4757,6 +4694,115 @@ var init_Entities = __esm({
4757
4694
  }
4758
4695
  });
4759
4696
 
4697
+ // src/assets/BlockEntities.ts
4698
+ import { Matrix4 as Matrix42 } from "three";
4699
+ var log9, BlockEntities;
4700
+ var init_BlockEntities = __esm({
4701
+ "src/assets/BlockEntities.ts"() {
4702
+ init_AssetKey();
4703
+ init_Entities();
4704
+ init_AssetLoader();
4705
+ init_source();
4706
+ init_Caching();
4707
+ init_log();
4708
+ log9 = logger("BlockEntities");
4709
+ BlockEntities = class {
4710
+ /**
4711
+ * Loads the block index of a version. Versions and sources without one yield an empty index, and so does
4712
+ * a failed load: the index is optional, and blocks must not retry or fail with it one by one. The empty
4713
+ * result is cached like any other; `Caching.clear()` or `AssetLoader.setVersion()` forces a new attempt.
4714
+ */
4715
+ static async getIndex(root) {
4716
+ const key = new AssetKey("entity-models", "blocks", void 0, void 0, void 0, ".json", root);
4717
+ key.rootType = void 0;
4718
+ const index = await Caching.blockEntityIndexCache.get(key.serialize(), async () => {
4719
+ try {
4720
+ key.root = await Entities.getDatasetRoot(root);
4721
+ return await AssetLoader.get(key, "json" /* JSON */) ?? {};
4722
+ } catch (error) {
4723
+ log9.warn("Could not load the block entity index; block entities are drawn as plain block models", error);
4724
+ return {};
4725
+ }
4726
+ });
4727
+ return index ?? {};
4728
+ }
4729
+ /** The index entry of a block ID such as `minecraft:chest`. */
4730
+ static entry(index, block) {
4731
+ const entry = Object.prototype.hasOwnProperty.call(index, block) ? index[block] : void 0;
4732
+ return Array.isArray(entry?.parts) ? entry : void 0;
4733
+ }
4734
+ /** Selects the parts and placement for a block ID such as `minecraft:chest`; undefined when nothing is drawn. */
4735
+ static resolve(index, block, state = {}) {
4736
+ const entry = this.entry(index, block);
4737
+ if (!entry) return void 0;
4738
+ const value = (property) => String(state[property]).toLowerCase();
4739
+ const parts = entry.parts.filter((part) => Object.entries(part.when ?? {}).every(([property, values]) => String(values).split("|").includes(value(property))));
4740
+ if (!parts.length) return void 0;
4741
+ let rotation = 0;
4742
+ if (entry.rotation) {
4743
+ const selected = value(entry.rotation.property);
4744
+ rotation = "degrees" in entry.rotation ? entry.rotation.degrees[selected] : Number(selected) * entry.rotation.step;
4745
+ if (!Number.isFinite(rotation)) rotation = 0;
4746
+ }
4747
+ return { parts, rotation, translation: entry.translation ?? [0, 0, 0] };
4748
+ }
4749
+ /** Placement in block space (0..16 per axis), applied on top of the model's own transform. */
4750
+ static matrix(resolved, matrix = new Matrix42()) {
4751
+ return matrix.makeTranslation(8, 0, 8).multiply(new Matrix42().makeRotationY(resolved.rotation * Math.PI / 180)).multiply(new Matrix42().makeTranslation(resolved.translation[0] - 8, resolved.translation[1], resolved.translation[2] - 8));
4752
+ }
4753
+ };
4754
+ }
4755
+ });
4756
+
4757
+ // src/assets/DecoratedPots.ts
4758
+ var POTTERY_SHERDS, DecoratedPots;
4759
+ var init_DecoratedPots = __esm({
4760
+ "src/assets/DecoratedPots.ts"() {
4761
+ init_AssetKey();
4762
+ POTTERY_SHERDS = Object.freeze([
4763
+ "angler",
4764
+ "archer",
4765
+ "arms_up",
4766
+ "blade",
4767
+ "brewer",
4768
+ "burn",
4769
+ "danger",
4770
+ "explorer",
4771
+ "flow",
4772
+ "friend",
4773
+ "guster",
4774
+ "heart",
4775
+ "heartbreak",
4776
+ "howl",
4777
+ "miner",
4778
+ "mourner",
4779
+ "plenty",
4780
+ "prize",
4781
+ "scrape",
4782
+ "sheaf",
4783
+ "shelter",
4784
+ "skull",
4785
+ "snort"
4786
+ ].map((pattern2) => `minecraft:${pattern2}_pottery_sherd`));
4787
+ DecoratedPots = class {
4788
+ /** Reads up to four item IDs in back, left, right, front order; missing and unmapped items use plain sides. */
4789
+ static getSideTextures(value, root) {
4790
+ const items = value === void 0 ? [] : value;
4791
+ if (!Array.isArray(items) || items.length > 4) throw new Error("pot_decorations must be an array of at most four item IDs");
4792
+ const textures = items.map((item) => {
4793
+ if (!isResourceLocation(item)) throw new Error("Pot decorations must be item identifiers");
4794
+ const id = item.includes(":") ? item : `minecraft:${item}`;
4795
+ return POTTERY_SHERDS.includes(id) ? id.slice("minecraft:".length).replace(/_sherd$/, "_pattern") : "decorated_pot_side";
4796
+ });
4797
+ return Object.fromEntries(["back", "left", "right", "front"].map((side, index) => [
4798
+ side,
4799
+ new AssetKey("minecraft", `decorated_pot/${textures[index] ?? "decorated_pot_side"}`, "textures", "entity", "assets", ".png", root)
4800
+ ]));
4801
+ }
4802
+ };
4803
+ }
4804
+ });
4805
+
4760
4806
  // src/assets/_fonts/Unihex.ts
4761
4807
  var Unihex_exports = {};
4762
4808
  __export(Unihex_exports, {
@@ -17835,7 +17881,7 @@ var AnvilWorldSource = class {
17835
17881
  read.waiters++;
17836
17882
  return new Promise((resolve, reject) => {
17837
17883
  let finished = false;
17838
- const release2 = () => {
17884
+ const release = () => {
17839
17885
  if (finished) return false;
17840
17886
  finished = true;
17841
17887
  signal?.removeEventListener("abort", abort);
@@ -17843,7 +17889,7 @@ var AnvilWorldSource = class {
17843
17889
  return true;
17844
17890
  };
17845
17891
  const abort = () => {
17846
- if (!release2()) return;
17892
+ if (!release()) return;
17847
17893
  if (!read.waiters && !read.settled) {
17848
17894
  if (this.pending.get(key) === read) this.pending.delete(key);
17849
17895
  read.controller.abort(signal.reason);
@@ -17853,10 +17899,10 @@ var AnvilWorldSource = class {
17853
17899
  signal?.addEventListener("abort", abort, { once: true });
17854
17900
  read.promise.then(
17855
17901
  (data) => {
17856
- if (release2()) resolve(data);
17902
+ if (release()) resolve(data);
17857
17903
  },
17858
17904
  (error) => {
17859
- if (release2()) reject(error);
17905
+ if (release()) reject(error);
17860
17906
  }
17861
17907
  );
17862
17908
  if (signal?.aborted) abort();