minecraft-renderer 0.1.79 → 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 +27 -27
  6. package/dist/minecraft-renderer.js.meta.json +1 -1
  7. package/dist/threeWorker.js +133 -133
  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 +239 -282
  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 +167 -100
  126. package/src/three/globalLegacyBuffer.ts +257 -166
  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 +10 -16
  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 +34 -34
  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 +300 -41
  170. package/src/three/tests/legacyMultiDraw.test.ts +8 -29
  171. package/src/three/tests/pr3PerFrameCpuCuts.test.ts +106 -29
  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 +117 -129
  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,7 +9,15 @@ import { proxy, subscribe } from 'valtio'
9
9
  import type { ResourcesManagerTransferred } from '../resourcesManager/resourcesManager'
10
10
  import { dynamicMcDataFiles } from './buildSharedConfig.mjs'
11
11
  import { DisplayWorldOptions, GraphicsInitOptions, RendererReactiveState, SoundSystem } from '../graphicsBackend/types'
12
- import { HighestBlockInfo, CustomBlockModels, BlockStateModelInfo, getBlockAssetsCacheKey, MesherConfig, MesherMainEvent, SECTION_HEIGHT } from '../mesher-shared/shared'
12
+ import {
13
+ HighestBlockInfo,
14
+ CustomBlockModels,
15
+ BlockStateModelInfo,
16
+ getBlockAssetsCacheKey,
17
+ MesherConfig,
18
+ MesherMainEvent,
19
+ SECTION_HEIGHT
20
+ } from '../mesher-shared/shared'
13
21
  import { chunkPos } from './simpleUtils'
14
22
  import { addNewStat, MC_RENDERER_DEBUG_OVERLAY_CLASS, removeAllStats, updatePanesVisibility, updateStatText } from './ui/newStats'
15
23
  import { getPlayerStateUtils } from '../graphicsBackend/playerState'
@@ -43,7 +51,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
43
51
  chunksRenderBelowEnabled: false,
44
52
  chunksRenderDistanceOverride: undefined as number | undefined,
45
53
  chunksRenderDistanceEnabled: false,
46
- disableEntities: false,
54
+ disableEntities: false
47
55
  // disableParticles: false
48
56
  })
49
57
 
@@ -90,7 +98,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
90
98
  isProcessingQueue = false
91
99
  ONMESSAGE_TIME_LIMIT = 30 // ms
92
100
 
93
- handleResize = () => { }
101
+ handleResize = () => {}
94
102
  highestBlocksByChunks = new Map<string, { [chunkKey: string]: HighestBlockInfo }>()
95
103
  blockEntities = {}
96
104
 
@@ -131,7 +139,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
131
139
  private mesherPoolSnapshot = {
132
140
  mesherWorkers: -1,
133
141
  wasmMesher: false,
134
- dedicatedChangeWorker: false,
142
+ dedicatedChangeWorker: false
135
143
  }
136
144
  private mesherReconfigureQueue: Promise<void> = Promise.resolve()
137
145
  private heightmapDebounceTimers = new Map<string, ReturnType<typeof setTimeout>>()
@@ -203,7 +211,11 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
203
211
  return (this.initOptions.config.statsVisible ?? 0) > 1
204
212
  }
205
213
 
206
- constructor(public readonly resourcesManager: ResourcesManagerTransferred, public displayOptions: DisplayWorldOptions, public initOptions: GraphicsInitOptions) {
214
+ constructor(
215
+ public readonly resourcesManager: ResourcesManagerTransferred,
216
+ public displayOptions: DisplayWorldOptions,
217
+ public initOptions: GraphicsInitOptions
218
+ ) {
207
219
  this.snapshotInitialValues()
208
220
  this.worldRendererConfig = displayOptions.inWorldRenderingConfig
209
221
  this.playerStateReactive = displayOptions.playerStateReactive!
@@ -270,12 +282,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
270
282
  if (!this.active) return
271
283
  await this.updateAssetsData()
272
284
  }
273
- bindAbortableEmitterListener(
274
- this.resourcesManager,
275
- 'assetsTexturesUpdated',
276
- onAssetsTexturesUpdated,
277
- this.abortController.signal
278
- )
285
+ bindAbortableEmitterListener(this.resourcesManager, 'assetsTexturesUpdated', onAssetsTexturesUpdated, this.abortController.signal)
279
286
 
280
287
  this.watchReactivePlayerState()
281
288
  this.watchReactiveConfig()
@@ -283,7 +290,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
283
290
  this.worldReadyResolvers.resolve()
284
291
  }
285
292
 
286
- snapshotInitialValues() { }
293
+ snapshotInitialValues() {}
287
294
 
288
295
  wasChunkSentToWorker(chunkKey: string) {
289
296
  return this.loadedChunks[chunkKey]
@@ -305,7 +312,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
305
312
  }
306
313
  }
307
314
 
308
- async getBlockInfo(blockPos: { x: number, y: number, z: number }, stateId: number) {
315
+ async getBlockInfo(blockPos: { x: number; y: number; z: number }, stateId: number) {
309
316
  const CHUNK_SIZE = 16
310
317
  const chunkKey = `${Math.floor(blockPos.x / CHUNK_SIZE) * CHUNK_SIZE},${Math.floor(blockPos.z / CHUNK_SIZE) * CHUNK_SIZE}`
311
318
  const customBlockName = this.protocolCustomBlocks.get(chunkKey)?.[`${blockPos.x},${blockPos.y},${blockPos.z}`]
@@ -323,14 +330,17 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
323
330
 
324
331
  private createMesherWorker() {
325
332
  const script = this.getMesherWorkerScript()
326
- return initMesherWorker((data) => {
327
- if (Array.isArray(data)) {
328
- this.messageQueue.push(...data)
329
- } else {
330
- this.messageQueue.push(data)
331
- }
332
- void this.processMessageQueue('worker')
333
- }, script === 'wasm' ? 'mesherWasm.js' : 'mesher.js')
333
+ return initMesherWorker(
334
+ data => {
335
+ if (Array.isArray(data)) {
336
+ this.messageQueue.push(...data)
337
+ } else {
338
+ this.messageQueue.push(data)
339
+ }
340
+ void this.processMessageQueue('worker')
341
+ },
342
+ script === 'wasm' ? 'mesherWasm.js' : 'mesher.js'
343
+ )
334
344
  }
335
345
 
336
346
  initWorkers(numWorkers = this.worldRendererConfig.mesherWorkers) {
@@ -343,7 +353,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
343
353
  this.mesherPoolSnapshot = {
344
354
  mesherWorkers: this.worldRendererConfig.mesherWorkers,
345
355
  wasmMesher: this.worldRendererConfig.wasmMesher,
346
- dedicatedChangeWorker: this.worldRendererConfig.dedicatedChangeWorker,
356
+ dedicatedChangeWorker: this.worldRendererConfig.dedicatedChangeWorker
347
357
  }
348
358
  }
349
359
 
@@ -353,11 +363,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
353
363
  const tryReconfigure = () => {
354
364
  const cfg = this.worldRendererConfig
355
365
  const snap = this.mesherPoolSnapshot
356
- if (
357
- cfg.mesherWorkers === snap.mesherWorkers &&
358
- cfg.wasmMesher === snap.wasmMesher &&
359
- cfg.dedicatedChangeWorker === snap.dedicatedChangeWorker
360
- ) {
366
+ if (cfg.mesherWorkers === snap.mesherWorkers && cfg.wasmMesher === snap.wasmMesher && cfg.dedicatedChangeWorker === snap.dedicatedChangeWorker) {
361
367
  return
362
368
  }
363
369
  this.syncMesherPoolSnapshot()
@@ -365,16 +371,14 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
365
371
  }
366
372
 
367
373
  for (const key of ['mesherWorkers', 'wasmMesher', 'dedicatedChangeWorker'] as const) {
368
- this.valtioUnsubs.push(
369
- this.onReactiveConfigUpdated(key, tryReconfigure, false)
370
- )
374
+ this.valtioUnsubs.push(this.onReactiveConfigUpdated(key, tryReconfigure, false))
371
375
  }
372
376
  }
373
377
 
374
378
  private enqueueMesherWorkersReconfigure() {
375
379
  this.mesherReconfigureQueue = this.mesherReconfigureQueue
376
380
  .then(() => this.reconfigureMesherWorkers())
377
- .catch((err) => {
381
+ .catch(err => {
378
382
  console.error('[Mesher] Failed to reconfigure workers:', err)
379
383
  })
380
384
  }
@@ -450,7 +454,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
450
454
 
451
455
  onReactiveConfigUpdated<T extends keyof typeof this.worldRendererConfig>(
452
456
  key: T,
453
- callback: (value: typeof this.worldRendererConfig[T]) => void,
457
+ callback: (value: (typeof this.worldRendererConfig)[T]) => void,
454
458
  initial = true
455
459
  ) {
456
460
  if (initial) {
@@ -462,19 +466,19 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
462
466
  return subscribeKey(this.worldRendererConfig, key, callback)
463
467
  }
464
468
 
465
- onReactiveDebugUpdated<T extends keyof typeof this.reactiveDebugParams>(key: T, callback: (value: typeof this.reactiveDebugParams[T]) => void) {
469
+ onReactiveDebugUpdated<T extends keyof typeof this.reactiveDebugParams>(key: T, callback: (value: (typeof this.reactiveDebugParams)[T]) => void) {
466
470
  callback(this.reactiveDebugParams[key])
467
471
  subscribeKey(this.reactiveDebugParams, key, callback)
468
472
  }
469
473
 
470
474
  watchReactivePlayerState() {
471
475
  this.valtioUnsubs.push(
472
- this.onReactivePlayerStateUpdated('backgroundColor', (value) => {
476
+ this.onReactivePlayerStateUpdated('backgroundColor', value => {
473
477
  this.changeBackgroundColor(value)
474
478
  })
475
479
  )
476
480
  this.valtioUnsubs.push(
477
- this.onReactivePlayerStateUpdated('cardinalLight', (value) => {
481
+ this.onReactivePlayerStateUpdated('cardinalLight', value => {
478
482
  this.changeCardinalLight(value)
479
483
  })
480
484
  )
@@ -482,7 +486,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
482
486
 
483
487
  watchReactiveConfig() {
484
488
  this.valtioUnsubs.push(
485
- this.onReactiveConfigUpdated('fetchPlayerSkins', (value) => {
489
+ this.onReactiveConfigUpdated('fetchPlayerSkins', value => {
486
490
  setSkinsConfig({ apiEnabled: value })
487
491
  })
488
492
  )
@@ -491,7 +495,12 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
491
495
  async processMessageQueue(source: string) {
492
496
  if (this.isProcessingQueue || this.messageQueue.length === 0) return
493
497
  this.logWorkerWork(`# ${source} processing queue`)
494
- if (this.lastRendered && performance.now() - this.lastRendered > this.ONMESSAGE_TIME_LIMIT && this.worldRendererConfig._experimentalSmoothChunkLoading && this.renderingActive) {
498
+ if (
499
+ this.lastRendered &&
500
+ performance.now() - this.lastRendered > this.ONMESSAGE_TIME_LIMIT &&
501
+ this.worldRendererConfig._experimentalSmoothChunkLoading &&
502
+ this.renderingActive
503
+ ) {
495
504
  const start = performance.now()
496
505
  await new Promise(resolve => {
497
506
  requestAnimationFrame(resolve)
@@ -512,7 +521,10 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
512
521
  }
513
522
 
514
523
  // Check if we've exceeded the time limit
515
- if (processingStopped || (performance.now() - startTime > this.ONMESSAGE_TIME_LIMIT && this.renderingActive && this.worldRendererConfig._experimentalSmoothChunkLoading)) {
524
+ if (
525
+ processingStopped ||
526
+ (performance.now() - startTime > this.ONMESSAGE_TIME_LIMIT && this.renderingActive && this.worldRendererConfig._experimentalSmoothChunkLoading)
527
+ ) {
516
528
  // If we have more messages and exceeded time limit, schedule next batch
517
529
  if (this.messageQueue.length > 0) {
518
530
  requestAnimationFrame(async () => {
@@ -544,7 +556,8 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
544
556
  const chunkCoords = data.key.split(',').map(Number)
545
557
  this.lastChunkDistance = Math.max(...this.getDistance(new Vec3(chunkCoords[0], 0, chunkCoords[2])))
546
558
  }
547
- if (data.type === 'sectionFinished') { // on after load & unload section
559
+ if (data.type === 'sectionFinished') {
560
+ // on after load & unload section
548
561
  this.logWorkerWork(`<- ${data.workerIndex} sectionFinished ${data.key} ${JSON.stringify({ processTime: data.processTime })}`)
549
562
  if (!this.sectionsWaiting.has(data.key)) {
550
563
  console.debug(`sectionFinished for non-outstanding section ${data.key} (viewDistance=${this.viewDistance})`)
@@ -558,7 +571,8 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
558
571
 
559
572
  const chunkCoords = data.key.split(',').map(Number)
560
573
  const chunkKey = `${chunkCoords[0]},${chunkCoords[2]}`
561
- if (this.loadedChunks[chunkKey]) { // ensure chunk data was added, not a neighbor chunk update
574
+ if (this.loadedChunks[chunkKey]) {
575
+ // ensure chunk data was added, not a neighbor chunk update
562
576
  let loaded = true
563
577
  const sectionHeight = this.getSectionHeight()
564
578
  for (let y = this.worldMinYRender; y < this.worldSizeParams.worldHeight; y += sectionHeight) {
@@ -601,15 +615,15 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
601
615
  this.renderUpdateEmitter.emit('update')
602
616
  if (data.processTime) {
603
617
  this.workersProcessAverageTimeCount++
604
- this.workersProcessAverageTime = ((this.workersProcessAverageTime * (this.workersProcessAverageTimeCount - 1)) + data.processTime) / this.workersProcessAverageTimeCount
618
+ this.workersProcessAverageTime =
619
+ (this.workersProcessAverageTime * (this.workersProcessAverageTimeCount - 1) + data.processTime) / this.workersProcessAverageTimeCount
605
620
  this.maxWorkersProcessTime = Math.max(this.maxWorkersProcessTime, data.processTime)
606
621
  }
607
- if (typeof data.pre === 'number' && typeof data.wasm === 'number' && typeof data.post === 'number'
608
- && (data.pre > 0 || data.wasm > 0 || data.post > 0)) {
622
+ if (typeof data.pre === 'number' && typeof data.wasm === 'number' && typeof data.post === 'number' && (data.pre > 0 || data.wasm > 0 || data.post > 0)) {
609
623
  const n = ++this.workersPhaseSampleCount
610
- this.workersPreAverageTime = ((this.workersPreAverageTime * (n - 1)) + data.pre) / n
611
- this.workersWasmAverageTime = ((this.workersWasmAverageTime * (n - 1)) + data.wasm) / n
612
- this.workersPostAverageTime = ((this.workersPostAverageTime * (n - 1)) + data.post) / n
624
+ this.workersPreAverageTime = (this.workersPreAverageTime * (n - 1) + data.pre) / n
625
+ this.workersWasmAverageTime = (this.workersWasmAverageTime * (n - 1) + data.wasm) / n
626
+ this.workersPostAverageTime = (this.workersPostAverageTime * (n - 1) + data.post) / n
613
627
  // Pre-stage substages — additive schema; treat undefined as 0 so
614
628
  // events from older mesher builds don't poison the running mean.
615
629
  const ptc = typeof data.preTargetConvert === 'number' ? data.preTargetConvert : 0
@@ -619,11 +633,11 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
619
633
  const po = typeof data.preOther === 'number' ? data.preOther : 0
620
634
  const pch = typeof data.preCacheHits === 'number' ? data.preCacheHits : 0
621
635
  const pcm = typeof data.preCacheMisses === 'number' ? data.preCacheMisses : 0
622
- this.workersPreTargetConvertAverageTime = ((this.workersPreTargetConvertAverageTime * (n - 1)) + ptc) / n
623
- this.workersPreNeighborConvertAverageTime = ((this.workersPreNeighborConvertAverageTime * (n - 1)) + pnc) / n
624
- this.workersPreNeighborCountAverage = ((this.workersPreNeighborCountAverage * (n - 1)) + pncn) / n
625
- this.workersPreTypedArrayBuildAverageTime = ((this.workersPreTypedArrayBuildAverageTime * (n - 1)) + ptab) / n
626
- this.workersPreOtherAverageTime = ((this.workersPreOtherAverageTime * (n - 1)) + po) / n
636
+ this.workersPreTargetConvertAverageTime = (this.workersPreTargetConvertAverageTime * (n - 1) + ptc) / n
637
+ this.workersPreNeighborConvertAverageTime = (this.workersPreNeighborConvertAverageTime * (n - 1) + pnc) / n
638
+ this.workersPreNeighborCountAverage = (this.workersPreNeighborCountAverage * (n - 1) + pncn) / n
639
+ this.workersPreTypedArrayBuildAverageTime = (this.workersPreTypedArrayBuildAverageTime * (n - 1) + ptab) / n
640
+ this.workersPreOtherAverageTime = (this.workersPreOtherAverageTime * (n - 1) + po) / n
627
641
  this.workersPreCacheHitsTotal += pch
628
642
  this.workersPreCacheMissesTotal += pcm
629
643
  if (this.worldRendererConfig.debugWasmPerf && n % WorldRendererCommon.PHASE_PERF_LOG_INTERVAL === 0) {
@@ -641,7 +655,9 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
641
655
  const cacheTotal = this.workersPreCacheHitsTotal + this.workersPreCacheMissesTotal
642
656
  const cacheHitPct = cacheTotal > 0 ? (this.workersPreCacheHitsTotal / cacheTotal) * 100 : 0
643
657
  // eslint-disable-next-line no-console
644
- console.log(`[wasm-mesher perf] n=${n} pre=${this.workersPreAverageTime.toFixed(2)}ms (${prePct.toFixed(1)}%) wasm=${this.workersWasmAverageTime.toFixed(2)}ms (${wasmPct.toFixed(1)}%) post=${this.workersPostAverageTime.toFixed(2)}ms (${postPct.toFixed(1)}%) | pre.targetConvert=${this.workersPreTargetConvertAverageTime.toFixed(2)}ms (${tgtPct.toFixed(1)}%) pre.neighborConvert=${this.workersPreNeighborConvertAverageTime.toFixed(2)}ms (${nbrPct.toFixed(1)}%) [n̄=${nbrCnt.toFixed(2)}, per-nbr=${nbrPerAvg.toFixed(2)}ms] pre.typedArrayBuild=${this.workersPreTypedArrayBuildAverageTime.toFixed(2)}ms (${tabPct.toFixed(1)}%) pre.other=${this.workersPreOtherAverageTime.toFixed(2)}ms (${othPct.toFixed(1)}%) | pre.cache hits=${this.workersPreCacheHitsTotal} misses=${this.workersPreCacheMissesTotal} (${cacheHitPct.toFixed(1)}% hit)`)
658
+ console.log(
659
+ `[wasm-mesher perf] n=${n} pre=${this.workersPreAverageTime.toFixed(2)}ms (${prePct.toFixed(1)}%) wasm=${this.workersWasmAverageTime.toFixed(2)}ms (${wasmPct.toFixed(1)}%) post=${this.workersPostAverageTime.toFixed(2)}ms (${postPct.toFixed(1)}%) | pre.targetConvert=${this.workersPreTargetConvertAverageTime.toFixed(2)}ms (${tgtPct.toFixed(1)}%) pre.neighborConvert=${this.workersPreNeighborConvertAverageTime.toFixed(2)}ms (${nbrPct.toFixed(1)}%) [n̄=${nbrCnt.toFixed(2)}, per-nbr=${nbrPerAvg.toFixed(2)}ms] pre.typedArrayBuild=${this.workersPreTypedArrayBuildAverageTime.toFixed(2)}ms (${tabPct.toFixed(1)}%) pre.other=${this.workersPreOtherAverageTime.toFixed(2)}ms (${othPct.toFixed(1)}%) | pre.cache hits=${this.workersPreCacheHitsTotal} misses=${this.workersPreCacheMissesTotal} (${cacheHitPct.toFixed(1)}% hit)`
660
+ )
645
661
  }
646
662
  }
647
663
  }
@@ -679,7 +695,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
679
695
  this.updateChunksStats()
680
696
  }
681
697
 
682
- changeHandSwingingState(isAnimationPlaying: boolean, isLeftHand: boolean): void { }
698
+ changeHandSwingingState(isAnimationPlaying: boolean, isLeftHand: boolean): void {}
683
699
 
684
700
  abstract handleWorkerMessage(data: WorkerReceive): void
685
701
 
@@ -769,7 +785,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
769
785
 
770
786
  getMesherConfig(): MesherConfig {
771
787
  const timeOfDay = this.timeOfTheDay
772
- const skyLight = (timeOfDay < 0 || timeOfDay > 24_000) ? 15 : calculateSkyLightSimple(timeOfDay)
788
+ const skyLight = timeOfDay < 0 || timeOfDay > 24_000 ? 15 : calculateSkyLightSimple(timeOfDay)
773
789
  return {
774
790
  version: this.version,
775
791
  enableLighting: this.worldRendererConfig.enableLighting,
@@ -786,7 +802,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
786
802
  worldMaxY: this.worldMinYRender + this.worldSizeParams.worldHeight,
787
803
  disableConversionCache: this.worldRendererConfig.disableMesherConversionCache,
788
804
  computeWireframeEdges: this.worldRendererConfig.futuristicReveal === true,
789
- shaderCubeBlocks: this.isShaderCubeBlocksEnabled(),
805
+ shaderCubeBlocks: this.isShaderCubeBlocksEnabled()
790
806
  }
791
807
  }
792
808
 
@@ -810,7 +826,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
810
826
  latest: resources.blocksAtlasJson
811
827
  },
812
828
  blockstatesModels,
813
- config: this.getMesherConfig(),
829
+ config: this.getMesherConfig()
814
830
  })
815
831
  }
816
832
 
@@ -868,10 +884,13 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
868
884
  this.workers[0].postMessage({
869
885
  type: 'getHeightmap',
870
886
  x,
871
- z,
887
+ z
872
888
  })
873
889
  }
874
- this.logWorkerWork(() => `-> chunk ${JSON.stringify({ x, z, chunkLength: chunk.length, customBlockModelsLength: customBlockModels ? Object.keys(customBlockModels).length : 0 })}`)
890
+ this.logWorkerWork(
891
+ () =>
892
+ `-> chunk ${JSON.stringify({ x, z, chunkLength: chunk.length, customBlockModelsLength: customBlockModels ? Object.keys(customBlockModels).length : 0 })}`
893
+ )
875
894
  this.mesherLogReader?.chunkReceived(x, z, chunk.length)
876
895
  const sectionHeight = this.getSectionHeight()
877
896
  const CHUNK_SIZE = 16
@@ -933,7 +952,9 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
933
952
  const sectionKey = `${x},${y},${z}`
934
953
  const waitingCount = this.sectionsWaiting.get(sectionKey)
935
954
  if (waitingCount !== undefined && waitingCount > 0) {
936
- console.debug(`[removeColumn] clearing non-zero sectionsWaiting for ${sectionKey}: ${waitingCount} (chunk ${x},${z}, viewDistance=${this.viewDistance})`)
955
+ console.debug(
956
+ `[removeColumn] clearing non-zero sectionsWaiting for ${sectionKey}: ${waitingCount} (chunk ${x},${z}, viewDistance=${this.viewDistance})`
957
+ )
937
958
  }
938
959
  this.sectionsWaiting.delete(sectionKey)
939
960
  delete this.finishedSections[sectionKey]
@@ -971,126 +992,206 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
971
992
  void set()
972
993
  }
973
994
 
974
- updateEntity(e: any, isUpdate = false) { }
995
+ updateEntity(e: any, isUpdate = false) {}
975
996
 
976
997
  abstract updatePlayerEntity?(e: any): void
977
998
 
978
- lightUpdate(chunkX: number, chunkZ: number) { }
999
+ lightUpdate(chunkX: number, chunkZ: number) {}
979
1000
 
980
1001
  connect(worldView: WorldViewWorker) {
981
1002
  const worldEmitter = worldView
982
1003
  const signal = this.abortController.signal
983
1004
 
984
- bindAbortableListener(worldEmitter, 'entity', (e) => {
985
- this.updateEntity(e, false)
986
- }, signal)
987
- bindAbortableListener(worldEmitter, 'entityMoved', (e) => {
988
- this.updateEntity(e, true)
989
- }, signal)
990
- bindAbortableListener(worldEmitter, 'playerEntity', (e) => {
991
- this.updatePlayerEntity?.(e)
992
- }, signal)
1005
+ bindAbortableListener(
1006
+ worldEmitter,
1007
+ 'entity',
1008
+ e => {
1009
+ this.updateEntity(e, false)
1010
+ },
1011
+ signal
1012
+ )
1013
+ bindAbortableListener(
1014
+ worldEmitter,
1015
+ 'entityMoved',
1016
+ e => {
1017
+ this.updateEntity(e, true)
1018
+ },
1019
+ signal
1020
+ )
1021
+ bindAbortableListener(
1022
+ worldEmitter,
1023
+ 'playerEntity',
1024
+ e => {
1025
+ this.updatePlayerEntity?.(e)
1026
+ },
1027
+ signal
1028
+ )
993
1029
 
994
1030
  let currentLoadChunkBatch = null as {
995
1031
  timeout
996
1032
  data
997
1033
  } | null
998
- bindAbortableListener(worldEmitter, 'loadChunk', ({ x, z, chunk, worldConfig, isLightUpdate }) => {
999
- this.worldSizeParams = worldConfig
1000
- this.queuedChunks.add(`${x},${z}`)
1001
- const args = [x, z, chunk, isLightUpdate]
1002
- if (!currentLoadChunkBatch) {
1003
- // add a setting to use debounce instead
1004
- currentLoadChunkBatch = {
1005
- data: [],
1006
- timeout: setTimeout(() => {
1007
- for (const args of currentLoadChunkBatch!.data) {
1008
- this.queuedChunks.delete(`${args[0]},${args[1]}`)
1009
- this.addColumn(...args as Parameters<typeof this.addColumn>)
1010
- }
1011
- for (const fn of this.queuedFunctions) {
1012
- fn()
1013
- }
1014
- this.queuedFunctions = []
1015
- currentLoadChunkBatch = null
1016
- }, this.worldRendererConfig.addChunksBatchWaitTime)
1034
+ bindAbortableListener(
1035
+ worldEmitter,
1036
+ 'loadChunk',
1037
+ ({ x, z, chunk, worldConfig, isLightUpdate }) => {
1038
+ this.worldSizeParams = worldConfig
1039
+ this.queuedChunks.add(`${x},${z}`)
1040
+ const args = [x, z, chunk, isLightUpdate]
1041
+ if (!currentLoadChunkBatch) {
1042
+ // add a setting to use debounce instead
1043
+ currentLoadChunkBatch = {
1044
+ data: [],
1045
+ timeout: setTimeout(() => {
1046
+ for (const args of currentLoadChunkBatch!.data) {
1047
+ this.queuedChunks.delete(`${args[0]},${args[1]}`)
1048
+ this.addColumn(...(args as Parameters<typeof this.addColumn>))
1049
+ }
1050
+ for (const fn of this.queuedFunctions) {
1051
+ fn()
1052
+ }
1053
+ this.queuedFunctions = []
1054
+ currentLoadChunkBatch = null
1055
+ }, this.worldRendererConfig.addChunksBatchWaitTime)
1056
+ }
1017
1057
  }
1018
- }
1019
- currentLoadChunkBatch.data.push(args)
1020
- }, signal)
1058
+ currentLoadChunkBatch.data.push(args)
1059
+ },
1060
+ signal
1061
+ )
1021
1062
  // todo remove and use other architecture instead so data flow is clear
1022
- bindAbortableListener(worldEmitter, 'blockEntities', (blockEntities) => {
1023
- this.blockEntities = blockEntities
1024
- }, signal)
1025
-
1026
- bindAbortableListener(worldEmitter, 'unloadChunk', ({ x, z }) => {
1027
- this.removeColumn(x, z)
1028
- }, signal)
1029
-
1030
- bindAbortableListener(worldEmitter, 'blockUpdate', ({ pos, stateId }) => {
1031
- this.setBlockStateId(new Vec3(pos.x, pos.y, pos.z), stateId)
1032
- }, signal)
1033
-
1034
- bindAbortableListener(worldEmitter, 'chunkPosUpdate', ({ pos }) => {
1035
- this.updateViewerPosition(pos)
1036
- }, signal)
1037
-
1038
- bindAbortableListener(worldEmitter, 'end', () => {
1039
- this.worldStop?.()
1040
- }, signal)
1041
-
1042
- bindAbortableListener(worldEmitter, 'renderDistance', (d) => {
1043
- this.viewDistance = d
1044
- this.chunksLength = d === 0 ? 1 : generateSpiralMatrix(d).length
1045
- this.allChunksFinished = Object.keys(this.finishedChunks).length === this.chunksLength
1046
- this.onRenderDistanceChanged?.(d)
1047
- }, signal)
1048
-
1049
- bindAbortableListener(worldEmitter, 'markAsLoaded', ({ x, z }) => {
1050
- this.markAsLoaded(x, z)
1051
- }, signal)
1052
-
1053
- bindAbortableListener(worldEmitter, 'updateLight', ({ pos }) => {
1054
- this.lightUpdate(pos.x, pos.z)
1055
- }, signal)
1056
-
1057
- bindAbortableListener(worldEmitter, 'onWorldSwitch', () => {
1058
- for (const fn of this.onWorldSwitched) {
1059
- try {
1060
- fn()
1061
- } catch (e) {
1062
- setTimeout(() => {
1063
- console.log('[Renderer Backend] Error in onWorldSwitched:')
1064
- throw e
1065
- }, 0)
1063
+ bindAbortableListener(
1064
+ worldEmitter,
1065
+ 'blockEntities',
1066
+ blockEntities => {
1067
+ this.blockEntities = blockEntities
1068
+ },
1069
+ signal
1070
+ )
1071
+
1072
+ bindAbortableListener(
1073
+ worldEmitter,
1074
+ 'unloadChunk',
1075
+ ({ x, z }) => {
1076
+ this.removeColumn(x, z)
1077
+ },
1078
+ signal
1079
+ )
1080
+
1081
+ bindAbortableListener(
1082
+ worldEmitter,
1083
+ 'blockUpdate',
1084
+ ({ pos, stateId }) => {
1085
+ this.setBlockStateId(new Vec3(pos.x, pos.y, pos.z), stateId)
1086
+ },
1087
+ signal
1088
+ )
1089
+
1090
+ bindAbortableListener(
1091
+ worldEmitter,
1092
+ 'chunkPosUpdate',
1093
+ ({ pos }) => {
1094
+ this.updateViewerPosition(pos)
1095
+ },
1096
+ signal
1097
+ )
1098
+
1099
+ bindAbortableListener(
1100
+ worldEmitter,
1101
+ 'end',
1102
+ () => {
1103
+ this.worldStop?.()
1104
+ },
1105
+ signal
1106
+ )
1107
+
1108
+ bindAbortableListener(
1109
+ worldEmitter,
1110
+ 'renderDistance',
1111
+ d => {
1112
+ this.viewDistance = d
1113
+ this.chunksLength = d === 0 ? 1 : generateSpiralMatrix(d).length
1114
+ this.allChunksFinished = Object.keys(this.finishedChunks).length === this.chunksLength
1115
+ this.onRenderDistanceChanged?.(d)
1116
+ },
1117
+ signal
1118
+ )
1119
+
1120
+ bindAbortableListener(
1121
+ worldEmitter,
1122
+ 'markAsLoaded',
1123
+ ({ x, z }) => {
1124
+ this.markAsLoaded(x, z)
1125
+ },
1126
+ signal
1127
+ )
1128
+
1129
+ bindAbortableListener(
1130
+ worldEmitter,
1131
+ 'updateLight',
1132
+ ({ pos }) => {
1133
+ this.lightUpdate(pos.x, pos.z)
1134
+ },
1135
+ signal
1136
+ )
1137
+
1138
+ bindAbortableListener(
1139
+ worldEmitter,
1140
+ 'onWorldSwitch',
1141
+ () => {
1142
+ for (const fn of this.onWorldSwitched) {
1143
+ try {
1144
+ fn()
1145
+ } catch (e) {
1146
+ setTimeout(() => {
1147
+ console.log('[Renderer Backend] Error in onWorldSwitched:')
1148
+ throw e
1149
+ }, 0)
1150
+ }
1066
1151
  }
1067
- }
1068
- }, signal)
1152
+ },
1153
+ signal
1154
+ )
1069
1155
 
1070
- bindAbortableListener(worldEmitter, 'time', (timeOfDay) => {
1071
- if (!this.worldRendererConfig.dayCycle) {
1072
- return
1073
- }
1074
- this.timeUpdated?.(timeOfDay)
1156
+ bindAbortableListener(
1157
+ worldEmitter,
1158
+ 'time',
1159
+ timeOfDay => {
1160
+ if (!this.worldRendererConfig.dayCycle) {
1161
+ return
1162
+ }
1163
+ this.timeUpdated?.(timeOfDay)
1075
1164
 
1076
- this.timeOfTheDay = timeOfDay
1165
+ this.timeOfTheDay = timeOfDay
1077
1166
 
1078
- const skyLight = (timeOfDay < 0 || timeOfDay > 24_000) ? 15 : calculateSkyLightSimple(timeOfDay)
1079
- if (this.lastMesherSkyLight === skyLight) return
1080
- this.lastMesherSkyLight = skyLight
1081
- if (this.workers.length > 0) {
1082
- this.sendWorkers({ config: { skyLight } } as WorkerSend)
1083
- }
1084
- this.onDayCycleSkyLightChanged?.(skyLight)
1085
- }, signal)
1167
+ const skyLight = timeOfDay < 0 || timeOfDay > 24_000 ? 15 : calculateSkyLightSimple(timeOfDay)
1168
+ if (this.lastMesherSkyLight === skyLight) return
1169
+ this.lastMesherSkyLight = skyLight
1170
+ if (this.workers.length > 0) {
1171
+ this.sendWorkers({ config: { skyLight } } as WorkerSend)
1172
+ }
1173
+ this.onDayCycleSkyLightChanged?.(skyLight)
1174
+ },
1175
+ signal
1176
+ )
1086
1177
 
1087
- bindAbortableListener(worldEmitter, 'biomeUpdate', ({ biome }) => {
1088
- this.biomeUpdated?.(biome)
1089
- }, signal)
1178
+ bindAbortableListener(
1179
+ worldEmitter,
1180
+ 'biomeUpdate',
1181
+ ({ biome }) => {
1182
+ this.biomeUpdated?.(biome)
1183
+ },
1184
+ signal
1185
+ )
1090
1186
 
1091
- bindAbortableListener(worldEmitter, 'biomeReset', () => {
1092
- this.biomeReset?.()
1093
- }, signal)
1187
+ bindAbortableListener(
1188
+ worldEmitter,
1189
+ 'biomeReset',
1190
+ () => {
1191
+ this.biomeReset?.()
1192
+ },
1193
+ signal
1194
+ )
1094
1195
  }
1095
1196
 
1096
1197
  setBlockStateIdInner(pos: Vec3, stateId: number | undefined, needAoRecalculation = true) {
@@ -1118,10 +1219,13 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
1118
1219
  if (!this.worldRendererConfig.wasmMesher) {
1119
1220
  const existing = this.heightmapDebounceTimers.get(chunkKey2)
1120
1221
  if (existing) clearTimeout(existing)
1121
- this.heightmapDebounceTimers.set(chunkKey2, setTimeout(() => {
1122
- this.heightmapDebounceTimers.delete(chunkKey2)
1123
- this.workers[0]?.postMessage({ type: 'getHeightmap', x: chunkCornerX, z: chunkCornerZ })
1124
- }, 100))
1222
+ this.heightmapDebounceTimers.set(
1223
+ chunkKey2,
1224
+ setTimeout(() => {
1225
+ this.heightmapDebounceTimers.delete(chunkKey2)
1226
+ this.workers[0]?.postMessage({ type: 'getHeightmap', x: chunkCornerX, z: chunkCornerZ })
1227
+ }, 100)
1228
+ )
1125
1229
  }
1126
1230
  }
1127
1231
  this.logWorkerWork(`-> blockUpdate ${JSON.stringify({ pos, stateId, customBlockModels })}`)
@@ -1132,7 +1236,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
1132
1236
  if ((pos.x & 15) === 0) this.setSectionDirty(pos.offset(-CHUNK_SIZE, 0, 0), true, true)
1133
1237
  if ((pos.x & 15) === 15) this.setSectionDirty(pos.offset(CHUNK_SIZE, 0, 0), true, true)
1134
1238
  if ((pos.y & (sectionHeight - 1)) === 0) this.setSectionDirty(pos.offset(0, -sectionHeight, 0), true, true)
1135
- if ((pos.y & (sectionHeight - 1)) === (sectionHeight - 1)) this.setSectionDirty(pos.offset(0, sectionHeight, 0), true, true)
1239
+ if ((pos.y & (sectionHeight - 1)) === sectionHeight - 1) this.setSectionDirty(pos.offset(0, sectionHeight, 0), true, true)
1136
1240
  if ((pos.z & 15) === 0) this.setSectionDirty(pos.offset(0, 0, -CHUNK_SIZE), true, true)
1137
1241
  if ((pos.z & 15) === 15) this.setSectionDirty(pos.offset(0, 0, CHUNK_SIZE), true, true)
1138
1242
 
@@ -1153,10 +1257,14 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
1153
1257
  if ((pos.z & 15) === 15 && (pos.y & (sectionHeight - 1)) === 0) this.setSectionDirty(pos.offset(0, -sectionHeight, CHUNK_SIZE), true, true)
1154
1258
 
1155
1259
  // x & z neighbors
1156
- if ((pos.y & (sectionHeight - 1)) === 0 && (pos.x & 15) === 0 && (pos.z & 15) === 0) this.setSectionDirty(pos.offset(-CHUNK_SIZE, -sectionHeight, -CHUNK_SIZE), true, true)
1157
- if ((pos.y & (sectionHeight - 1)) === 0 && (pos.x & 15) === 15 && (pos.z & 15) === 0) this.setSectionDirty(pos.offset(CHUNK_SIZE, -sectionHeight, -CHUNK_SIZE), true, true)
1158
- if ((pos.y & (sectionHeight - 1)) === 0 && (pos.x & 15) === 0 && (pos.z & 15) === 15) this.setSectionDirty(pos.offset(-CHUNK_SIZE, -sectionHeight, CHUNK_SIZE), true, true)
1159
- if ((pos.y & (sectionHeight - 1)) === 0 && (pos.x & 15) === 15 && (pos.z & 15) === 15) this.setSectionDirty(pos.offset(CHUNK_SIZE, -sectionHeight, CHUNK_SIZE), true, true)
1260
+ if ((pos.y & (sectionHeight - 1)) === 0 && (pos.x & 15) === 0 && (pos.z & 15) === 0)
1261
+ this.setSectionDirty(pos.offset(-CHUNK_SIZE, -sectionHeight, -CHUNK_SIZE), true, true)
1262
+ if ((pos.y & (sectionHeight - 1)) === 0 && (pos.x & 15) === 15 && (pos.z & 15) === 0)
1263
+ this.setSectionDirty(pos.offset(CHUNK_SIZE, -sectionHeight, -CHUNK_SIZE), true, true)
1264
+ if ((pos.y & (sectionHeight - 1)) === 0 && (pos.x & 15) === 0 && (pos.z & 15) === 15)
1265
+ this.setSectionDirty(pos.offset(-CHUNK_SIZE, -sectionHeight, CHUNK_SIZE), true, true)
1266
+ if ((pos.y & (sectionHeight - 1)) === 0 && (pos.x & 15) === 15 && (pos.z & 15) === 15)
1267
+ this.setSectionDirty(pos.offset(CHUNK_SIZE, -sectionHeight, CHUNK_SIZE), true, true)
1160
1268
  }
1161
1269
  }
1162
1270
  }
@@ -1211,13 +1319,15 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
1211
1319
  async debugGetWorkerCustomBlockModel(pos: Vec3) {
1212
1320
  const data = [] as Array<Promise<string>>
1213
1321
  for (const worker of this.workers) {
1214
- data.push(new Promise((resolve) => {
1215
- worker.addEventListener('message', (e) => {
1216
- if (e.data.type === 'customBlockModel') {
1217
- resolve(e.data.customBlockModel)
1218
- }
1322
+ data.push(
1323
+ new Promise(resolve => {
1324
+ worker.addEventListener('message', e => {
1325
+ if (e.data.type === 'customBlockModel') {
1326
+ resolve(e.data.customBlockModel)
1327
+ }
1328
+ })
1219
1329
  })
1220
- }))
1330
+ )
1221
1331
  worker.postMessage({
1222
1332
  type: 'getCustomBlockModel',
1223
1333
  pos
@@ -1226,7 +1336,8 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
1226
1336
  return Promise.all(data)
1227
1337
  }
1228
1338
 
1229
- setSectionDirty(pos: Vec3, value = true, useChangeWorker = false) { // value false is used for unloading chunks
1339
+ setSectionDirty(pos: Vec3, value = true, useChangeWorker = false) {
1340
+ // value false is used for unloading chunks
1230
1341
  if (!this.forceCallFromMesherReplayer && this.mesherLogReader) return
1231
1342
 
1232
1343
  if (this.viewDistance === -1) throw new Error('viewDistance not set')
@@ -1254,30 +1365,36 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
1254
1365
 
1255
1366
  // Schedule trailing dispatch after throttle window
1256
1367
  if (!this.sectionDirtyTimers.has(key)) {
1257
- this.sectionDirtyTimers.set(key, setTimeout(() => {
1258
- const args = this.sectionDirtyPendingArgs.get(key)
1259
- this.sectionDirtyCount.delete(key)
1260
- this.sectionDirtyTimers.delete(key)
1261
- this.sectionDirtyPendingArgs.delete(key)
1262
- if (args) {
1263
- this._dispatchDirtyImmediate(args.pos, args.value, args.useChangeWorker)
1264
- }
1265
- }, WorldRendererCommon.GEOMETRY_THROTTLE_DELAY))
1368
+ this.sectionDirtyTimers.set(
1369
+ key,
1370
+ setTimeout(() => {
1371
+ const args = this.sectionDirtyPendingArgs.get(key)
1372
+ this.sectionDirtyCount.delete(key)
1373
+ this.sectionDirtyTimers.delete(key)
1374
+ this.sectionDirtyPendingArgs.delete(key)
1375
+ if (args) {
1376
+ this._dispatchDirtyImmediate(args.pos, args.value, args.useChangeWorker)
1377
+ }
1378
+ }, WorldRendererCommon.GEOMETRY_THROTTLE_DELAY)
1379
+ )
1266
1380
  }
1267
1381
  } else {
1268
1382
  // Subsequent requests — throttle: store latest args, existing timer will dispatch
1269
1383
  this.sectionDirtyPendingArgs.set(key, { pos, value, useChangeWorker })
1270
1384
 
1271
1385
  if (!this.sectionDirtyTimers.has(key)) {
1272
- this.sectionDirtyTimers.set(key, setTimeout(() => {
1273
- const args = this.sectionDirtyPendingArgs.get(key)
1274
- this.sectionDirtyCount.delete(key)
1275
- this.sectionDirtyTimers.delete(key)
1276
- this.sectionDirtyPendingArgs.delete(key)
1277
- if (args) {
1278
- this._dispatchDirtyImmediate(args.pos, args.value, args.useChangeWorker)
1279
- }
1280
- }, WorldRendererCommon.GEOMETRY_THROTTLE_DELAY))
1386
+ this.sectionDirtyTimers.set(
1387
+ key,
1388
+ setTimeout(() => {
1389
+ const args = this.sectionDirtyPendingArgs.get(key)
1390
+ this.sectionDirtyCount.delete(key)
1391
+ this.sectionDirtyTimers.delete(key)
1392
+ this.sectionDirtyPendingArgs.delete(key)
1393
+ if (args) {
1394
+ this._dispatchDirtyImmediate(args.pos, args.value, args.useChangeWorker)
1395
+ }
1396
+ }, WorldRendererCommon.GEOMETRY_THROTTLE_DELAY)
1397
+ )
1281
1398
  }
1282
1399
  }
1283
1400
  }
@@ -1302,7 +1419,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
1302
1419
  y: pos.y,
1303
1420
  z: pos.z,
1304
1421
  value,
1305
- config: this.getMesherConfig(),
1422
+ config: this.getMesherConfig()
1306
1423
  })
1307
1424
  } else {
1308
1425
  this.toWorkerMessagesQueue[hash] ??= []
@@ -1312,7 +1429,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
1312
1429
  y: pos.y,
1313
1430
  z: pos.z,
1314
1431
  value,
1315
- config: this.getMesherConfig(),
1432
+ config: this.getMesherConfig()
1316
1433
  })
1317
1434
  this.dispatchMessages()
1318
1435
  }
@@ -1328,7 +1445,9 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
1328
1445
  const messages = this.toWorkerMessagesQueue[workerIndex]
1329
1446
  worker.postMessage(messages)
1330
1447
  for (const message of messages) {
1331
- this.logWorkerWork(`-> ${workerIndex} dispatchMessages ${message.type} ${JSON.stringify({ x: message.x, y: message.y, z: message.z, value: message.value })}`)
1448
+ this.logWorkerWork(
1449
+ `-> ${workerIndex} dispatchMessages ${message.type} ${JSON.stringify({ x: message.x, y: message.y, z: message.z, value: message.value })}`
1450
+ )
1332
1451
  }
1333
1452
  }
1334
1453
  this.toWorkerMessagesQueue = {}
@@ -1436,7 +1555,10 @@ export const initMesherWorker = (onGotMessage: (data: any) => void, workerName =
1436
1555
  worker.onmessage = ({ data }) => {
1437
1556
  onGotMessage(data)
1438
1557
  }
1439
- if (worker.on) worker.on('message', (data) => { worker.onmessage({ data }) })
1558
+ if (worker.on)
1559
+ worker.on('message', data => {
1560
+ worker.onmessage({ data })
1561
+ })
1440
1562
  return worker
1441
1563
  }
1442
1564
 
@@ -1467,19 +1589,24 @@ export const meshersSendMcDataAwait = (
1467
1589
  mcDataFull: IndexedData,
1468
1590
  timeoutMs = 10_000
1469
1591
  ): Promise<void> => {
1470
- return Promise.all(workers.map(worker => new Promise<void>((resolve, reject) => {
1471
- const timeout = setTimeout(() => {
1472
- worker.removeEventListener('message', handler as EventListener)
1473
- reject(new Error(`mcData transfer timeout (${timeoutMs}ms)`))
1474
- }, timeoutMs)
1475
- const handler = ({ data }: MessageEvent) => {
1476
- if (data?.type === 'mcDataApplied') {
1477
- clearTimeout(timeout)
1478
- worker.removeEventListener('message', handler as EventListener)
1479
- resolve()
1480
- }
1481
- }
1482
- worker.addEventListener('message', handler as EventListener)
1483
- meshersSendMcData([worker], version, mcDataKeys, mcDataFull)
1484
- }))).then(() => undefined)
1592
+ return Promise.all(
1593
+ workers.map(
1594
+ worker =>
1595
+ new Promise<void>((resolve, reject) => {
1596
+ const timeout = setTimeout(() => {
1597
+ worker.removeEventListener('message', handler as EventListener)
1598
+ reject(new Error(`mcData transfer timeout (${timeoutMs}ms)`))
1599
+ }, timeoutMs)
1600
+ const handler = ({ data }: MessageEvent) => {
1601
+ if (data?.type === 'mcDataApplied') {
1602
+ clearTimeout(timeout)
1603
+ worker.removeEventListener('message', handler as EventListener)
1604
+ resolve()
1605
+ }
1606
+ }
1607
+ worker.addEventListener('message', handler as EventListener)
1608
+ meshersSendMcData([worker], version, mcDataKeys, mcDataFull)
1609
+ })
1610
+ )
1611
+ ).then(() => undefined)
1485
1612
  }