minecraft-renderer 0.1.78 → 0.1.80

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 (200) hide show
  1. package/README.md +29 -25
  2. package/dist/mesher.js +36 -36
  3. package/dist/mesher.js.map +4 -4
  4. package/dist/mesherWasm.js +217 -217
  5. package/dist/minecraft-renderer.js +32 -32
  6. package/dist/minecraft-renderer.js.meta.json +1 -1
  7. package/dist/threeWorker.js +415 -415
  8. package/package.json +5 -2
  9. package/src/bundler/bundlePrepare.ts +3 -8
  10. package/src/graphicsBackend/appViewer.ts +10 -7
  11. package/src/graphicsBackend/config.ts +1 -5
  12. package/src/graphicsBackend/playerState.ts +35 -34
  13. package/src/graphicsBackend/preloadWorkers.ts +43 -41
  14. package/src/graphicsBackend/rendererDefaultOptions.ts +68 -70
  15. package/src/graphicsBackend/rendererOptionsSync.ts +24 -50
  16. package/src/graphicsBackend/types.ts +2 -2
  17. package/src/index.ts +6 -27
  18. package/src/lib/DebugGui.ts +34 -31
  19. package/src/lib/animationController.ts +9 -9
  20. package/src/lib/bakeLegacyLight.ts +1 -6
  21. package/src/lib/bindAbortableListener.test.ts +32 -12
  22. package/src/lib/bindAbortableListener.ts +14 -6
  23. package/src/lib/blockEntityLightRegistry.ts +11 -20
  24. package/src/lib/blockEntityLighting.test.ts +1 -6
  25. package/src/lib/blockEntityLighting.ts +5 -5
  26. package/src/lib/buildSharedConfig.mjs +4 -4
  27. package/src/lib/buildWorkerMcDataIndexes.test.ts +4 -4
  28. package/src/lib/buildWorkerMcDataIndexes.ts +11 -19
  29. package/src/lib/cameraBobbing.ts +1 -1
  30. package/src/lib/canvas2DOverlay.example.ts +13 -33
  31. package/src/lib/canvas2DOverlay.quickstart.ts +24 -54
  32. package/src/lib/canvas2DOverlay.ts +28 -47
  33. package/src/lib/cleanupDecorator.ts +2 -2
  34. package/src/lib/createPlayerObject.test.ts +2 -7
  35. package/src/lib/createPlayerObject.ts +18 -25
  36. package/src/lib/frameTimingCollector.ts +2 -5
  37. package/src/lib/guiRenderer.ts +57 -42
  38. package/src/lib/items.ts +15 -9
  39. package/src/lib/mesherlogReader.ts +5 -6
  40. package/src/lib/simpleUtils.ts +5 -5
  41. package/src/lib/skyLight.ts +3 -3
  42. package/src/lib/smoothSwitcher.ts +11 -18
  43. package/src/lib/ui/newStats.ts +10 -12
  44. package/src/lib/utils/proxy.ts +7 -1
  45. package/src/lib/utils/skins.ts +4 -4
  46. package/src/lib/utils.ts +1 -1
  47. package/src/lib/workerMessageSanitize.ts +5 -12
  48. package/src/lib/workerProxy.ts +52 -42
  49. package/src/lib/workerSyncOps.test.ts +58 -41
  50. package/src/lib/worldrendererCommon.reconfigure.test.ts +24 -21
  51. package/src/lib/worldrendererCommon.removeColumn.test.ts +11 -13
  52. package/src/lib/worldrendererCommon.ts +338 -211
  53. package/src/mesher-legacy/mesher.ts +9 -4
  54. package/src/mesher-legacy/test/a.ts +1 -1
  55. package/src/mesher-legacy/test/mesherTester.ts +2 -2
  56. package/src/mesher-legacy/test/playground.ts +1 -1
  57. package/src/mesher-legacy/test/run/test-js.ts +1 -1
  58. package/src/mesher-legacy/test/snapshotUtils.ts +3 -8
  59. package/src/mesher-legacy/test/test-perf.ts +1 -1
  60. package/src/mesher-legacy/test/tests.test.ts +2 -2
  61. package/src/mesher-shared/blockEntityMetadata.test.ts +7 -5
  62. package/src/mesher-shared/blockEntityMetadata.ts +28 -19
  63. package/src/mesher-shared/blockPropertiesForMeshing.ts +2 -2
  64. package/src/mesher-shared/computeHeightmap.ts +2 -4
  65. package/src/mesher-shared/exportedGeometryTypes.ts +3 -3
  66. package/src/mesher-shared/faceOcclusion.ts +324 -0
  67. package/src/mesher-shared/models.ts +80 -131
  68. package/src/mesher-shared/modelsGeometryCommon.ts +30 -15
  69. package/src/mesher-shared/shared.ts +46 -46
  70. package/src/mesher-shared/standaloneRenderer.ts +40 -44
  71. package/src/mesher-shared/tests/faceOcclusion.test.ts +179 -0
  72. package/src/mesher-shared/tests/liquidQuadInvariant.test.ts +12 -8
  73. package/src/mesher-shared/vertexShading.ts +2 -11
  74. package/src/mesher-shared/world.ts +19 -19
  75. package/src/mesher-shared/worldLighting.test.ts +2 -2
  76. package/src/performanceMonitor/PerformanceMonitor.ts +4 -13
  77. package/src/performanceMonitor/formatPerformanceFactorsDebug.ts +2 -2
  78. package/src/performanceMonitor/index.ts +1 -5
  79. package/src/performanceMonitor/types.ts +1 -1
  80. package/src/playerState/playerState.ts +55 -50
  81. package/src/playerState/types.ts +2 -8
  82. package/src/playground/allEntitiesDebug.ts +19 -16
  83. package/src/playground/baseScene.ts +29 -32
  84. package/src/playground/mobileControls.tsx +133 -129
  85. package/src/playground/playground.html +76 -73
  86. package/src/playground/playground.ts +14 -1
  87. package/src/playground/playgroundUi.tsx +100 -74
  88. package/src/playground/reactUtils.ts +3 -7
  89. package/src/playground/scenes/entities.ts +10 -7
  90. package/src/playground/scenes/frequentUpdates.ts +5 -5
  91. package/src/playground/scenes/geometryExport.ts +6 -20
  92. package/src/playground/scenes/highPosEntityUpdates.ts +2 -10
  93. package/src/playground/scenes/index.ts +1 -1
  94. package/src/playground/scenes/lightingStarfield.ts +18 -12
  95. package/src/playground/scenes/main.ts +15 -11
  96. package/src/playground/scenes/partialBlockCulling.ts +31 -0
  97. package/src/playground/scenes/railsCobweb.ts +1 -1
  98. package/src/playground/scenes/rotationIssue.ts +1 -1
  99. package/src/playground/scenes/slabsOptimization.ts +1 -2
  100. package/src/playground/scenes/transparencyIssue.ts +1 -1
  101. package/src/playground/shared.ts +2 -2
  102. package/src/resourcesManager/resourcesManager.ts +29 -54
  103. package/src/resourcesManager/resourcesManager.worker.test.ts +6 -10
  104. package/src/shims/minecraftData.ts +70 -24
  105. package/src/sign-renderer/index.html +10 -12
  106. package/src/sign-renderer/index.ts +29 -29
  107. package/src/sign-renderer/playground.ts +22 -15
  108. package/src/sign-renderer/tests.test.ts +16 -13
  109. package/src/sign-renderer/vite.config.ts +2 -2
  110. package/src/three/appShared.ts +46 -25
  111. package/src/three/bannerRenderer.ts +33 -61
  112. package/src/three/cameraShake.ts +15 -6
  113. package/src/three/chunkMeshManager.ts +329 -301
  114. package/src/three/cinimaticScript.ts +26 -15
  115. package/src/three/cubeDrawSpans.ts +27 -37
  116. package/src/three/cubeMultiDraw.ts +9 -22
  117. package/src/three/documentRenderer.ts +15 -18
  118. package/src/three/entities.ts +175 -150
  119. package/src/three/entity/EntityMesh.ts +114 -108
  120. package/src/three/entity/animations.js +3 -3
  121. package/src/three/entity/armorModels.ts +15 -15
  122. package/src/three/entity/gltfAnimationUtils.ts +2 -5
  123. package/src/three/fireworks.ts +34 -32
  124. package/src/three/fireworksRenderer.ts +32 -29
  125. package/src/three/globalBlockBuffer.ts +174 -99
  126. package/src/three/globalLegacyBuffer.ts +465 -136
  127. package/src/three/graphicsBackendBase.ts +19 -25
  128. package/src/three/graphicsBackendOffThread.ts +12 -15
  129. package/src/three/hand.ts +21 -21
  130. package/src/three/handLegacy.ts +3 -13
  131. package/src/three/holdingBlock.ts +116 -79
  132. package/src/three/holdingBlockFactory.ts +1 -4
  133. package/src/three/holdingBlockItemIdentity.test.ts +25 -17
  134. package/src/three/holdingBlockItemIdentity.ts +9 -6
  135. package/src/three/holdingBlockLegacy.ts +113 -132
  136. package/src/three/itemMesh.ts +20 -29
  137. package/src/three/legacyMultiDraw.ts +93 -0
  138. package/src/three/legacySectionCull.ts +8 -14
  139. package/src/three/menuBackground/activeView.ts +1 -4
  140. package/src/three/menuBackground/assetUrl.ts +1 -1
  141. package/src/three/menuBackground/classic.ts +4 -12
  142. package/src/three/menuBackground/gpuPreference.ts +1 -3
  143. package/src/three/menuBackground/index.ts +2 -11
  144. package/src/three/menuBackground/renderer.ts +1 -5
  145. package/src/three/menuBackground/types.ts +1 -4
  146. package/src/three/menuBackground/v2.ts +196 -96
  147. package/src/three/menuBackground/v2Meta.ts +3 -3
  148. package/src/three/menuBackground/worldBlocks.ts +37 -35
  149. package/src/three/modules/blockBreakParticles.ts +59 -28
  150. package/src/three/modules/cameraBobbing.ts +3 -6
  151. package/src/three/modules/index.ts +1 -1
  152. package/src/three/modules/rain.ts +10 -23
  153. package/src/three/modules/sciFiWorldReveal.ts +11 -25
  154. package/src/three/modules/starfield.ts +7 -13
  155. package/src/three/renderSlot.ts +28 -16
  156. package/src/three/rendererModuleSystem.ts +1 -3
  157. package/src/three/sceneOrigin.ts +96 -33
  158. package/src/three/sectionRaycastAabb.ts +30 -18
  159. package/src/three/shaderCubeMesh.ts +6 -17
  160. package/src/three/shaders/cubeBlockShader.ts +85 -96
  161. package/src/three/shaders/legacyBlockShader.ts +21 -43
  162. package/src/three/shaders/textureIndexMapping.ts +89 -95
  163. package/src/three/shaders/tintPalette.ts +156 -170
  164. package/src/three/signTextureCache.ts +9 -12
  165. package/src/three/skyboxRenderer.ts +62 -70
  166. package/src/three/tests/chunkMeshManagerLegacy.test.ts +133 -25
  167. package/src/three/tests/cubeDrawSpans.test.ts +55 -25
  168. package/src/three/tests/globalBlockBuffer.test.ts +217 -0
  169. package/src/three/tests/globalLegacyBuffer.test.ts +460 -58
  170. package/src/three/tests/legacyMultiDraw.test.ts +88 -0
  171. package/src/three/tests/pr3PerFrameCpuCuts.test.ts +370 -0
  172. package/src/three/tests/signTextureCache.test.ts +4 -4
  173. package/src/three/threeJsMedia.ts +55 -72
  174. package/src/three/threeJsParticles.ts +32 -41
  175. package/src/three/threeJsSound.ts +3 -3
  176. package/src/three/threeJsUtils.ts +3 -3
  177. package/src/three/threeWorker.ts +2 -2
  178. package/src/three/waypointSprite.ts +55 -82
  179. package/src/three/waypoints.ts +20 -23
  180. package/src/three/world/cursorBlock.ts +25 -12
  181. package/src/three/world/vr.ts +11 -17
  182. package/src/three/worldGeometryExport.ts +15 -34
  183. package/src/three/worldRendererThree.ts +131 -133
  184. package/src/wasm-mesher/bridge/convertChunk.ts +3 -9
  185. package/src/wasm-mesher/bridge/render-from-wasm.ts +204 -229
  186. package/src/wasm-mesher/bridge/shaderCubeBridge.ts +26 -67
  187. package/src/wasm-mesher/tests/cullingRegression.test.ts +307 -0
  188. package/src/wasm-mesher/tests/heightmapParity.test.ts +8 -18
  189. package/src/wasm-mesher/tests/mesherWasmConversionCache.test.ts +2 -2
  190. package/src/wasm-mesher/tests/sectionRaycastAabb.test.ts +17 -4
  191. package/src/wasm-mesher/tests/shaderCubeInstances.test.ts +103 -141
  192. package/src/wasm-mesher/tests/splitColumnWasmOutput.test.ts +14 -22
  193. package/src/wasm-mesher/worker/mesherWasm.ts +150 -141
  194. package/src/wasm-mesher/worker/mesherWasmConversionCache.ts +2 -8
  195. package/src/wasm-mesher/worker/mesherWasmLightDirty.test.ts +1 -3
  196. package/src/wasm-mesher/worker/mesherWasmLightDirty.ts +1 -5
  197. package/src/wasm-mesher/worker/mesherWasmRequestTracker.ts +42 -42
  198. package/src/worldView/worldView.spiral.test.ts +1 -1
  199. package/src/worldView/worldView.ts +34 -39
  200. package/src/worldView/worldViewWorkerBridge.test.ts +4 -4
@@ -9,13 +9,7 @@ import { handleGetHeightmap, EMPTY_COLUMN_HEIGHTMAP_SENTINEL } from '../../meshe
9
9
  import { collectBlockEntityMetadata, type SignMeta, type HeadMeta, type BannerMeta } from '../../mesher-shared/blockEntityMetadata'
10
10
  import { SectionRequestTracker } from './mesherWasmRequestTracker'
11
11
  import { sectionYsForLightColumnDirty } from './mesherWasmLightDirty'
12
- import {
13
- CONVERSION_CACHE_LIMIT,
14
- clearConversionCache,
15
- getOrConvertColumn,
16
- invalidateConversion,
17
- setConversionCacheLimit,
18
- } from './mesherWasmConversionCache'
12
+ import { CONVERSION_CACHE_LIMIT, clearConversionCache, getOrConvertColumn, invalidateConversion, setConversionCacheLimit } from './mesherWasmConversionCache'
19
13
 
20
14
  let wasm: typeof import('../runtime-build/wasm_mesher.js') | null = null
21
15
  let wasmInitialized = false
@@ -28,13 +22,13 @@ let wasmReady = false // true ONLY after wasm.default() instantiates the module;
28
22
  // (under trees, cliff edges) look brighter than vanilla, and after the
29
23
  // renderer interpolates with neighbour chunks that DO have real light,
30
24
  // the seams look like "local night".
31
- const pendingUpdateLightV17: Array<{ rawPacket: Uint8Array, numSections: number }> = []
25
+ const pendingUpdateLightV17: Array<{ rawPacket: Uint8Array; numSections: number }> = []
32
26
  // Separate v16 pending queue so 1.16 update_light packets that arrive
33
27
  // before WASM is initialised land in the v16 light cache (not v17) on
34
28
  // drain — the mesh hot path looks them up per protocol family.
35
29
  const pendingUpdateLightV16: Array<{ rawPacket: Uint8Array }> = []
36
30
 
37
- function processUpdateLightV17 (rawPacket: Uint8Array, numSections: number): void {
31
+ function processUpdateLightV17(rawPacket: Uint8Array, numSections: number): void {
38
32
  if (!wasmReady) {
39
33
  pendingUpdateLightV17.push({ rawPacket, numSections })
40
34
  return
@@ -46,7 +40,7 @@ function processUpdateLightV17 (rawPacket: Uint8Array, numSections: number): voi
46
40
  const skyLight = parsed.skyLight as Uint8Array
47
41
  updateLightV17Cache.set(rawCacheKey(x, z), {
48
42
  skyLight,
49
- blockLight: parsed.blockLight as Uint8Array,
43
+ blockLight: parsed.blockLight as Uint8Array
50
44
  })
51
45
  invalidateConversion(x, z)
52
46
  dirtyColumnSectionsForLightUpdate(x, z)
@@ -60,7 +54,7 @@ function processUpdateLightV17 (rawPacket: Uint8Array, numSections: number): voi
60
54
  // families fully isolated — the mesh tick picks v16 vs v17 by which raw
61
55
  // chunk cache has the entry, and crossing the streams could mismatch a
62
56
  // stale 1.17 column with 1.16 light or vice versa during version switches.
63
- function processUpdateLightV16 (rawPacket: Uint8Array): void {
57
+ function processUpdateLightV16(rawPacket: Uint8Array): void {
64
58
  if (!wasmReady) {
65
59
  pendingUpdateLightV16.push({ rawPacket })
66
60
  return
@@ -71,7 +65,7 @@ function processUpdateLightV16 (rawPacket: Uint8Array): void {
71
65
  const z = (parsed.z as number) * 16
72
66
  updateLightV16Cache.set(rawCacheKey(x, z), {
73
67
  skyLight: parsed.skyLight as Uint8Array,
74
- blockLight: parsed.blockLight as Uint8Array,
68
+ blockLight: parsed.blockLight as Uint8Array
75
69
  })
76
70
  invalidateConversion(x, z)
77
71
  dirtyColumnSectionsForLightUpdate(x, z)
@@ -85,7 +79,7 @@ async function initWasm() {
85
79
  try {
86
80
  wasmInitialized = true
87
81
  wasm = await import('../runtime-build/wasm_mesher.js')
88
- await wasm.default('/wasm_mesher_bg.wasm') as any
82
+ ;(await wasm.default('/wasm_mesher_bg.wasm')) as any
89
83
  wasmReady = true // instance is now usable; drained packets below will pass the guard
90
84
 
91
85
  if (pendingUpdateLightV17.length > 0) {
@@ -100,23 +94,22 @@ async function initWasm() {
100
94
  for (const item of queue) processUpdateLightV16(item.rawPacket)
101
95
  }
102
96
  } catch (err) {
103
- console.error(
104
- '[WASM Mesher] Failed to initialize WASM mesher — block lighting may stay at full brightness:',
105
- err,
106
- )
97
+ console.error('[WASM Mesher] Failed to initialize WASM mesher — block lighting may stay at full brightness:', err)
107
98
  wasmInitialized = true // Don't try to initialize again
108
99
  // Don't throw - allow worker to continue without WASM (will fail on first use)
109
100
  }
110
101
  }
111
102
 
112
- globalThis.structuredClone ??= (value) => JSON.parse(JSON.stringify(value))
103
+ globalThis.structuredClone ??= value => JSON.parse(JSON.stringify(value))
113
104
 
114
105
  if (globalThis.module && module.require) {
115
106
  // If we are in a node environment, we need to fake some env variables
116
107
  const r = module.require
117
108
  const { parentPort } = r('worker_threads')
118
109
  global.self = parentPort
119
- global.postMessage = (value, transferList) => { parentPort.postMessage(value, transferList) }
110
+ global.postMessage = (value, transferList) => {
111
+ parentPort.postMessage(value, transferList)
112
+ }
120
113
  global.performance = r('perf_hooks').performance
121
114
  }
122
115
 
@@ -154,7 +147,10 @@ const postMessage = (data: any, transferList: any[] = []) => {
154
147
 
155
148
  function drainQueue(from: number, to: number) {
156
149
  const messages = queuedMessages.slice(from, to)
157
- global.postMessage(messages.map(m => m.data), messages.flatMap(m => m.transferList) as unknown as string)
150
+ global.postMessage(
151
+ messages.map(m => m.data),
152
+ messages.flatMap(m => m.transferList) as unknown as string
153
+ )
158
154
  queuedMessages = queuedMessages.slice(to)
159
155
  }
160
156
 
@@ -165,7 +161,7 @@ function drainQueue(from: number, to: number) {
165
161
  // contract violation (`WorldRendererCommon` would throw on the main thread)
166
162
  // and we surface it via `console.warn` so it shows up in dev/CI without
167
163
  // killing the worker.
168
- const emitSectionFinished = (payload: { type: 'sectionFinished', key: string } & Record<string, any>) => {
164
+ const emitSectionFinished = (payload: { type: 'sectionFinished'; key: string } & Record<string, any>) => {
169
165
  const consumed = requestTracker.consumeOne(payload.key)
170
166
  if (!consumed) {
171
167
  console.warn(`[WASM Mesher] sectionFinished for non-requested key ${payload.key} (column-mode contract violation)`)
@@ -205,7 +201,7 @@ function setSectionDirty(pos: Vec3, value = true) {
205
201
  }
206
202
 
207
203
  /** Re-mesh every section in a column after `update_light` updates the light cache. */
208
- function dirtyColumnSectionsForLightUpdate (x: number, z: number) {
204
+ function dirtyColumnSectionsForLightUpdate(x: number, z: number) {
209
205
  const worldMinY = config?.worldMinY ?? 0
210
206
  const worldMaxY = config?.worldMaxY ?? 256
211
207
  for (const y of sectionYsForLightColumnDirty(worldMinY, worldMaxY, SECTION_HEIGHT)) {
@@ -275,10 +271,7 @@ const updateLightV16Cache = new Map<string, UpdateLightV17Entry>()
275
271
 
276
272
  // Mirrors `convertChunkToWasm`'s output (same layout: x + z*16 + y*256,
277
273
  // y outer) so it can be dropped straight into `generate_geometry`.
278
- const convertRawMapChunkToWasm = (
279
- raw: RawMapChunkEntry,
280
- version: string
281
- ): ChunkConversionResult | null => {
274
+ const convertRawMapChunkToWasm = (raw: RawMapChunkEntry, version: string): ChunkConversionResult | null => {
282
275
  if (!wasm || !(wasm as any).parseMapChunkV18Plus) return null
283
276
  // 1.18 introduced the new chunk format; on earlier protocols the packet
284
277
  // shape differs and our parser would throw. Fall back to the JS path.
@@ -289,13 +282,7 @@ const convertRawMapChunkToWasm = (
289
282
  const MAX_BITS_PER_BIOME = 3
290
283
  let parsed: any
291
284
  try {
292
- parsed = (wasm as any).parseMapChunkV18Plus(
293
- raw.rawPacket,
294
- raw.numSections,
295
- MAX_BITS_PER_BLOCK,
296
- MAX_BITS_PER_BIOME,
297
- raw.protocol
298
- )
285
+ parsed = (wasm as any).parseMapChunkV18Plus(raw.rawPacket, raw.numSections, MAX_BITS_PER_BLOCK, MAX_BITS_PER_BIOME, raw.protocol)
299
286
  } catch (err) {
300
287
  console.warn('[WASM Mesher] parseMapChunkV18Plus failed, falling back:', err)
301
288
  return null
@@ -316,7 +303,7 @@ const convertRawMapChunkToWasm = (
316
303
  noAoBlocks: meta.noAoBlocks,
317
304
  cullIdenticalBlocks: meta.cullIdenticalBlocks,
318
305
  occludingBlocks: meta.occludingBlocks,
319
- blockCount,
306
+ blockCount
320
307
  }
321
308
  }
322
309
 
@@ -324,11 +311,7 @@ const convertRawMapChunkToWasm = (
324
311
  // (expanded from the 4×4×4 cell layout). Light comes from the paired
325
312
  // `update_light` cache when available; otherwise we fall back to full
326
313
  // daylight (sky=15) and no block light so geometry stays visible.
327
- const convertParsedV17ToWasm = (
328
- entry: ParsedV17Entry,
329
- lightEntry: UpdateLightV17Entry | undefined,
330
- version: string
331
- ): ChunkConversionResult | null => {
314
+ const convertParsedV17ToWasm = (entry: ParsedV17Entry, lightEntry: UpdateLightV17Entry | undefined, version: string): ChunkConversionResult | null => {
332
315
  if (!wasm || !(wasm as any).parseChunkSectionsV16V17) return null
333
316
  // Empty `Int32Array` signals "no biomes captured" — WASM falls back to
334
317
  // `default_biome` for every block. Plains (id 1) matches the JS path.
@@ -336,14 +319,7 @@ const convertParsedV17ToWasm = (
336
319
  const DEFAULT_BIOME = 1
337
320
  let parsed: any
338
321
  try {
339
- parsed = (wasm as any).parseChunkSectionsV16V17(
340
- entry.chunkData,
341
- entry.bitMapLoHi,
342
- entry.numSections,
343
- entry.maxBitsPerBlock,
344
- biomesCells,
345
- DEFAULT_BIOME,
346
- )
322
+ parsed = (wasm as any).parseChunkSectionsV16V17(entry.chunkData, entry.bitMapLoHi, entry.numSections, entry.maxBitsPerBlock, biomesCells, DEFAULT_BIOME)
347
323
  } catch (err) {
348
324
  console.warn('[WASM Mesher] parseChunkSectionsV16V17 failed, falling back:', err)
349
325
  return null
@@ -376,7 +352,7 @@ const convertParsedV17ToWasm = (
376
352
  noAoBlocks: meta.noAoBlocks,
377
353
  cullIdenticalBlocks: meta.cullIdenticalBlocks,
378
354
  occludingBlocks: meta.occludingBlocks,
379
- blockCount,
355
+ blockCount
380
356
  }
381
357
  }
382
358
 
@@ -386,11 +362,7 @@ const convertParsedV17ToWasm = (
386
362
  // defaults — anything else means a non-vanilla server we don't support
387
363
  // on the fast path, in which case we return null and fall back to the
388
364
  // JS column-walk via `convertChunkToWasm`.
389
- const convertParsedV16ToWasm = (
390
- entry: ParsedV16Entry,
391
- lightEntry: UpdateLightV17Entry | undefined,
392
- version: string
393
- ): ChunkConversionResult | null => {
365
+ const convertParsedV16ToWasm = (entry: ParsedV16Entry, lightEntry: UpdateLightV17Entry | undefined, version: string): ChunkConversionResult | null => {
394
366
  if (!wasm || !(wasm as any).parseChunkSectionsV16V17) return null
395
367
  const NUM_SECTIONS = 16
396
368
  const MAX_BITS_PER_BLOCK = 15
@@ -402,14 +374,7 @@ const convertParsedV16ToWasm = (
402
374
  const biomesCells = entry.biomes ?? new Int32Array(0)
403
375
  let parsed: any
404
376
  try {
405
- parsed = (wasm as any).parseChunkSectionsV16V17(
406
- entry.chunkData,
407
- bitMapLoHi,
408
- NUM_SECTIONS,
409
- MAX_BITS_PER_BLOCK,
410
- biomesCells,
411
- DEFAULT_BIOME,
412
- )
377
+ parsed = (wasm as any).parseChunkSectionsV16V17(entry.chunkData, bitMapLoHi, NUM_SECTIONS, MAX_BITS_PER_BLOCK, biomesCells, DEFAULT_BIOME)
413
378
  } catch (err) {
414
379
  console.warn('[WASM Mesher] parseChunkSectionsV16V17 (v16) failed, falling back:', err)
415
380
  return null
@@ -442,7 +407,7 @@ const convertParsedV16ToWasm = (
442
407
  noAoBlocks: meta.noAoBlocks,
443
408
  cullIdenticalBlocks: meta.cullIdenticalBlocks,
444
409
  occludingBlocks: meta.occludingBlocks,
445
- blockCount,
410
+ blockCount
446
411
  }
447
412
  }
448
413
 
@@ -473,8 +438,12 @@ const meshColumnFromRawV18Plus = (
473
438
  MAX_BITS_PER_BLOCK,
474
439
  MAX_BITS_PER_BIOME,
475
440
  raw.protocol,
476
- x, worldMinY, z, columnHeight,
477
- worldMinY, worldMaxY,
441
+ x,
442
+ worldMinY,
443
+ z,
444
+ columnHeight,
445
+ worldMinY,
446
+ worldMaxY,
478
447
  worldMinY,
479
448
  meta.invisibleBlocks,
480
449
  meta.transparentBlocks,
@@ -519,8 +488,12 @@ const meshColumnFromParsedV16V17 = (
519
488
  defaultBiome,
520
489
  skyLight ?? new Uint8Array(0),
521
490
  blockLight ?? new Uint8Array(0),
522
- x, worldMinY, z, columnHeight,
523
- worldMinY, worldMaxY,
491
+ x,
492
+ worldMinY,
493
+ z,
494
+ columnHeight,
495
+ worldMinY,
496
+ worldMaxY,
524
497
  worldMinY,
525
498
  meta.invisibleBlocks,
526
499
  meta.transparentBlocks,
@@ -543,7 +516,7 @@ const meshColumnFromParsedV16V17 = (
543
516
  // ---------------------------------------------------------------------------
544
517
 
545
518
  const meshMultiColumnsFromRawV18Plus = (
546
- chunksToUse: Array<{ x: number, z: number, chunk: any }>,
519
+ chunksToUse: Array<{ x: number; z: number; chunk: any }>,
547
520
  x: number,
548
521
  z: number,
549
522
  worldMinY: number,
@@ -580,8 +553,12 @@ const meshMultiColumnsFromRawV18Plus = (
580
553
  protocol,
581
554
  chunkXs,
582
555
  chunkZs,
583
- x, worldMinY, z, columnHeight,
584
- worldMinY, worldMaxY,
556
+ x,
557
+ worldMinY,
558
+ z,
559
+ columnHeight,
560
+ worldMinY,
561
+ worldMaxY,
585
562
  worldMinY,
586
563
  meta.invisibleBlocks,
587
564
  meta.transparentBlocks,
@@ -599,7 +576,7 @@ const meshMultiColumnsFromRawV18Plus = (
599
576
  }
600
577
 
601
578
  const meshMultiColumnsFromParsedV16V17 = (
602
- chunksToUse: Array<{ x: number, z: number, chunk: any }>,
579
+ chunksToUse: Array<{ x: number; z: number; chunk: any }>,
603
580
  x: number,
604
581
  z: number,
605
582
  worldMinY: number,
@@ -680,8 +657,12 @@ const meshMultiColumnsFromParsedV16V17 = (
680
657
  blockLightList,
681
658
  chunkXs,
682
659
  chunkZs,
683
- x, worldMinY, z, columnHeight,
684
- worldMinY, worldMaxY,
660
+ x,
661
+ worldMinY,
662
+ z,
663
+ columnHeight,
664
+ worldMinY,
665
+ worldMaxY,
685
666
  worldMinY,
686
667
  meta.invisibleBlocks,
687
668
  meta.transparentBlocks,
@@ -768,10 +749,7 @@ const handleMessage = async (data: any) => {
768
749
  }
769
750
  if (!hasAnySection) {
770
751
  const emptyHeightmap = new Int16Array(256).fill(EMPTY_COLUMN_HEIGHTMAP_SENTINEL)
771
- postMessage(
772
- { type: 'heightmap', key: `${data.x >> 4},${data.z >> 4}`, heightmap: emptyHeightmap },
773
- [emptyHeightmap.buffer]
774
- )
752
+ postMessage({ type: 'heightmap', key: `${data.x >> 4},${data.z >> 4}`, heightmap: emptyHeightmap }, [emptyHeightmap.buffer])
775
753
  }
776
754
  break
777
755
  }
@@ -828,7 +806,7 @@ const handleMessage = async (data: any) => {
828
806
  rawMapChunkCache.set(rawCacheKey(data.x, data.z), {
829
807
  rawPacket: data.rawPacket as Uint8Array,
830
808
  protocol: data.protocol as number,
831
- numSections: data.numSections as number,
809
+ numSections: data.numSections as number
832
810
  })
833
811
  invalidateConversion(data.x, data.z)
834
812
  break
@@ -841,7 +819,7 @@ const handleMessage = async (data: any) => {
841
819
  maxBitsPerBlock: data.maxBitsPerBlock as number,
842
820
  chunkData: data.chunkData as Uint8Array,
843
821
  bitMapLoHi: data.bitMapLoHi as Uint32Array,
844
- biomes: data.biomes as Int32Array | undefined,
822
+ biomes: data.biomes as Int32Array | undefined
845
823
  })
846
824
  invalidateConversion(data.x, data.z)
847
825
  break
@@ -865,7 +843,7 @@ const handleMessage = async (data: any) => {
865
843
  protocol: data.protocol as number,
866
844
  chunkData: data.chunkData as Uint8Array,
867
845
  bitMap: data.bitMap as number,
868
- biomes: data.biomes as Int32Array,
846
+ biomes: data.biomes as Int32Array
869
847
  })
870
848
  invalidateConversion(data.x, data.z)
871
849
  break
@@ -898,7 +876,7 @@ const handleMessage = async (data: any) => {
898
876
  type: 'mc-web-pong',
899
877
  workerIndex: replyWorkerIndex,
900
878
  t: data.t,
901
- recvAt: typeof performance !== 'undefined' ? performance.now() : undefined,
879
+ recvAt: typeof performance !== 'undefined' ? performance.now() : undefined
902
880
  })
903
881
  break
904
882
  }
@@ -908,7 +886,7 @@ const handleMessage = async (data: any) => {
908
886
  type: 'mc-web-pong',
909
887
  workerIndex: replyWorkerIndex,
910
888
  t: data.t,
911
- recvAt: typeof performance !== 'undefined' ? performance.now() : undefined,
889
+ recvAt: typeof performance !== 'undefined' ? performance.now() : undefined
912
890
  })
913
891
  break
914
892
  }
@@ -918,7 +896,7 @@ const handleMessage = async (data: any) => {
918
896
  type: 'mc-web-pong',
919
897
  workerIndex: replyWorkerIndex,
920
898
  t: data.t,
921
- recvAt: typeof performance !== 'undefined' ? performance.now() : undefined,
899
+ recvAt: typeof performance !== 'undefined' ? performance.now() : undefined
922
900
  })
923
901
  break
924
902
  }
@@ -958,11 +936,10 @@ self.onmessage = ({ data }) => {
958
936
  // Section height is always 16 in column mode (the only WASM path).
959
937
  const getSectionHeight = () => SECTION_HEIGHT
960
938
 
961
-
962
939
  // 3x3 X/Z neighbor set for column meshing. Y-agnostic because full-column
963
940
  // meshing converts the entire world Y range in one go.
964
941
  function collectChunksForColumn(x: number, z: number) {
965
- const result = [] as Array<{ x: number, z: number, chunk: any }>
942
+ const result = [] as Array<{ x: number; z: number; chunk: any }>
966
943
  const target = world.getColumn(x, z)
967
944
  if (target) result.push({ x, z, chunk: target })
968
945
  const offsets = [-16, 0, 16]
@@ -1005,7 +982,7 @@ function makeEmptyColumnGeometry(sx: number, sy: number, sz: number, sectionHeig
1005
982
  signs: {},
1006
983
  banners: {},
1007
984
  hadErrors,
1008
- blocksCount: 0,
985
+ blocksCount: 0
1009
986
  }
1010
987
  }
1011
988
 
@@ -1023,7 +1000,7 @@ function processColumnTick() {
1023
1000
  // coords — the same units used by section keys). This guarantees a
1024
1001
  // single WASM call per column per tick even when multiple section keys
1025
1002
  // of the same column are dirty.
1026
- const groups = new Map<string, { x: number, z: number, sections: Array<{ key: string, x: number, y: number, z: number, count: number }> }>()
1003
+ const groups = new Map<string, { x: number; z: number; sections: Array<{ key: string; x: number; y: number; z: number; count: number }> }>()
1027
1004
  for (const [key, count] of dirtySections) {
1028
1005
  const [sx, sy, sz] = key.split(',').map(v => parseInt(v, 10))
1029
1006
  const colKey = `${sx},${sz}`
@@ -1040,7 +1017,7 @@ function processColumnTick() {
1040
1017
  const { x, z, sections } = group
1041
1018
  const targetChunk = world.getColumn(x, z)
1042
1019
 
1043
- let exportedMap: Map<string, { exported: import('../../three/worldGeometryExport').ExportedSection, blocksCount: number }> | null = null
1020
+ let exportedMap: Map<string, { exported: import('../../three/worldGeometryExport').ExportedSection; blocksCount: number }> | null = null
1044
1021
  let processTime = 0
1045
1022
  let prePhase = 0
1046
1023
  let wasmPhase = 0
@@ -1093,20 +1070,38 @@ function processColumnTick() {
1093
1070
  } else if (v17Entry) {
1094
1071
  const v17Light = updateLightV17Cache.get(rawCacheKey(x, z))
1095
1072
  wasmResult = meshColumnFromParsedV16V17(
1096
- v17Entry.chunkData, v17Entry.bitMapLoHi, v17Entry.numSections, v17Entry.maxBitsPerBlock,
1097
- v17Entry.biomes, 1,
1098
- v17Light?.skyLight ?? null, v17Light?.blockLight ?? null,
1099
- x, z, worldMinY, worldMaxY, meta
1073
+ v17Entry.chunkData,
1074
+ v17Entry.bitMapLoHi,
1075
+ v17Entry.numSections,
1076
+ v17Entry.maxBitsPerBlock,
1077
+ v17Entry.biomes,
1078
+ 1,
1079
+ v17Light?.skyLight ?? null,
1080
+ v17Light?.blockLight ?? null,
1081
+ x,
1082
+ z,
1083
+ worldMinY,
1084
+ worldMaxY,
1085
+ meta
1100
1086
  )
1101
1087
  if (wasmResult) columnMeshPath = 'v17_fused'
1102
1088
  } else if (v16Entry) {
1103
1089
  const v16Light = updateLightV16Cache.get(rawCacheKey(x, z))
1104
1090
  const bitMapLoHi = new Uint32Array([v16Entry.bitMap >>> 0, 0])
1105
1091
  wasmResult = meshColumnFromParsedV16V17(
1106
- v16Entry.chunkData, bitMapLoHi, 16, 15,
1107
- v16Entry.biomes, 1,
1108
- v16Light?.skyLight ?? null, v16Light?.blockLight ?? null,
1109
- x, z, worldMinY, worldMaxY, meta
1092
+ v16Entry.chunkData,
1093
+ bitMapLoHi,
1094
+ 16,
1095
+ 15,
1096
+ v16Entry.biomes,
1097
+ 1,
1098
+ v16Light?.skyLight ?? null,
1099
+ v16Light?.blockLight ?? null,
1100
+ x,
1101
+ z,
1102
+ worldMinY,
1103
+ worldMaxY,
1104
+ meta
1110
1105
  )
1111
1106
  if (wasmResult) columnMeshPath = 'v16_fused'
1112
1107
  }
@@ -1126,8 +1121,9 @@ function processColumnTick() {
1126
1121
  // or any helper returns null.
1127
1122
  // ------------------------------------------------------------------
1128
1123
  if (!wasmResult && chunkCount > 1) {
1129
- wasmResult = meshMultiColumnsFromRawV18Plus(chunksToUse, x, z, worldMinY, worldMaxY, meta)
1130
- ?? meshMultiColumnsFromParsedV16V17(chunksToUse, x, z, worldMinY, worldMaxY, meta)
1124
+ wasmResult =
1125
+ meshMultiColumnsFromRawV18Plus(chunksToUse, x, z, worldMinY, worldMaxY, meta) ??
1126
+ meshMultiColumnsFromParsedV16V17(chunksToUse, x, z, worldMinY, worldMaxY, meta)
1131
1127
  if (wasmResult) {
1132
1128
  columnMeshPath = 'multi_fused'
1133
1129
  usedFusedPath = true
@@ -1168,7 +1164,12 @@ function processColumnTick() {
1168
1164
  // JS-fallback (column walk) — still cached, since this is the
1169
1165
  // expensive path the conversion cache was built for.
1170
1166
  const cached = getOrConvertColumn(
1171
- cx, cz, chunk, version, worldMinY, worldMaxY,
1167
+ cx,
1168
+ cz,
1169
+ chunk,
1170
+ version,
1171
+ worldMinY,
1172
+ worldMaxY,
1172
1173
  () => convertChunkToWasm(chunk, version, cx, cz, worldMinY, worldMaxY),
1173
1174
  chunk
1174
1175
  )
@@ -1188,23 +1189,28 @@ function processColumnTick() {
1188
1189
  return conv
1189
1190
  })
1190
1191
 
1191
- const {
1192
- invisibleBlocks,
1193
- transparentBlocks,
1194
- noAoBlocks,
1195
- cullIdenticalBlocks,
1196
- occludingBlocks,
1197
- } = conversions[0]
1192
+ const { invisibleBlocks, transparentBlocks, noAoBlocks, cullIdenticalBlocks, occludingBlocks } = conversions[0]
1198
1193
 
1199
1194
  if (chunkCount === 1 || !(wasm as any).generate_geometry_multi) {
1200
1195
  const { blockStates, blockLight, skyLight, biomesArray } = conversions[0]
1201
1196
  t1 = performance.now()
1202
1197
  wasmResult = wasm.generate_geometry(
1203
- x, worldMinY, z, columnHeight,
1204
- worldMinY, worldMaxY,
1198
+ x,
1199
+ worldMinY,
1200
+ z,
1201
+ columnHeight,
1202
+ worldMinY,
1203
+ worldMaxY,
1205
1204
  worldMinY,
1206
- blockStates, blockLight, skyLight, biomesArray,
1207
- invisibleBlocks, transparentBlocks, noAoBlocks, cullIdenticalBlocks, occludingBlocks,
1205
+ blockStates,
1206
+ blockLight,
1207
+ skyLight,
1208
+ biomesArray,
1209
+ invisibleBlocks,
1210
+ transparentBlocks,
1211
+ noAoBlocks,
1212
+ cullIdenticalBlocks,
1213
+ occludingBlocks,
1208
1214
  config?.enableLighting !== false,
1209
1215
  config?.smoothLighting !== false,
1210
1216
  config?.skyLight ?? 15
@@ -1233,12 +1239,24 @@ function processColumnTick() {
1233
1239
 
1234
1240
  t1 = performance.now()
1235
1241
  wasmResult = (wasm as any).generate_geometry_multi(
1236
- x, worldMinY, z, columnHeight,
1237
- worldMinY, worldMaxY,
1242
+ x,
1238
1243
  worldMinY,
1239
- xs, zs,
1240
- blockStatesAll, blockLightAll, skyLightAll, biomesAll,
1241
- invisibleBlocks, transparentBlocks, noAoBlocks, cullIdenticalBlocks, occludingBlocks,
1244
+ z,
1245
+ columnHeight,
1246
+ worldMinY,
1247
+ worldMaxY,
1248
+ worldMinY,
1249
+ xs,
1250
+ zs,
1251
+ blockStatesAll,
1252
+ blockLightAll,
1253
+ skyLightAll,
1254
+ biomesAll,
1255
+ invisibleBlocks,
1256
+ transparentBlocks,
1257
+ noAoBlocks,
1258
+ cullIdenticalBlocks,
1259
+ occludingBlocks,
1242
1260
  config?.enableLighting !== false,
1243
1261
  config?.smoothLighting !== false,
1244
1262
  config?.skyLight ?? 15
@@ -1256,16 +1274,12 @@ function processColumnTick() {
1256
1274
  // intentionally skipped (the request tracker would warn if we
1257
1275
  // emitted sectionFinished for them).
1258
1276
  const requestedSectionKeys = sections.map(s => ({ x: s.x, y: s.y, z: s.z }))
1259
- exportedMap = splitColumnWasmOutputToSections(
1260
- wasmResult,
1261
- requestedSectionKeys,
1262
- {
1263
- version,
1264
- world,
1265
- sectionHeight,
1266
- shaderCubes: config?.shaderCubeBlocks === true,
1267
- },
1268
- )
1277
+ exportedMap = splitColumnWasmOutputToSections(wasmResult, requestedSectionKeys, {
1278
+ version,
1279
+ world,
1280
+ sectionHeight,
1281
+ shaderCubes: config?.shaderCubeBlocks === true
1282
+ })
1269
1283
 
1270
1284
  // Push heightmap from the WASM column output. With column meshing as
1271
1285
  // the only WASM path, the main thread does not request heightmaps
@@ -1313,7 +1327,7 @@ function processColumnTick() {
1313
1327
  // finalized — otherwise the totals attached to the first event
1314
1328
  // would miss the typed-array allocation, block-entity walk, and
1315
1329
  // postMessage cost of every section in this column.
1316
- const finished: Array<{ key: string, count: number }> = []
1330
+ const finished: Array<{ key: string; count: number }> = []
1317
1331
  for (const s of sections) {
1318
1332
  const { key, x: sx, y: sy, z: sz, count } = s
1319
1333
 
@@ -1346,9 +1360,7 @@ function processColumnTick() {
1346
1360
  const hasLegacyMesh = hasOpaqueMesh || hasBlendMesh
1347
1361
  const hasShaderCubes = (exported?.shaderCubes?.count ?? 0) > 0
1348
1362
  if (exported && (hasLegacyMesh || hasShaderCubes)) {
1349
- const maxIndex = exported.geometry.indices.length > 0
1350
- ? Math.max(...exported.geometry.indices)
1351
- : 0
1363
+ const maxIndex = exported.geometry.indices.length > 0 ? Math.max(...exported.geometry.indices) : 0
1352
1364
  const using32Array = maxIndex > 65535
1353
1365
  geometry = {
1354
1366
  sectionYNumber: (sy - (config?.worldMinY || 0)) >> 4,
@@ -1368,9 +1380,7 @@ function processColumnTick() {
1368
1380
  skyLights: new Float32Array(exported.geometry.skyLights),
1369
1381
  blockLights: new Float32Array(exported.geometry.blockLights),
1370
1382
  uvs: new Float32Array(exported.geometry.uvs),
1371
- indices: using32Array
1372
- ? new Uint32Array(exported.geometry.indices)
1373
- : new Uint16Array(exported.geometry.indices),
1383
+ indices: using32Array ? new Uint32Array(exported.geometry.indices) : new Uint16Array(exported.geometry.indices),
1374
1384
  indicesCount: exported.geometry.indices.length,
1375
1385
  using32Array,
1376
1386
  tiles: {},
@@ -1383,13 +1393,13 @@ function processColumnTick() {
1383
1393
  // `B:` debug overlay stat) and matches the per-section path's
1384
1394
  // semantics: number of blocks that contributed faces to this
1385
1395
  // section's geometry.
1386
- blocksCount: sectionBlocksCount,
1396
+ blocksCount: sectionBlocksCount
1387
1397
  }
1388
1398
  if (exported.shaderCubes) {
1389
1399
  geometry.shaderCubes = {
1390
1400
  words: new Uint32Array(exported.shaderCubes.words),
1391
1401
  count: exported.shaderCubes.count,
1392
- formatVersion: 3,
1402
+ formatVersion: 3
1393
1403
  }
1394
1404
  }
1395
1405
  if (exported.blendGeometry && hasBlendMesh) {
@@ -1401,9 +1411,7 @@ function processColumnTick() {
1401
1411
  skyLights: new Float32Array(exported.blendGeometry.skyLights),
1402
1412
  blockLights: new Float32Array(exported.blendGeometry.blockLights),
1403
1413
  uvs: new Float32Array(exported.blendGeometry.uvs),
1404
- indices: blendMax > 65535
1405
- ? new Uint32Array(exported.blendGeometry.indices)
1406
- : new Uint16Array(exported.blendGeometry.indices),
1414
+ indices: blendMax > 65535 ? new Uint32Array(exported.blendGeometry.indices) : new Uint16Array(exported.blendGeometry.indices)
1407
1415
  }
1408
1416
  }
1409
1417
  transferable = [
@@ -1423,14 +1431,15 @@ function processColumnTick() {
1423
1431
  geometry.blend?.uvs?.buffer,
1424
1432
  //@ts-ignore
1425
1433
  geometry.blend?.indices?.buffer,
1426
- geometry.shaderCubes?.words?.buffer,
1434
+ geometry.shaderCubes?.words?.buffer
1427
1435
  ].filter(Boolean)
1428
1436
 
1429
1437
  if (exported.geometry.indices.length > 0 && config.computeWireframeEdges) {
1430
1438
  try {
1431
- const wireframeF32 = geometry.indices instanceof Uint32Array
1432
- ? wasm!.computeWireframeEdges(geometry.positions as Float32Array, geometry.indices)
1433
- : wasm!.computeWireframeEdgesU16(geometry.positions as Float32Array, geometry.indices as Uint16Array)
1439
+ const wireframeF32 =
1440
+ geometry.indices instanceof Uint32Array
1441
+ ? wasm!.computeWireframeEdges(geometry.positions as Float32Array, geometry.indices)
1442
+ : wasm!.computeWireframeEdgesU16(geometry.positions as Float32Array, geometry.indices as Uint16Array)
1434
1443
  if (wireframeF32.length > 0) {
1435
1444
  geometry.wireframePositions = wireframeF32
1436
1445
  transferable.push(wireframeF32.buffer)
@@ -1488,7 +1497,7 @@ function processColumnTick() {
1488
1497
  preTypedArrayBuild: !attributed ? preTypedArrayBuild : 0,
1489
1498
  preOther: !attributed ? preOther : 0,
1490
1499
  preCacheHits: !attributed ? preCacheHits : 0,
1491
- preCacheMisses: !attributed ? preCacheMisses : 0,
1500
+ preCacheMisses: !attributed ? preCacheMisses : 0
1492
1501
  })
1493
1502
  attributed = true
1494
1503
  }
@@ -78,13 +78,7 @@ export function getOrConvertColumn(
78
78
  return { result: convert(), hit: false }
79
79
  }
80
80
  const e = cache.get(k)
81
- if (
82
- e
83
- && e.chunkRef === chunkRef
84
- && e.version === version
85
- && e.worldMinY === worldMinY
86
- && e.worldMaxY === worldMaxY
87
- ) {
81
+ if (e && e.chunkRef === chunkRef && e.version === version && e.worldMinY === worldMinY && e.worldMaxY === worldMaxY) {
88
82
  // Defense-in-depth: if the live world ref no longer matches the stored
89
83
  // ref, the explicit invalidation path was missed somewhere upstream.
90
84
  if (liveChunkRef !== undefined && liveChunkRef !== e.chunkRef) {
@@ -142,7 +136,7 @@ export function getConversionCacheSize(): number {
142
136
  return cache.size
143
137
  }
144
138
 
145
- export function consumeConversionCacheStats(): { hits: number, misses: number } {
139
+ export function consumeConversionCacheStats(): { hits: number; misses: number } {
146
140
  const r = { hits, misses }
147
141
  hits = 0
148
142
  misses = 0