minerender 2.1.0-alpha.136 → 2.1.0-alpha.138

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.
Files changed (60) hide show
  1. package/dist/browser/esm/Materials.mjs +3 -1
  2. package/dist/browser/esm/Materials.mjs.map +1 -1
  3. package/dist/browser/esm/Ticker.mjs +3 -1
  4. package/dist/browser/esm/Ticker.mjs.map +1 -1
  5. package/dist/browser/esm/UVMapper.mjs +9 -9
  6. package/dist/browser/esm/UVMapper.mjs.map +1 -1
  7. package/dist/browser/esm/assets/AssetLoader.mjs +4 -4
  8. package/dist/browser/esm/assets/AssetLoader.mjs.map +1 -1
  9. package/dist/browser/esm/assets/BlockEntities.mjs +3 -1
  10. package/dist/browser/esm/assets/BlockEntities.mjs.map +1 -1
  11. package/dist/browser/esm/assets/_fonts/Unihex.mjs +4 -2
  12. package/dist/browser/esm/assets/_fonts/Unihex.mjs.map +1 -1
  13. package/dist/browser/esm/assets/source/HostedAssetSource.mjs +6 -7
  14. package/dist/browser/esm/assets/source/HostedAssetSource.mjs.map +1 -1
  15. package/dist/browser/esm/env/browser/BrowserCache.mjs +3 -3
  16. package/dist/browser/esm/env/browser/BrowserCache.mjs.map +1 -1
  17. package/dist/browser/esm/image/ImageLoader.mjs +3 -3
  18. package/dist/browser/esm/image/ImageLoader.mjs.map +1 -1
  19. package/dist/browser/esm/index.browser.d.mts +49 -7
  20. package/dist/browser/esm/inspector/SceneInspector.mjs +3 -3
  21. package/dist/browser/esm/inspector/SceneInspector.mjs.map +1 -1
  22. package/dist/browser/esm/instance/InstanceManager.mjs +4 -4
  23. package/dist/browser/esm/instance/InstanceManager.mjs.map +1 -1
  24. package/dist/browser/esm/model/block/scene/BlockObject.mjs +3 -1
  25. package/dist/browser/esm/model/block/scene/BlockObject.mjs.map +1 -1
  26. package/dist/browser/esm/model/scene/ModelObject.mjs +4 -4
  27. package/dist/browser/esm/model/scene/ModelObject.mjs.map +1 -1
  28. package/dist/browser/esm/renderer/Renderer.mjs +4 -2
  29. package/dist/browser/esm/renderer/Renderer.mjs.map +1 -1
  30. package/dist/browser/esm/renderer/SceneObject.mjs +0 -6
  31. package/dist/browser/esm/renderer/SceneObject.mjs.map +1 -1
  32. package/dist/browser/esm/request/Requests.mjs +3 -3
  33. package/dist/browser/esm/request/Requests.mjs.map +1 -1
  34. package/dist/browser/esm/skin/Skins.mjs +6 -4
  35. package/dist/browser/esm/skin/Skins.mjs.map +1 -1
  36. package/dist/browser/esm/texture/TextureLoader.mjs +4 -2
  37. package/dist/browser/esm/texture/TextureLoader.mjs.map +1 -1
  38. package/dist/browser/esm/util/log.mjs +75 -0
  39. package/dist/browser/esm/util/log.mjs.map +1 -1
  40. package/dist/browser/esm/util/performance.mjs.map +1 -1
  41. package/dist/browser/esm/world/Chunk.mjs +8 -5
  42. package/dist/browser/esm/world/Chunk.mjs.map +1 -1
  43. package/dist/browser/esm/world/MineRenderWorld.mjs +96 -54
  44. package/dist/browser/esm/world/MineRenderWorld.mjs.map +1 -1
  45. package/dist/browser/esm/world/WorldStreamer.mjs +21 -20
  46. package/dist/browser/esm/world/WorldStreamer.mjs.map +1 -1
  47. package/dist/browser/esm/world/_entities/WorldEntities.mjs +3 -1
  48. package/dist/browser/esm/world/_entities/WorldEntities.mjs.map +1 -1
  49. package/dist/browser/index.d.ts +49 -7
  50. package/dist/browser/index.js +566 -461
  51. package/dist/browser/index.js.map +1 -1
  52. package/dist/bundle.js +2101 -1994
  53. package/dist/bundle.js.map +1 -1
  54. package/dist/node/index.d.mts +49 -7
  55. package/dist/node/index.d.ts +49 -7
  56. package/dist/node/index.js +566 -459
  57. package/dist/node/index.js.map +1 -1
  58. package/dist/node/index.mjs +537 -431
  59. package/dist/node/index.mjs.map +1 -1
  60. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/world/WorldStreamer.ts"],"sourcesContent":["import type { AnvilChunk } from \"./AnvilParser\";\nimport type { MineRenderWorld } from \"./MineRenderWorld\";\nimport type { WorldChunkSource } from \"./WorldChunkSource\";\n\nexport interface WorldStreamerOptions {\n /** Loads a square of chunk columns around the center. Integer from 0 to 32; defaults to 2. */\n loadRadius?: number;\n /** Retains loaded columns within this radius. Defaults to loadRadius + 1, capped at 32. */\n unloadRadius?: number;\n}\n\ninterface ChunkPosition {\n x: number;\n z: number;\n}\n\n/**\n * Loads nearby chunk columns in distance order and unloads distant columns.\n * Updates coalesce to the latest center and serialize world mutations. Use a dedicated world:\n * the streamer replaces columns it loads, and callers must await disposal before editing that world.\n * The source remains caller-owned. Lighting, biome tint, and LOD are not added by streaming.\n */\nexport class WorldStreamer {\n private readonly loadRadius: number;\n private readonly unloadRadius: number;\n private readonly loaded = new Map<string, ChunkPosition>();\n private readonly owned = new Map<string, ChunkPosition>();\n private readonly missing = new Map<string, ChunkPosition>();\n private readonly failed = new Map<string, ChunkPosition & { error: unknown }>();\n private desired: ChunkPosition[] = [];\n private center?: ChunkPosition;\n private loading?: { position: ChunkPosition; controller: AbortController };\n private running?: Promise<void>;\n private disposal?: Promise<void>;\n private disposed = false;\n\n constructor(\n private readonly world: Pick<MineRenderWorld<boolean>, \"placeChunk\" | \"unloadChunkColumn\">,\n private readonly source: WorldChunkSource,\n options: WorldStreamerOptions = {}\n ) {\n this.loadRadius = options.loadRadius ?? 2;\n this.unloadRadius = options.unloadRadius ?? Math.min(32, this.loadRadius + 1);\n if (![this.loadRadius, this.unloadRadius].every(value => Number.isInteger(value) && value >= 0 && value <= 32)\n || this.unloadRadius < this.loadRadius) {\n throw new RangeError(\"Chunk radii must be integers from 0 to 32, with unloadRadius at least loadRadius\");\n }\n }\n\n /** Returns absolute coordinates of successfully placed columns, including retained columns. */\n public get loadedChunks(): readonly { x: number; z: number }[] {\n return [...this.loaded.values()].map(position => ({ ...position }));\n }\n\n /** Returns source failures retained within the unload radius. Failed columns do not block other loads. */\n public get failedChunks(): readonly { x: number; z: number; error: unknown }[] {\n return [...this.failed.values()].map(failure => ({ ...failure }));\n }\n\n /** Counts desired columns still waiting for a source read or placement, including the active column. */\n public get pendingChunks(): number {\n return this.desired.filter(position => this.needsLoad(position)).length;\n }\n\n /**\n * Updates the center in absolute chunk coordinates. Repeated calls share the active drain.\n * Source failures are recorded in failedChunks; other columns continue loading. Missing and failed\n * columns are remembered within the retention radius. Call retryFailedChunks to retry source failures.\n * Placement and unload failures reject the drain; call update again to retry those operations.\n * Reads outside the latest load radius receive an abort signal; placement finishes before unloading.\n * Sources should represent an unchanged world during streaming.\n */\n public async update(x: number, z: number): Promise<void> {\n if (this.disposed) throw new Error(\"WorldStreamer is disposed\");\n if (![x, z].every(value => Number.isSafeInteger(value)\n && Number.isSafeInteger(value - this.unloadRadius) && Number.isSafeInteger(value + this.unloadRadius))) {\n throw new RangeError(\"Chunk coordinates must be safe integers within the streaming radius\");\n }\n if (!this.center || this.center.x !== x || this.center.z !== z) {\n this.center = { x, z };\n this.desired = [];\n for (let dz = -this.loadRadius; dz <= this.loadRadius; dz++) {\n for (let dx = -this.loadRadius; dx <= this.loadRadius; dx++) {\n this.desired.push({ x: x + dx, z: z + dz });\n }\n }\n this.desired.sort((a, b) => (a.x - x) ** 2 + (a.z - z) ** 2 - (b.x - x) ** 2 - (b.z - z) ** 2);\n if (this.loading && !this.within(this.loading.position, this.loadRadius)) {\n this.loading.controller.abort();\n }\n }\n return this.running ??= Promise.resolve().then(() => this.drain());\n }\n\n /** Updates from a camera or view-center position in scene units. One chunk spans 256 scene units. */\n public async updatePosition(position: { x: number; z: number }): Promise<void> {\n if (![position.x, position.z].every(Number.isFinite)) throw new RangeError(\"Scene coordinates must be finite\");\n return this.update(Math.floor(position.x / 256), Math.floor(position.z / 256));\n }\n\n /** Forgets recorded source failures and retries desired columns at the latest center. */\n public async retryFailedChunks(): Promise<void> {\n if (this.disposed) throw new Error(\"WorldStreamer is disposed\");\n this.failed.clear();\n if (this.center) await this.update(this.center.x, this.center.z);\n }\n\n /** Aborts source reads, waits for active work, then unloads owned columns. The world and source remain caller-owned. */\n public dispose(): Promise<void> {\n this.disposed = true;\n this.desired = [];\n this.center = undefined;\n this.loading?.controller.abort();\n return this.disposal ??= Promise.resolve().then(async () => {\n await this.running?.catch(() => undefined);\n let failure: unknown;\n let failed = false;\n for (const position of this.owned.values()) {\n try {\n await this.unload(position);\n } catch (error) {\n failed = true;\n failure ??= error;\n }\n }\n this.missing.clear();\n this.failed.clear();\n if (failed) throw failure;\n }).catch(error => {\n this.disposal = undefined;\n throw error;\n });\n }\n\n private async drain(): Promise<void> {\n try {\n while (!this.disposed) {\n for (const [key, position] of this.missing) {\n if (!this.within(position, this.unloadRadius)) this.missing.delete(key);\n }\n for (const [key, position] of this.failed) {\n if (!this.within(position, this.unloadRadius)) this.failed.delete(key);\n }\n // Failed placements remain owned until cleanup succeeds, even inside the retained area.\n const obsolete = [...this.owned.values()].find(position =>\n !this.loaded.has(this.key(position)) || !this.within(position, this.unloadRadius));\n if (obsolete) {\n await this.unload(obsolete);\n continue;\n }\n const position = this.desired.find(position => this.needsLoad(position));\n if (!position) return;\n let chunk: AnvilChunk | undefined;\n const controller = new AbortController();\n this.loading = { position, controller };\n try {\n chunk = await this.source.getChunk(position.x, position.z, controller.signal);\n if (chunk) this.validateChunk(chunk, position);\n } catch (error) {\n if (!controller.signal.aborted && !this.disposed && this.within(position, this.loadRadius)) {\n this.failed.set(this.key(position), { ...position, error });\n }\n continue;\n } finally {\n this.loading = undefined;\n }\n if (controller.signal.aborted || this.disposed || !this.within(position, this.loadRadius)) continue;\n if (!chunk) {\n this.missing.set(this.key(position), position);\n continue;\n }\n this.owned.set(this.key(position), position);\n try {\n await this.world.placeChunk(chunk);\n this.loaded.set(this.key(position), position);\n } catch (error) {\n try {\n await this.unload(position);\n } catch (cleanupError) {\n const failure = error instanceof Error ? error : new Error(String(error));\n try {\n Object.defineProperty(failure, \"cause\", { value: cleanupError, configurable: true });\n } catch {\n throw Object.assign(new Error(failure.message), { cause: cleanupError });\n }\n throw failure;\n }\n throw error;\n }\n }\n } finally {\n this.running = undefined;\n }\n }\n\n private needsLoad(position: ChunkPosition): boolean {\n const key = this.key(position);\n return !this.loaded.has(key) && !this.missing.has(key) && !this.failed.has(key);\n }\n\n private async unload(position: ChunkPosition): Promise<void> {\n await this.world.unloadChunkColumn(position.x, position.z);\n this.owned.delete(this.key(position));\n this.loaded.delete(this.key(position));\n }\n\n private validateChunk(chunk: AnvilChunk, position: ChunkPosition): void {\n if (chunk.x !== position.x || chunk.z !== position.z) {\n throw new Error(`World source returned chunk ${chunk.x},${chunk.z} for ${position.x},${position.z}`);\n }\n }\n\n private within(position: ChunkPosition, radius: number): boolean {\n return !!this.center && Math.abs(position.x - this.center.x) <= radius && Math.abs(position.z - this.center.z) <= radius;\n }\n\n private key(position: ChunkPosition): string {\n return `${position.x},${position.z}`;\n }\n}\n"],"mappings":"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","names":["position"]}
1
+ {"version":3,"sources":["../../../../src/world/WorldStreamer.ts"],"sourcesContent":["import type { AnvilChunk } from \"./AnvilParser\";\nimport type { MineRenderWorld } from \"./MineRenderWorld\";\nimport type { WorldChunkSource } from \"./WorldChunkSource\";\n\nexport interface WorldStreamerOptions {\n /** Loads a square of chunk columns around the center. Integer from 0 to 32; defaults to 2. */\n loadRadius?: number;\n /** Retains loaded columns within this radius. Defaults to loadRadius + 1, capped at 32. */\n unloadRadius?: number;\n}\n\ninterface ChunkPosition {\n x: number;\n z: number;\n}\n\n/**\n * Loads nearby chunk columns in distance order and unloads distant columns.\n * Updates coalesce to the latest center and serialize world mutations. Use a dedicated world:\n * the streamer replaces columns it loads, and callers must await disposal before editing that world.\n * The source remains caller-owned. Lighting, biome tint, and LOD are not added by streaming.\n */\nexport class WorldStreamer {\n private readonly loadRadius: number;\n private readonly unloadRadius: number;\n private readonly loaded = new Map<string, ChunkPosition>();\n private readonly owned = new Map<string, ChunkPosition>();\n private readonly missing = new Map<string, ChunkPosition>();\n private readonly failed = new Map<string, ChunkPosition & { error: unknown }>();\n private desired: ChunkPosition[] = [];\n private center?: ChunkPosition;\n private loading?: { position: ChunkPosition; controller: AbortController };\n private running?: Promise<void>;\n private disposal?: Promise<void>;\n private disposed = false;\n\n constructor(\n private readonly world: Pick<MineRenderWorld<boolean>, \"placeChunk\" | \"unloadChunkColumns\">,\n private readonly source: WorldChunkSource,\n options: WorldStreamerOptions = {}\n ) {\n this.loadRadius = options.loadRadius ?? 2;\n this.unloadRadius = options.unloadRadius ?? Math.min(32, this.loadRadius + 1);\n if (![this.loadRadius, this.unloadRadius].every(value => Number.isInteger(value) && value >= 0 && value <= 32)\n || this.unloadRadius < this.loadRadius) {\n throw new RangeError(\"Chunk radii must be integers from 0 to 32, with unloadRadius at least loadRadius\");\n }\n }\n\n /** Returns absolute coordinates of successfully placed columns, including retained columns. */\n public get loadedChunks(): readonly { x: number; z: number }[] {\n return [...this.loaded.values()].map(position => ({ ...position }));\n }\n\n /** Returns source failures retained within the unload radius. Failed columns do not block other loads. */\n public get failedChunks(): readonly { x: number; z: number; error: unknown }[] {\n return [...this.failed.values()].map(failure => ({ ...failure }));\n }\n\n /** Counts desired columns still waiting for a source read or placement, including the active column. */\n public get pendingChunks(): number {\n return this.desired.filter(position => this.needsLoad(position)).length;\n }\n\n /**\n * Updates the center in absolute chunk coordinates. Repeated calls share the active drain.\n * Source failures are recorded in failedChunks; other columns continue loading. Missing and failed\n * columns are remembered within the retention radius. Call retryFailedChunks to retry source failures.\n * Placement and unload failures reject the drain; call update again to retry those operations.\n * Reads outside the latest load radius receive an abort signal; placement finishes before unloading.\n * Sources should represent an unchanged world during streaming.\n */\n public async update(x: number, z: number): Promise<void> {\n if (this.disposed) throw new Error(\"WorldStreamer is disposed\");\n if (![x, z].every(value => Number.isSafeInteger(value)\n && Number.isSafeInteger(value - this.unloadRadius) && Number.isSafeInteger(value + this.unloadRadius))) {\n throw new RangeError(\"Chunk coordinates must be safe integers within the streaming radius\");\n }\n if (!this.center || this.center.x !== x || this.center.z !== z) {\n this.center = { x, z };\n this.desired = [];\n for (let dz = -this.loadRadius; dz <= this.loadRadius; dz++) {\n for (let dx = -this.loadRadius; dx <= this.loadRadius; dx++) {\n this.desired.push({ x: x + dx, z: z + dz });\n }\n }\n this.desired.sort((a, b) => (a.x - x) ** 2 + (a.z - z) ** 2 - (b.x - x) ** 2 - (b.z - z) ** 2);\n if (this.loading && !this.within(this.loading.position, this.loadRadius)) {\n this.loading.controller.abort();\n }\n }\n return this.running ??= Promise.resolve().then(() => this.drain());\n }\n\n /** Updates from a camera or view-center position in scene units. One chunk spans 256 scene units. */\n public async updatePosition(position: { x: number; z: number }): Promise<void> {\n if (![position.x, position.z].every(Number.isFinite)) throw new RangeError(\"Scene coordinates must be finite\");\n return this.update(Math.floor(position.x / 256), Math.floor(position.z / 256));\n }\n\n /** Forgets recorded source failures and retries desired columns at the latest center. */\n public async retryFailedChunks(): Promise<void> {\n if (this.disposed) throw new Error(\"WorldStreamer is disposed\");\n this.failed.clear();\n if (this.center) await this.update(this.center.x, this.center.z);\n }\n\n /** Aborts source reads, waits for active work, then unloads owned columns. The world and source remain caller-owned. */\n public dispose(): Promise<void> {\n this.disposed = true;\n this.desired = [];\n this.center = undefined;\n this.loading?.controller.abort();\n return this.disposal ??= Promise.resolve().then(async () => {\n await this.running?.catch(() => undefined);\n try {\n await this.unloadMany([...this.owned.values()]);\n } finally {\n this.missing.clear();\n this.failed.clear();\n }\n }).catch(error => {\n this.disposal = undefined;\n throw error;\n });\n }\n\n private async drain(): Promise<void> {\n try {\n while (!this.disposed) {\n for (const [key, position] of this.missing) {\n if (!this.within(position, this.unloadRadius)) this.missing.delete(key);\n }\n for (const [key, position] of this.failed) {\n if (!this.within(position, this.unloadRadius)) this.failed.delete(key);\n }\n // Failed placements remain owned until cleanup succeeds, even inside the retained area.\n const obsolete = [...this.owned.values()].filter(position =>\n !this.loaded.has(this.key(position)) || !this.within(position, this.unloadRadius));\n if (obsolete.length) {\n await this.unloadMany(obsolete);\n continue;\n }\n const position = this.desired.find(position => this.needsLoad(position));\n if (!position) return;\n let chunk: AnvilChunk | undefined;\n const controller = new AbortController();\n this.loading = { position, controller };\n try {\n chunk = await this.source.getChunk(position.x, position.z, controller.signal);\n if (chunk) this.validateChunk(chunk, position);\n } catch (error) {\n if (!controller.signal.aborted && !this.disposed && this.within(position, this.loadRadius)) {\n this.failed.set(this.key(position), { ...position, error });\n }\n continue;\n } finally {\n this.loading = undefined;\n }\n if (controller.signal.aborted || this.disposed || !this.within(position, this.loadRadius)) continue;\n if (!chunk) {\n this.missing.set(this.key(position), position);\n continue;\n }\n this.owned.set(this.key(position), position);\n try {\n await this.world.placeChunk(chunk);\n this.loaded.set(this.key(position), position);\n } catch (error) {\n try {\n await this.unloadMany([position]);\n } catch (cleanupError) {\n const failure = error instanceof Error ? error : new Error(String(error));\n try {\n Object.defineProperty(failure, \"cause\", { value: cleanupError, configurable: true });\n } catch {\n throw Object.assign(new Error(failure.message), { cause: cleanupError });\n }\n throw failure;\n }\n throw error;\n }\n }\n } finally {\n this.running = undefined;\n }\n }\n\n private needsLoad(position: ChunkPosition): boolean {\n const key = this.key(position);\n return !this.loaded.has(key) && !this.missing.has(key) && !this.failed.has(key);\n }\n\n private async unloadMany(positions: readonly ChunkPosition[]): Promise<void> {\n if (!positions.length) return;\n try {\n await this.world.unloadChunkColumns(positions.map(({ x, z }) => ({ x, z })));\n } catch (error) {\n // A rejected batch may have removed any subset; retry every owner before reloading it.\n for (const position of positions) this.loaded.delete(this.key(position));\n throw error;\n }\n for (const position of positions) {\n this.owned.delete(this.key(position));\n this.loaded.delete(this.key(position));\n }\n }\n\n private validateChunk(chunk: AnvilChunk, position: ChunkPosition): void {\n if (chunk.x !== position.x || chunk.z !== position.z) {\n throw new Error(`World source returned chunk ${chunk.x},${chunk.z} for ${position.x},${position.z}`);\n }\n }\n\n private within(position: ChunkPosition, radius: number): boolean {\n return !!this.center && Math.abs(position.x - this.center.x) <= radius && Math.abs(position.z - this.center.z) <= radius;\n }\n\n private key(position: ChunkPosition): string {\n return `${position.x},${position.z}`;\n }\n}\n"],"mappings":"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","names":["position"]}
@@ -6,6 +6,8 @@ import { resolveSavedEntity } from "./SavedEntities.mjs";
6
6
  import { BannerPatterns } from "../../assets/BannerPatterns.mjs";
7
7
  import { Vector3 } from "three";
8
8
  import { hitNormal, pickableMeshes, visibleHits } from "../_picking/Picking.mjs";
9
+ import { logger } from "../../util/log.mjs";
10
+ var log = logger("WorldEntities");
9
11
  var WorldEntities = class {
10
12
  constructor(scene) {
11
13
  this.scene = scene;
@@ -74,7 +76,7 @@ var WorldEntities = class {
74
76
  placement.owner.set(object, { type: key.toNamespacedString(), position: placement.position });
75
77
  this.scene.add(object);
76
78
  } catch (error) {
77
- console.warn(`Could not draw saved entity ${placement.key.toNamespacedString()}, skipping it`, error);
79
+ log.warn(`Could not draw saved entity ${placement.key.toNamespacedString()}, skipping it`, error);
78
80
  } finally {
79
81
  if (object && !placement.owner.has(object)) object.dispose();
80
82
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/world/_entities/WorldEntities.ts"],"sourcesContent":["import { Renames } from \"../../assets/Renames\";\nimport { Entities } from \"../../assets/Entities\";\nimport { EntityObject } from \"../../entity/scene/EntityObject\";\nimport type { MultiBlockEntity } from \"../../model/multiblock/MultiBlockStructure\";\nimport type { MineRenderScene } from \"../../renderer/MineRenderScene\";\nimport { resolveSavedEntity } from \"./SavedEntities\";\nimport { BannerPatterns } from \"../../assets/BannerPatterns\";\nimport { Mesh, Raycaster, Vector3 } from \"three\";\nimport type { WorldEntityIntersection } from \"../WorldEntityIntersection\";\nimport { hitNormal, pickableMeshes, visibleHits } from \"../_picking/Picking\";\n\n/** Owns static entity placements independently of the world's block sections. */\nexport class WorldEntities {\n private readonly columns = new Map<string, Map<EntityObject, { type: string; position: MultiBlockEntity[\"position\"] }>>();\n\n constructor(private readonly scene: MineRenderScene) {}\n\n /** Finds the nearest visible triangle belonging to an entity owned by this world. */\n raycast(raycaster: Raycaster): WorldEntityIntersection | undefined {\n const owners = new Map<Mesh, { object: EntityObject; type: string; position: MultiBlockEntity[\"position\"] }>();\n for (const column of this.columns.values()) {\n for (const [object, placement] of column) {\n for (const mesh of pickableMeshes(object, this.scene, raycaster)) {\n owners.set(mesh, { object, ...placement });\n }\n }\n }\n for (const hit of visibleHits(raycaster, [...owners.keys()])) {\n const mesh = hit.object as Mesh;\n const owner = owners.get(mesh)!;\n return {\n type: owner.type,\n position: new Vector3(...owner.position),\n object: owner.object,\n point: hit.point.clone(),\n normal: hitNormal(hit),\n distance: hit.distance\n };\n }\n return undefined;\n }\n\n /** Captures column ownership before asynchronous block or asset loading starts. */\n prepare(entities: readonly MultiBlockEntity[] = [], column?: [number, number]): () => Promise<void> {\n const placements = entities.flatMap(entity => {\n const resolved = resolveSavedEntity(entity);\n if (!resolved) return [];\n const position = [...entity.position] as MultiBlockEntity[\"position\"];\n const key = column?.join(\",\") ?? `${Math.floor(position[0] / 16)},${Math.floor(position[2] / 16)}`;\n let owner = this.columns.get(key);\n if (!owner) this.columns.set(key, owner = new Map());\n return [{ ...resolved, position, column: key, owner }];\n });\n return async () => {\n await Promise.all(placements.map(async placement => {\n const current = () => this.columns.get(placement.column) === placement.owner;\n let object: EntityObject | undefined;\n try {\n if (!current()) return;\n const models = await Entities.getEntityList();\n // An ID the entity dataset does not list may be an older name for one it lists.\n const renamed = models.includes(placement.key.path) ? undefined : await Renames.key(\"entities\", placement.key);\n const key = renamed && models.includes(renamed.path) ? renamed : placement.key;\n if (!models.includes(key.path) || !current()) return;\n const model = await Entities.getEntity(key, placement.texture, { when: placement.when });\n if (!model || !current()) return;\n let tints = placement.tints;\n if (placement.woolDye) {\n const dye = (await BannerPatterns.getColors(placement.key.root))[placement.woolDye];\n // Sheep darken each dye channel; white wool has its own fixed shade.\n const tint = placement.woolDye === \"white\" ? 0xe6e6e6 : (Math.floor((dye >> 16 & 255) * 0.75) << 16)\n | (Math.floor((dye >> 8 & 255) * 0.75) << 8) | Math.floor((dye & 255) * 0.75);\n tints = { ...tints, wool_color: tint };\n }\n if (!current()) return;\n object = new EntityObject(model, { instanceMeshes: false, tints });\n object.scene = this.scene;\n // Block geometry is centered on integer coordinates; saved positions use block corners.\n object.position.fromArray(placement.position).multiplyScalar(16).addScalar(-8);\n object.rotation.y = placement.yaw;\n await object.init();\n if (!current()) return;\n placement.owner.set(object, { type: key.toNamespacedString(), position: placement.position });\n this.scene.add(object);\n } catch (error) {\n console.warn(`Could not draw saved entity ${placement.key.toNamespacedString()}, skipping it`, error);\n } finally {\n if (object && !placement.owner.has(object)) object.dispose();\n }\n }));\n };\n }\n\n clearColumn(x: number, z: number): void {\n const key = `${x},${z}`;\n const objects = this.columns.get(key);\n this.columns.delete(key);\n if (!objects) return;\n for (const object of objects.keys()) {\n object.removeFromScene();\n object.dispose();\n }\n objects.clear();\n }\n\n clear(): void {\n for (const key of this.columns.keys()) {\n const [x, z] = key.split(\",\").map(Number);\n this.clearColumn(x, z);\n }\n }\n}\n"],"mappings":";AAAA,SAAS,eAAe;AACxB,SAAS,gBAAgB;AACzB,SAAS,oBAAoB;AAG7B,SAAS,0BAA0B;AACnC,SAAS,sBAAsB;AAC/B,SAA0B,eAAe;AAEzC,SAAS,WAAW,gBAAgB,mBAAmB;AAGhD,IAAM,gBAAN,MAAoB;AAAA,EAGvB,YAA6B,OAAwB;AAAxB;AAF7B,SAAiB,UAAU,oBAAI,IAAyF;AAAA,EAElE;AAAA;AAAA,EAGtD,QAAQ,WAA2D;AAC/D,UAAM,SAAS,oBAAI,IAA0F;AAC7G,eAAW,UAAU,KAAK,QAAQ,OAAO,GAAG;AACxC,iBAAW,CAAC,QAAQ,SAAS,KAAK,QAAQ;AACtC,mBAAW,QAAQ,eAAe,QAAQ,KAAK,OAAO,SAAS,GAAG;AAC9D,iBAAO,IAAI,MAAM,EAAE,QAAQ,GAAG,UAAU,CAAC;AAAA,QAC7C;AAAA,MACJ;AAAA,IACJ;AACA,eAAW,OAAO,YAAY,WAAW,CAAC,GAAG,OAAO,KAAK,CAAC,CAAC,GAAG;AAC1D,YAAM,OAAO,IAAI;AACjB,YAAM,QAAQ,OAAO,IAAI,IAAI;AAC7B,aAAO;AAAA,QACH,MAAM,MAAM;AAAA,QACZ,UAAU,IAAI,QAAQ,GAAG,MAAM,QAAQ;AAAA,QACvC,QAAQ,MAAM;AAAA,QACd,OAAO,IAAI,MAAM,MAAM;AAAA,QACvB,QAAQ,UAAU,GAAG;AAAA,QACrB,UAAU,IAAI;AAAA,MAClB;AAAA,IACJ;AACA,WAAO;AAAA,EACX;AAAA;AAAA,EAGA,QAAQ,WAAwC,CAAC,GAAG,QAAgD;AAChG,UAAM,aAAa,SAAS,QAAQ,YAAU;AAC1C,YAAM,WAAW,mBAAmB,MAAM;AAC1C,UAAI,CAAC,SAAU,QAAO,CAAC;AACvB,YAAM,WAAW,CAAC,GAAG,OAAO,QAAQ;AACpC,YAAM,MAAM,QAAQ,KAAK,GAAG,KAAK,GAAG,KAAK,MAAM,SAAS,CAAC,IAAI,EAAE,CAAC,IAAI,KAAK,MAAM,SAAS,CAAC,IAAI,EAAE,CAAC;AAChG,UAAI,QAAQ,KAAK,QAAQ,IAAI,GAAG;AAChC,UAAI,CAAC,MAAO,MAAK,QAAQ,IAAI,KAAK,QAAQ,oBAAI,IAAI,CAAC;AACnD,aAAO,CAAC,EAAE,GAAG,UAAU,UAAU,QAAQ,KAAK,MAAM,CAAC;AAAA,IACzD,CAAC;AACD,WAAO,YAAY;AACf,YAAM,QAAQ,IAAI,WAAW,IAAI,OAAM,cAAa;AAChD,cAAM,UAAU,MAAM,KAAK,QAAQ,IAAI,UAAU,MAAM,MAAM,UAAU;AACvE,YAAI;AACJ,YAAI;AACA,cAAI,CAAC,QAAQ,EAAG;AAChB,gBAAM,SAAS,MAAM,SAAS,cAAc;AAE5C,gBAAM,UAAU,OAAO,SAAS,UAAU,IAAI,IAAI,IAAI,SAAY,MAAM,QAAQ,IAAI,YAAY,UAAU,GAAG;AAC7G,gBAAM,MAAM,WAAW,OAAO,SAAS,QAAQ,IAAI,IAAI,UAAU,UAAU;AAC3E,cAAI,CAAC,OAAO,SAAS,IAAI,IAAI,KAAK,CAAC,QAAQ,EAAG;AAC9C,gBAAM,QAAQ,MAAM,SAAS,UAAU,KAAK,UAAU,SAAS,EAAE,MAAM,UAAU,KAAK,CAAC;AACvF,cAAI,CAAC,SAAS,CAAC,QAAQ,EAAG;AAC1B,cAAI,QAAQ,UAAU;AACtB,cAAI,UAAU,SAAS;AACnB,kBAAM,OAAO,MAAM,eAAe,UAAU,UAAU,IAAI,IAAI,GAAG,UAAU,OAAO;AAElF,kBAAM,OAAO,UAAU,YAAY,UAAU,WAAY,KAAK,OAAO,OAAO,KAAK,OAAO,IAAI,KAAK,KAC1F,KAAK,OAAO,OAAO,IAAI,OAAO,IAAI,KAAK,IAAK,KAAK,OAAO,MAAM,OAAO,IAAI;AAChF,oBAAQ,EAAE,GAAG,OAAO,YAAY,KAAK;AAAA,UACzC;AACA,cAAI,CAAC,QAAQ,EAAG;AAChB,mBAAS,IAAI,aAAa,OAAO,EAAE,gBAAgB,OAAO,MAAM,CAAC;AACjE,iBAAO,QAAQ,KAAK;AAEpB,iBAAO,SAAS,UAAU,UAAU,QAAQ,EAAE,eAAe,EAAE,EAAE,UAAU,EAAE;AAC7E,iBAAO,SAAS,IAAI,UAAU;AAC9B,gBAAM,OAAO,KAAK;AAClB,cAAI,CAAC,QAAQ,EAAG;AAChB,oBAAU,MAAM,IAAI,QAAQ,EAAE,MAAM,IAAI,mBAAmB,GAAG,UAAU,UAAU,SAAS,CAAC;AAC5F,eAAK,MAAM,IAAI,MAAM;AAAA,QACzB,SAAS,OAAO;AACZ,kBAAQ,KAAK,+BAA+B,UAAU,IAAI,mBAAmB,CAAC,iBAAiB,KAAK;AAAA,QACxG,UAAE;AACE,cAAI,UAAU,CAAC,UAAU,MAAM,IAAI,MAAM,EAAG,QAAO,QAAQ;AAAA,QAC/D;AAAA,MACJ,CAAC,CAAC;AAAA,IACN;AAAA,EACJ;AAAA,EAEA,YAAY,GAAW,GAAiB;AACpC,UAAM,MAAM,GAAG,CAAC,IAAI,CAAC;AACrB,UAAM,UAAU,KAAK,QAAQ,IAAI,GAAG;AACpC,SAAK,QAAQ,OAAO,GAAG;AACvB,QAAI,CAAC,QAAS;AACd,eAAW,UAAU,QAAQ,KAAK,GAAG;AACjC,aAAO,gBAAgB;AACvB,aAAO,QAAQ;AAAA,IACnB;AACA,YAAQ,MAAM;AAAA,EAClB;AAAA,EAEA,QAAc;AACV,eAAW,OAAO,KAAK,QAAQ,KAAK,GAAG;AACnC,YAAM,CAAC,GAAG,CAAC,IAAI,IAAI,MAAM,GAAG,EAAE,IAAI,MAAM;AACxC,WAAK,YAAY,GAAG,CAAC;AAAA,IACzB;AAAA,EACJ;AACJ;","names":[]}
1
+ {"version":3,"sources":["../../../../../src/world/_entities/WorldEntities.ts"],"sourcesContent":["import { Renames } from \"../../assets/Renames\";\nimport { Entities } from \"../../assets/Entities\";\nimport { EntityObject } from \"../../entity/scene/EntityObject\";\nimport type { MultiBlockEntity } from \"../../model/multiblock/MultiBlockStructure\";\nimport type { MineRenderScene } from \"../../renderer/MineRenderScene\";\nimport { resolveSavedEntity } from \"./SavedEntities\";\nimport { BannerPatterns } from \"../../assets/BannerPatterns\";\nimport { Mesh, Raycaster, Vector3 } from \"three\";\nimport type { WorldEntityIntersection } from \"../WorldEntityIntersection\";\nimport { hitNormal, pickableMeshes, visibleHits } from \"../_picking/Picking\";\nimport { logger } from \"../../util/log\";\n\nconst log = logger(\"WorldEntities\");\n\n/** Owns static entity placements independently of the world's block sections. */\nexport class WorldEntities {\n private readonly columns = new Map<string, Map<EntityObject, { type: string; position: MultiBlockEntity[\"position\"] }>>();\n\n constructor(private readonly scene: MineRenderScene) {}\n\n /** Finds the nearest visible triangle belonging to an entity owned by this world. */\n raycast(raycaster: Raycaster): WorldEntityIntersection | undefined {\n const owners = new Map<Mesh, { object: EntityObject; type: string; position: MultiBlockEntity[\"position\"] }>();\n for (const column of this.columns.values()) {\n for (const [object, placement] of column) {\n for (const mesh of pickableMeshes(object, this.scene, raycaster)) {\n owners.set(mesh, { object, ...placement });\n }\n }\n }\n for (const hit of visibleHits(raycaster, [...owners.keys()])) {\n const mesh = hit.object as Mesh;\n const owner = owners.get(mesh)!;\n return {\n type: owner.type,\n position: new Vector3(...owner.position),\n object: owner.object,\n point: hit.point.clone(),\n normal: hitNormal(hit),\n distance: hit.distance\n };\n }\n return undefined;\n }\n\n /** Captures column ownership before asynchronous block or asset loading starts. */\n prepare(entities: readonly MultiBlockEntity[] = [], column?: [number, number]): () => Promise<void> {\n const placements = entities.flatMap(entity => {\n const resolved = resolveSavedEntity(entity);\n if (!resolved) return [];\n const position = [...entity.position] as MultiBlockEntity[\"position\"];\n const key = column?.join(\",\") ?? `${Math.floor(position[0] / 16)},${Math.floor(position[2] / 16)}`;\n let owner = this.columns.get(key);\n if (!owner) this.columns.set(key, owner = new Map());\n return [{ ...resolved, position, column: key, owner }];\n });\n return async () => {\n await Promise.all(placements.map(async placement => {\n const current = () => this.columns.get(placement.column) === placement.owner;\n let object: EntityObject | undefined;\n try {\n if (!current()) return;\n const models = await Entities.getEntityList();\n // An ID the entity dataset does not list may be an older name for one it lists.\n const renamed = models.includes(placement.key.path) ? undefined : await Renames.key(\"entities\", placement.key);\n const key = renamed && models.includes(renamed.path) ? renamed : placement.key;\n if (!models.includes(key.path) || !current()) return;\n const model = await Entities.getEntity(key, placement.texture, { when: placement.when });\n if (!model || !current()) return;\n let tints = placement.tints;\n if (placement.woolDye) {\n const dye = (await BannerPatterns.getColors(placement.key.root))[placement.woolDye];\n // Sheep darken each dye channel; white wool has its own fixed shade.\n const tint = placement.woolDye === \"white\" ? 0xe6e6e6 : (Math.floor((dye >> 16 & 255) * 0.75) << 16)\n | (Math.floor((dye >> 8 & 255) * 0.75) << 8) | Math.floor((dye & 255) * 0.75);\n tints = { ...tints, wool_color: tint };\n }\n if (!current()) return;\n object = new EntityObject(model, { instanceMeshes: false, tints });\n object.scene = this.scene;\n // Block geometry is centered on integer coordinates; saved positions use block corners.\n object.position.fromArray(placement.position).multiplyScalar(16).addScalar(-8);\n object.rotation.y = placement.yaw;\n await object.init();\n if (!current()) return;\n placement.owner.set(object, { type: key.toNamespacedString(), position: placement.position });\n this.scene.add(object);\n } catch (error) {\n log.warn(`Could not draw saved entity ${placement.key.toNamespacedString()}, skipping it`, error);\n } finally {\n if (object && !placement.owner.has(object)) object.dispose();\n }\n }));\n };\n }\n\n clearColumn(x: number, z: number): void {\n const key = `${x},${z}`;\n const objects = this.columns.get(key);\n this.columns.delete(key);\n if (!objects) return;\n for (const object of objects.keys()) {\n object.removeFromScene();\n object.dispose();\n }\n objects.clear();\n }\n\n clear(): void {\n for (const key of this.columns.keys()) {\n const [x, z] = key.split(\",\").map(Number);\n this.clearColumn(x, z);\n }\n }\n}\n"],"mappings":"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","names":[]}
@@ -933,6 +933,34 @@ declare class BatchedExecutor {
933
933
  }
934
934
  type Task<T = unknown> = () => T | Promise<T>;
935
935
 
936
+ /** Severity of a log message, from most to least verbose. */
937
+ type LogLevel = "debug" | "info" | "warn" | "error";
938
+ /** The lowest level that is printed, or `"silent"` to print nothing. */
939
+ type LogThreshold = LogLevel | "silent";
940
+ /** Receives each message that passes the threshold. `args` are the logger call's arguments, without a tag. */
941
+ type LogHandler = (level: LogLevel, namespace: string, args: unknown[]) => void;
942
+ interface LoggingOptions {
943
+ /** Threshold for namespaces without an override. Starts at `"warn"`. */
944
+ level?: LogThreshold;
945
+ /** Thresholds by logger name, such as `{ UVMapper: "debug" }`. Replaces earlier overrides. */
946
+ namespaces?: Record<string, LogThreshold>;
947
+ }
948
+ interface Logger {
949
+ debug(...args: unknown[]): void;
950
+ info(...args: unknown[]): void;
951
+ warn(...args: unknown[]): void;
952
+ error(...args: unknown[]): void;
953
+ }
954
+ /** Global log settings for every MineRender logger. */
955
+ declare class Logging {
956
+ /** Updates the given settings and keeps the others. */
957
+ static configure(options: LoggingOptions): void;
958
+ /** Sends messages to `handler` instead of the console. Call without an argument to restore console output. */
959
+ static setHandler(handler?: LogHandler): void;
960
+ }
961
+ /** Creates a logger whose messages are tagged and filtered as `namespace`. */
962
+ declare function logger(namespace: string): Logger;
963
+ /** @deprecated Use {@link logger}. */
936
964
  declare function prefix(prefix?: string): string;
937
965
 
938
966
  /** Rotates geometry in place around the element's origin. The supplied angle is in degrees. */
@@ -944,8 +972,11 @@ declare function addWireframeToObject(obj: Object3D, color?: number, w?: number)
944
972
  /** Adds edge and axis helpers to a mesh and returns a function that disposes their resources. */
945
973
  declare function addWireframeToMesh(geo: BufferGeometry, mesh: Mesh, color?: number, w?: number): () => void;
946
974
 
975
+ /** @deprecated Unused by MineRender. */
947
976
  declare function captureException(e: any, ...extra: any[]): void;
977
+ /** @deprecated Unused by MineRender. */
948
978
  declare function timingStart(marker: string): void;
979
+ /** @deprecated Unused by MineRender. */
949
980
  declare function timingEnd(marker: string): void;
950
981
 
951
982
  declare function sha1(str: string): string;
@@ -4035,7 +4066,7 @@ declare class Chunk<SectionMeshing extends boolean = false> {
4035
4066
  private ambientOcclusionCells?;
4036
4067
  private readonly blockChanges;
4037
4068
  private biomeChange?;
4038
- constructor(scene: MineRenderScene, x: number, y: number, z: number, onBlocksChanged?: ((positions: Vector3[]) => Promise<void>) | undefined, sectionModels?: SectionModels | undefined, neighborCell?: ((x: number, y: number, z: number) => number) | undefined, biomeAt?: ((x: number, y: number, z: number) => Maybe<string>) | undefined, onBiomesChanged?: ((biomes?: readonly string[]) => Promise<void>) | undefined, lightAt?: ((x: number, y: number, z: number) => number) | undefined, onLightCleared?: (() => Promise<void>) | undefined, ambientLight?: number, biomeBlend?: (() => BiomeBlend | undefined) | undefined);
4069
+ constructor(scene: MineRenderScene, x: number, y: number, z: number, onBlocksChanged?: ((positions: Vector3[], deferCulling?: boolean) => Promise<void>) | undefined, sectionModels?: SectionModels | undefined, neighborCell?: ((x: number, y: number, z: number) => number) | undefined, biomeAt?: ((x: number, y: number, z: number) => Maybe<string>) | undefined, onBiomesChanged?: ((biomes?: readonly string[]) => Promise<void>) | undefined, lightAt?: ((x: number, y: number, z: number) => number) | undefined, onLightCleared?: (() => Promise<void>) | undefined, ambientLight?: number, biomeBlend?: (() => BiomeBlend | undefined) | undefined);
4039
4070
  /** Finds the nearest owned block hit across sections, testing each shared mesh once. */
4040
4071
  static raycast<SectionMeshing extends boolean>(chunks: Iterable<Chunk<SectionMeshing>>, raycaster: Raycaster): WorldBlockIntersection | undefined;
4041
4072
  getBlockAt(x: number, y: number, z: number): Maybe<BlockInfo<SectionMeshing>>;
@@ -4047,11 +4078,14 @@ declare class Chunk<SectionMeshing extends boolean = false> {
4047
4078
  getBiomeAt(pos: TripleArray): Maybe<string>;
4048
4079
  /** Copies 64 biome IDs in x + z * 4 + y * 16 order and refreshes placed blocks. Omit the array to clear biome data. */
4049
4080
  setBiomes(biomes?: readonly string[]): Promise<void>;
4050
- /** Rebuilds biome-tinted blocks while preserving hidden blocks and unrelated render objects. */
4081
+ /**
4082
+ * Rebuilds biome-tinted blocks while preserving hidden blocks and unrelated render objects.
4083
+ * The owning world can defer neighbor culling until all affected chunks are refreshed.
4084
+ */
4051
4085
  refreshBiomes(columns?: readonly {
4052
4086
  x: number;
4053
4087
  z: number;
4054
- }[]): Promise<void>;
4088
+ }[], deferCulling?: boolean): Promise<void>;
4055
4089
  private refreshBiomeBlocks;
4056
4090
  /**
4057
4091
  * Places a block at integer world block coordinates. Pass `undefined` to remove it.
@@ -4062,7 +4096,7 @@ declare class Chunk<SectionMeshing extends boolean = false> {
4062
4096
  /**
4063
4097
  * Places a block at integer chunk-local coordinates from 0 to 15.
4064
4098
  */
4065
- setBlockInChunkAt(pos: Vector3, block?: Block, worldPos?: Vector3, onBlocksChanged?: ((positions: Vector3[]) => Promise<void>) | undefined): Promise<Maybe<BlockInfo<SectionMeshing>>>;
4099
+ setBlockInChunkAt(pos: Vector3, block?: Block, worldPos?: Vector3, onBlocksChanged?: ((positions: Vector3[], deferCulling?: boolean) => Promise<void>) | undefined): Promise<Maybe<BlockInfo<SectionMeshing>>>;
4066
4100
  /** Places cells in input order and returns changed world positions without refreshing neighbors. */
4067
4101
  placeBlocks(blocks: readonly {
4068
4102
  index: number;
@@ -4227,6 +4261,14 @@ declare class MineRenderWorld<SectionMeshing extends boolean = false> {
4227
4261
  * Coordinates match AnvilChunk.x/z, not block positions. Missing columns are ignored.
4228
4262
  */
4229
4263
  unloadChunkColumn(x: number, z: number): Promise<void>;
4264
+ /**
4265
+ * Removes chunk columns together and refreshes surviving neighbors once.
4266
+ * Coordinates are absolute chunk columns. Duplicate and missing columns are ignored.
4267
+ */
4268
+ unloadChunkColumns(positions: readonly {
4269
+ x: number;
4270
+ z: number;
4271
+ }[]): Promise<void>;
4230
4272
  private clearChunkColumn;
4231
4273
  private markSectionChanged;
4232
4274
  /** Removes all chunks and releases their blocks, section meshes, and owned entities. */
@@ -4304,7 +4346,7 @@ declare class WorldStreamer {
4304
4346
  private running?;
4305
4347
  private disposal?;
4306
4348
  private disposed;
4307
- constructor(world: Pick<MineRenderWorld<boolean>, "placeChunk" | "unloadChunkColumn">, source: WorldChunkSource, options?: WorldStreamerOptions);
4349
+ constructor(world: Pick<MineRenderWorld<boolean>, "placeChunk" | "unloadChunkColumns">, source: WorldChunkSource, options?: WorldStreamerOptions);
4308
4350
  /** Returns absolute coordinates of successfully placed columns, including retained columns. */
4309
4351
  get loadedChunks(): readonly {
4310
4352
  x: number;
@@ -4338,7 +4380,7 @@ declare class WorldStreamer {
4338
4380
  dispose(): Promise<void>;
4339
4381
  private drain;
4340
4382
  private needsLoad;
4341
- private unload;
4383
+ private unloadMany;
4342
4384
  private validateChunk;
4343
4385
  private within;
4344
4386
  private key;
@@ -4387,4 +4429,4 @@ declare class BrowserCache extends PersistentCache<LocalForage> {
4387
4429
  */
4388
4430
  declare function probeImageSize(data: Uint8Array): ImageSizeInfo;
4389
4431
 
4390
- export { AXES, type AnimationFrame, type AnimationMeta, type AnimatorFunction, type AnvilChunk, type AnvilEntityChunk, type AnvilExternalChunkReader, type AnvilParseOptions, AnvilParser, type AnvilRegion, type AnvilRegionReader, AnvilWorldSource, type AnvilWorldSourceOptions, ArchiveAssetSource, type ArchiveAssetSourceOptions, type ArchiveEntry, type ArchiveProxy, AssetKey, AssetLoadError, AssetLoader, AssetParser, AssetSource, type AssetType, Assets, Axis, BUILTIN_ENTITY, BUILTIN_GENERATED, type BannerPattern, type BannerPatternDraw, BannerPatterns, BasicAssetKey, type BasicMinecraftAsset, BatchedExecutor, type BedrockBone, type BedrockCube, type BedrockEntityDescription, type BedrockEntityFile, type BedrockGeometry, type BedrockGeometryFile, type BinaryAsset, type Biome, type BiomeColorKind, Biomes, type BitmapFont, type BitmapGlyph, type Block, BlockEntities, type BlockEntityEntry, type BlockEntityIndex, type BlockEntityPart, type BlockEntityRotation, type BlockFluidState, type BlockInfo, type BlockModel, type BlockModelTextures, BlockObject, type BlockObjectOptions, type BlockState, type BlockStateMultipart, type BlockStateProperties, type BlockStateProperty, type BlockStatePropertyDefaults, type BlockStatePropertyValue, BlockStateResolver, type BlockStateVariant, type BlockStateVariants, BlockStates, BlockTints, type BoxGeometryKey, BrowserArchiveProxy, BrowserCache, BrowserEnv, CUBE_FACES, CUBE_FACE_OFFSETS, type CacheKey, Caching, type CameraOptions, CanvasImage, type CapeLayout, Chunk, ChunkData, Colormaps, type CompatCanvas, type CompatImage, type ComposerOptions, type ControlsMode, type ControlsOptions, CubeFace, CubeFaceIndex, DEFAULT_ELEMENTS, DEFAULT_NAMESPACE, DEFAULT_ROOT, DEFAULT_UV, DISPLAY_POSITIONS, DYE_COLORS, type DebugOptions, type DecoratedPotSide, DecoratedPots, type DeepPartial, DefaultBlockStates, DisplayPosition, DisplayTransforms, type Disposable, type DoubleArray, type DyeColor, ENTITY_ANIMATION_CHANNELS, type ElementFace, type ElementRotation, Entities, type EntityAnimation, type EntityAnimationBone, type EntityAnimationChannel, type EntityAnimationFile, type EntityAnimationKeyframe, type EntityAnimationOptions, EntityAnimationPlayer, type EntityAnimationPose, type EntityLayer, type EntityModel, type EntityModelCube, type EntityModelFile, type EntityModelLayer, type EntityModelOptions, type EntityModelPart, type EntityModelPass, EntityObject, type EntityObjectOptions, type EntityRenderMode, type EntityTransformOp, Env, type EnvProvider, type ExtractableImageData, type FluidKind, FluidModelObject, type FluidQuads, type FluidRules, type FluidSample, type FluidSampler, type FlyAction, FlyControls, type FlyControlsEventMap, Fonts, type FrameCallback, type FromTo, GUI_CONTAINER_LAYOUTS, Geometries, type GuiBookButtonState, type GuiBookOptions, type GuiBossBarColor, type GuiBossBarOptions, type GuiContainerType, GuiHelper, type GuiItemLayer, type GuiLayer, GuiLight, GuiObject, type GuiObjectOptions, type GuiRecipe, type GuiRecipeIngredient, type GuiRecipeOptions, type GuiSpriteScaling, type GuiText, type GuiTextLayer, type GuiTextLayout, type GuiTextOptions, type GuiTextRun, type GuiTextStyle, type GuiTextureLayer, type GuiTooltipOptions, HostedAssetSource, type IModelTextures, ITEM_GENERATED, type ImageInfo, ImageLoader, type ImageSizeInfo, InstanceManager, InstanceReference, type Instanceable, ItemDefaults, ItemGlint, type ItemModel, type ItemModelContext, type ItemModelTextures, type ItemTintColor, type ItemTintSource, ItemTints, type ListAsset, LitematicaParser, type LitematicaRegion, type LitematicaStructure, type LoadedSceneDocument, type LoadedSceneObject, MapControls, type MaterialKey, Materials, type Maybe, type MeshKey, Meshes, MineRenderData, type MineRenderDataManifest, type MineRenderDatasets, MineRenderError, MineRenderScene, type MineRenderSceneOptions, MineRenderWorld, type MineRenderWorldOptions, type MinecraftAsset, MinecraftCubeTexture, MinecraftFaceUV, type MinecraftTextureMeta, type Model, ModelCulling, type ModelDisplay, type ModelElement, type ModelFaces, ModelGenerator, ModelMerger, ModelObject, type ModelObjectOptions, ModelTextures, Models, type MultiBlockBlock, type MultiBlockEntity, type MultiBlockStructure, type MultipartCondition, type NBTAsset, NBTHelper, OrbitControls, type PackFormat, PackFormats, PackMetadata, PersistentCache, PlayerHeadTextures, type PositionedGuiGlyph, type QuadArray, type RawImageKey, type RenameKind, Renames, type RenderOptions, Renderer, type RendererControls, type RendererFrame, type RendererOptions, type RendererSurface, type RequestConfig, RequestError, type RequestResponse, Requests, type ResolvedBlockEntity, type ResolvedMineRenderData, type ResponseParser, SCENE_DOCUMENT_MAX_BYTES, SKIN_PARTS, type SavedLightSample, type SceneBlockDefinition, type SceneDocument, SceneDocumentLoader, type SceneEntityDefinition, SceneExporter, type SceneGLTFExportOptions, type SceneGuiDefinition, type SceneGuiLayer, SceneInspector, type SceneItemContext, type SceneModelDefinition, SceneObject, type SceneObjectDefinition, type SceneObjectDefinitionBase, type SceneObjectOptions, type SceneSkinDefinition, type SceneSkinPosePart, SceneStats, SceneStatsDisplay, SchematicParser, type SchematicParserOptions, type SectionAtlasSize, type SectionFluidInput, type SectionFluidKind, type SectionFluids, type SectionGeometryInput, type SectionGeometryPage, type SectionLightData, SectionMesh, type SectionMeshEntry, type SectionMeshTemplate, SectionModels, type SectionTemplateData, SectionWorker, type Serializable, type SkinGeometries, SkinImage, SkinObject, type SkinObjectOptions, SkinPart, type SkinTexture, type SkinTextureCoordinates, SkinTextures, Skins, type SpecialItemPart, type SpecialItemRenderer, SpecialItems, SpongeSchematicParser, type StructureNBT, StructureParser, type TextureAsset, TextureAtlas, type TextureKey, TextureLoader, Textures, Ticker, type TickerFunction, TintColors, type Transformable, type TripleArray, UVMapper, type VideoExportOptions, VideoExporter, type WorldBlockIntersection, type WorldChunkSource, type WorldEntityIntersection, WorldStreamer, type WorldStreamerOptions, WrappedImage, addBox3WireframeToObject, addWireframeToMesh, addWireframeToObject, applyElementRotation, applyGenericRotation, axisToVec3, base64decode, base64encode, buildFluidQuads, buildSectionGeometry, capeTextureSizes, captureException, changeEvent, clampRotationDegrees, classicSkinGeometries, classicSkinTextureCoordinates, computeMorphedAttributes, copySectionLight, createCanvas, createFluidGeometry, createGuiTextGeometry, createGuiTextureGeometry, createImage, decodeSceneDocument, encodeSceneDocument, entityAnimationTime, estimateBytesUsed, fluidKindOf, getBlockFluidState, getFluidKind, interleaveAttributes, isAssetKey, isBasicAssetKey, isBlockObject, isDisposable, isDoubleArray, isEntityObject, isFlyControls, isGuiObject, isInstanceManager, isInstanceReference, isInstanceable, isInstancedMesh, isMesh, isMineRenderScene, isModelObject, isObject3D, isOrthographicCamera, isPerspectiveCamera, isQuadArray, isResourceLocation, isSceneObject, isTransformable, isTripleArray, isVector3, layoutGuiText, md5, mergeBufferAttributes, mergeBufferGeometries, mergeVertices, parseSceneDocument, prefix, probeImageSize, registerBrowserEnv, resolveBlockFluidState, resolveLegacyBlock, resolveLegacyBlockState, sampleEntityAnimation, sampleEntityKeyframes, sampleFluid, sampleSectionLight, sectionGeometryTransferables, serializeBoxGeometryKey, serializeImageKey, serializeMaterialKey, serializeMeshKey, serializeTextureKey, sha1, sha256, sha512, shutdown, sleep, slimSkinGeometries, slimSkinTextureCoordinates, timingEnd, timingStart, toDegrees, toRadians, toTrianglesDrawMode, trimCanvas, validateAnimationWeights, yieldToEventLoop };
4432
+ export { AXES, type AnimationFrame, type AnimationMeta, type AnimatorFunction, type AnvilChunk, type AnvilEntityChunk, type AnvilExternalChunkReader, type AnvilParseOptions, AnvilParser, type AnvilRegion, type AnvilRegionReader, AnvilWorldSource, type AnvilWorldSourceOptions, ArchiveAssetSource, type ArchiveAssetSourceOptions, type ArchiveEntry, type ArchiveProxy, AssetKey, AssetLoadError, AssetLoader, AssetParser, AssetSource, type AssetType, Assets, Axis, BUILTIN_ENTITY, BUILTIN_GENERATED, type BannerPattern, type BannerPatternDraw, BannerPatterns, BasicAssetKey, type BasicMinecraftAsset, BatchedExecutor, type BedrockBone, type BedrockCube, type BedrockEntityDescription, type BedrockEntityFile, type BedrockGeometry, type BedrockGeometryFile, type BinaryAsset, type Biome, type BiomeColorKind, Biomes, type BitmapFont, type BitmapGlyph, type Block, BlockEntities, type BlockEntityEntry, type BlockEntityIndex, type BlockEntityPart, type BlockEntityRotation, type BlockFluidState, type BlockInfo, type BlockModel, type BlockModelTextures, BlockObject, type BlockObjectOptions, type BlockState, type BlockStateMultipart, type BlockStateProperties, type BlockStateProperty, type BlockStatePropertyDefaults, type BlockStatePropertyValue, BlockStateResolver, type BlockStateVariant, type BlockStateVariants, BlockStates, BlockTints, type BoxGeometryKey, BrowserArchiveProxy, BrowserCache, BrowserEnv, CUBE_FACES, CUBE_FACE_OFFSETS, type CacheKey, Caching, type CameraOptions, CanvasImage, type CapeLayout, Chunk, ChunkData, Colormaps, type CompatCanvas, type CompatImage, type ComposerOptions, type ControlsMode, type ControlsOptions, CubeFace, CubeFaceIndex, DEFAULT_ELEMENTS, DEFAULT_NAMESPACE, DEFAULT_ROOT, DEFAULT_UV, DISPLAY_POSITIONS, DYE_COLORS, type DebugOptions, type DecoratedPotSide, DecoratedPots, type DeepPartial, DefaultBlockStates, DisplayPosition, DisplayTransforms, type Disposable, type DoubleArray, type DyeColor, ENTITY_ANIMATION_CHANNELS, type ElementFace, type ElementRotation, Entities, type EntityAnimation, type EntityAnimationBone, type EntityAnimationChannel, type EntityAnimationFile, type EntityAnimationKeyframe, type EntityAnimationOptions, EntityAnimationPlayer, type EntityAnimationPose, type EntityLayer, type EntityModel, type EntityModelCube, type EntityModelFile, type EntityModelLayer, type EntityModelOptions, type EntityModelPart, type EntityModelPass, EntityObject, type EntityObjectOptions, type EntityRenderMode, type EntityTransformOp, Env, type EnvProvider, type ExtractableImageData, type FluidKind, FluidModelObject, type FluidQuads, type FluidRules, type FluidSample, type FluidSampler, type FlyAction, FlyControls, type FlyControlsEventMap, Fonts, type FrameCallback, type FromTo, GUI_CONTAINER_LAYOUTS, Geometries, type GuiBookButtonState, type GuiBookOptions, type GuiBossBarColor, type GuiBossBarOptions, type GuiContainerType, GuiHelper, type GuiItemLayer, type GuiLayer, GuiLight, GuiObject, type GuiObjectOptions, type GuiRecipe, type GuiRecipeIngredient, type GuiRecipeOptions, type GuiSpriteScaling, type GuiText, type GuiTextLayer, type GuiTextLayout, type GuiTextOptions, type GuiTextRun, type GuiTextStyle, type GuiTextureLayer, type GuiTooltipOptions, HostedAssetSource, type IModelTextures, ITEM_GENERATED, type ImageInfo, ImageLoader, type ImageSizeInfo, InstanceManager, InstanceReference, type Instanceable, ItemDefaults, ItemGlint, type ItemModel, type ItemModelContext, type ItemModelTextures, type ItemTintColor, type ItemTintSource, ItemTints, type ListAsset, LitematicaParser, type LitematicaRegion, type LitematicaStructure, type LoadedSceneDocument, type LoadedSceneObject, type LogHandler, type LogLevel, type LogThreshold, type Logger, Logging, type LoggingOptions, MapControls, type MaterialKey, Materials, type Maybe, type MeshKey, Meshes, MineRenderData, type MineRenderDataManifest, type MineRenderDatasets, MineRenderError, MineRenderScene, type MineRenderSceneOptions, MineRenderWorld, type MineRenderWorldOptions, type MinecraftAsset, MinecraftCubeTexture, MinecraftFaceUV, type MinecraftTextureMeta, type Model, ModelCulling, type ModelDisplay, type ModelElement, type ModelFaces, ModelGenerator, ModelMerger, ModelObject, type ModelObjectOptions, ModelTextures, Models, type MultiBlockBlock, type MultiBlockEntity, type MultiBlockStructure, type MultipartCondition, type NBTAsset, NBTHelper, OrbitControls, type PackFormat, PackFormats, PackMetadata, PersistentCache, PlayerHeadTextures, type PositionedGuiGlyph, type QuadArray, type RawImageKey, type RenameKind, Renames, type RenderOptions, Renderer, type RendererControls, type RendererFrame, type RendererOptions, type RendererSurface, type RequestConfig, RequestError, type RequestResponse, Requests, type ResolvedBlockEntity, type ResolvedMineRenderData, type ResponseParser, SCENE_DOCUMENT_MAX_BYTES, SKIN_PARTS, type SavedLightSample, type SceneBlockDefinition, type SceneDocument, SceneDocumentLoader, type SceneEntityDefinition, SceneExporter, type SceneGLTFExportOptions, type SceneGuiDefinition, type SceneGuiLayer, SceneInspector, type SceneItemContext, type SceneModelDefinition, SceneObject, type SceneObjectDefinition, type SceneObjectDefinitionBase, type SceneObjectOptions, type SceneSkinDefinition, type SceneSkinPosePart, SceneStats, SceneStatsDisplay, SchematicParser, type SchematicParserOptions, type SectionAtlasSize, type SectionFluidInput, type SectionFluidKind, type SectionFluids, type SectionGeometryInput, type SectionGeometryPage, type SectionLightData, SectionMesh, type SectionMeshEntry, type SectionMeshTemplate, SectionModels, type SectionTemplateData, SectionWorker, type Serializable, type SkinGeometries, SkinImage, SkinObject, type SkinObjectOptions, SkinPart, type SkinTexture, type SkinTextureCoordinates, SkinTextures, Skins, type SpecialItemPart, type SpecialItemRenderer, SpecialItems, SpongeSchematicParser, type StructureNBT, StructureParser, type TextureAsset, TextureAtlas, type TextureKey, TextureLoader, Textures, Ticker, type TickerFunction, TintColors, type Transformable, type TripleArray, UVMapper, type VideoExportOptions, VideoExporter, type WorldBlockIntersection, type WorldChunkSource, type WorldEntityIntersection, WorldStreamer, type WorldStreamerOptions, WrappedImage, addBox3WireframeToObject, addWireframeToMesh, addWireframeToObject, applyElementRotation, applyGenericRotation, axisToVec3, base64decode, base64encode, buildFluidQuads, buildSectionGeometry, capeTextureSizes, captureException, changeEvent, clampRotationDegrees, classicSkinGeometries, classicSkinTextureCoordinates, computeMorphedAttributes, copySectionLight, createCanvas, createFluidGeometry, createGuiTextGeometry, createGuiTextureGeometry, createImage, decodeSceneDocument, encodeSceneDocument, entityAnimationTime, estimateBytesUsed, fluidKindOf, getBlockFluidState, getFluidKind, interleaveAttributes, isAssetKey, isBasicAssetKey, isBlockObject, isDisposable, isDoubleArray, isEntityObject, isFlyControls, isGuiObject, isInstanceManager, isInstanceReference, isInstanceable, isInstancedMesh, isMesh, isMineRenderScene, isModelObject, isObject3D, isOrthographicCamera, isPerspectiveCamera, isQuadArray, isResourceLocation, isSceneObject, isTransformable, isTripleArray, isVector3, layoutGuiText, logger, md5, mergeBufferAttributes, mergeBufferGeometries, mergeVertices, parseSceneDocument, prefix, probeImageSize, registerBrowserEnv, resolveBlockFluidState, resolveLegacyBlock, resolveLegacyBlockState, sampleEntityAnimation, sampleEntityKeyframes, sampleFluid, sampleSectionLight, sectionGeometryTransferables, serializeBoxGeometryKey, serializeImageKey, serializeMaterialKey, serializeMeshKey, serializeTextureKey, sha1, sha256, sha512, shutdown, sleep, slimSkinGeometries, slimSkinTextureCoordinates, timingEnd, timingStart, toDegrees, toRadians, toTrianglesDrawMode, trimCanvas, validateAnimationWeights, yieldToEventLoop };