minecraft-renderer 0.1.106 → 0.1.108
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.
- package/dist/lightOwnerWorker.js +9 -0
- package/dist/lightOwnerWorker.js.map +7 -0
- package/dist/mesher.js +1 -1
- package/dist/mesher.js.map +2 -2
- package/dist/mesherWasm.js +222 -222
- package/dist/minecraft-renderer.js +105 -76
- package/dist/minecraft-renderer.js.meta.json +1 -1
- package/dist/threeWorker.js +537 -508
- package/package.json +1 -1
- package/src/bundler/bundlePrepare.test.ts +25 -0
- package/src/bundler/bundlePrepare.ts +1 -1
- package/src/graphicsBackend/appViewer.ts +15 -0
- package/src/graphicsBackend/config.ts +8 -0
- package/src/graphicsBackend/rendererDefaultOptions.test.ts +51 -0
- package/src/graphicsBackend/rendererDefaultOptions.ts +18 -1
- package/src/graphicsBackend/rendererOptionsSync.ts +4 -3
- package/src/lib/blockEntityLightRegistry.ts +4 -0
- package/src/lib/skyLight.ts +17 -0
- package/src/lib/worldrendererCommon.lightOwner.test.ts +916 -0
- package/src/lib/worldrendererCommon.removeColumn.test.ts +16 -0
- package/src/lib/worldrendererCommon.ts +340 -67
- package/src/lighting/cache/rendererLightCache.test.ts +158 -0
- package/src/lighting/cache/rendererLightCache.ts +248 -0
- package/src/lighting/coordinator/clientLightCoordinator.test.ts +102 -0
- package/src/lighting/coordinator/clientLightCoordinator.ts +574 -0
- package/src/lighting/debug/clientLightProbe.test.ts +24 -0
- package/src/lighting/debug/clientLightProbe.ts +20 -0
- package/src/lighting/debug/clientLightTrace.test.ts +271 -0
- package/src/lighting/debug/clientLightTrace.ts +335 -0
- package/src/lighting/entity/entityLightController.test.ts +216 -0
- package/src/lighting/entity/entityLightController.ts +322 -0
- package/src/lighting/owner/clientLightOwner.test.ts +467 -0
- package/src/lighting/owner/clientLightOwner.ts +523 -0
- package/src/lighting/owner/lightOwnerHost.test.ts +78 -0
- package/src/lighting/owner/lightOwnerHost.ts +202 -0
- package/src/lighting/owner/lightOwnerWorker.ts +154 -0
- package/src/lighting/owner/updateLight1171OwnerPath.test.ts +68 -0
- package/src/lighting/owner/updateLightToOwnerEvents.ts +49 -0
- package/src/lighting/tables/lightTables1171.generated.json +10 -0
- package/src/lighting/tables/lightTables1171.test.ts +168 -0
- package/src/lighting/tables/lightTables1171.ts +59 -0
- package/src/lighting/versioning/clientLightVersions.test.ts +241 -0
- package/src/lighting/versioning/clientLightVersions.ts +203 -0
- package/src/lighting/versioning/pendingSectionFlush.test.ts +214 -0
- package/src/lighting/versioning/pendingSectionFlush.ts +123 -0
- package/src/mesher-shared/lightNibblePack.ts +26 -0
- package/src/mesher-shared/mcDataWorkerShim.cjs +9 -7
- package/src/mesher-shared/modelsGeometryCommon.test.ts +6 -1
- package/src/mesher-shared/shared.ts +48 -1
- package/src/three/chunkMeshManager.ts +26 -0
- package/src/three/entities.ts +97 -8
- package/src/three/entity/EntityMesh.ts +1 -0
- package/src/three/entity/legWalkAnimation.test.ts +154 -0
- package/src/three/entity/legWalkAnimation.ts +193 -0
- package/src/three/globalBlockBuffer.ts +94 -2
- package/src/three/globalLegacyBuffer.ts +94 -2
- package/src/three/graphicsBackendBase.ts +2 -9
- package/src/three/modules/clouds.ts +191 -0
- package/src/three/modules/index.ts +4 -0
- package/src/three/modules/sunMoon.ts +151 -0
- package/src/three/skyboxRenderer.ts +15 -8
- package/src/three/tests/clouds.test.ts +59 -0
- package/src/three/tests/globalBlockBuffer.test.ts +98 -0
- package/src/three/tests/globalLegacyBuffer.test.ts +108 -0
- package/src/three/tests/waterFog.test.ts +25 -0
- package/src/three/waterFog.ts +28 -0
- package/src/three/worldRendererThree.ts +84 -39
- package/src/wasm-mesher/bridge/render-from-wasm.ts +46 -1
- package/src/wasm-mesher/runtime-build/wasm_mesher.d.ts +31 -5
- package/src/wasm-mesher/runtime-build/wasm_mesher.js +180 -5
- package/src/wasm-mesher/runtime-build/wasm_mesher_bg.wasm +0 -0
- package/src/wasm-mesher/runtime-build/wasm_mesher_bg.wasm.d.ts +11 -0
- package/src/wasm-mesher/tests/mesherLightDependencies.test.ts +83 -0
- package/src/wasm-mesher/tests/mesherWasmColumnTickSelect.test.ts +39 -0
- package/src/wasm-mesher/tests/mesherWasmConversionCache.test.ts +30 -1
- package/src/wasm-mesher/tests/mesherWasmTickParseCache.test.ts +73 -0
- package/src/wasm-mesher/tests/mesherWasmTickSchedule.test.ts +51 -0
- package/src/wasm-mesher/tests/mesherWasmTopologyPostCache.test.ts +78 -0
- package/src/wasm-mesher/worker/clientLightDarkPublication.probe.test.ts +32 -0
- package/src/wasm-mesher/worker/mesherLightDependencies.ts +43 -0
- package/src/wasm-mesher/worker/mesherWasm.ownerLightSync.test.ts +198 -0
- package/src/wasm-mesher/worker/mesherWasm.ts +506 -96
- package/src/wasm-mesher/worker/mesherWasmChunkBuffer.ts +1 -0
- package/src/wasm-mesher/worker/mesherWasmColumnTickSelect.ts +47 -0
- package/src/wasm-mesher/worker/mesherWasmConversionCache.ts +16 -5
- package/src/wasm-mesher/worker/mesherWasmLightDirty.test.ts +17 -1
- package/src/wasm-mesher/worker/mesherWasmLightDirty.ts +10 -0
- package/src/wasm-mesher/worker/mesherWasmLightMerge.test.ts +208 -0
- package/src/wasm-mesher/worker/mesherWasmLightMerge.ts +228 -0
- package/src/wasm-mesher/worker/mesherWasmOwnerLight.test.ts +161 -0
- package/src/wasm-mesher/worker/mesherWasmOwnerLight.ts +112 -0
- package/src/wasm-mesher/worker/mesherWasmTickParseCache.ts +45 -0
- package/src/wasm-mesher/worker/mesherWasmTickSchedule.ts +44 -0
- package/src/wasm-mesher/worker/mesherWasmTopologyPostCache.ts +63 -0
- package/src/worldView/types.ts +3 -0
- package/src/worldView/worldView.geometryReload.test.ts +68 -0
- package/src/worldView/worldView.spiral.test.ts +1 -1
- package/src/worldView/worldView.ts +49 -1
|
@@ -34,6 +34,25 @@ import { PlayerStateReactive } from '../playerState/playerState'
|
|
|
34
34
|
import { IndexedData } from 'minecraft-data'
|
|
35
35
|
import { WorldRendererConfig } from '../graphicsBackend/config'
|
|
36
36
|
import { CameraCollisionBlockCache } from '../three/cameraCollisionBlockCache'
|
|
37
|
+
import { RendererLightCache } from '../lighting/cache/rendererLightCache'
|
|
38
|
+
import { ClientLightCoordinator, type ClientLightHost } from '../lighting/coordinator/clientLightCoordinator'
|
|
39
|
+
import {
|
|
40
|
+
bumpClientLightTraceSessionEpoch,
|
|
41
|
+
ingestRemoteClientLightTraceMessage,
|
|
42
|
+
isClientLightTraceEnabled,
|
|
43
|
+
isClientLightTraceMessage,
|
|
44
|
+
maybeEnableClientLightTraceFromLocation,
|
|
45
|
+
recordClientLightTrace,
|
|
46
|
+
setClientLightTraceConfigListener
|
|
47
|
+
} from '../lighting/debug/clientLightTrace'
|
|
48
|
+
import {
|
|
49
|
+
LIGHT_OWNER_WORKER_SCRIPT,
|
|
50
|
+
shouldAcceptMeshGeometry,
|
|
51
|
+
shouldSpawnClientLightOwner,
|
|
52
|
+
skyLightEnabledFromRendererState
|
|
53
|
+
} from '../lighting/owner/clientLightOwner'
|
|
54
|
+
import { shouldDispatchCoveringRemesh, validateMeshResultVersions } from '../lighting/versioning/clientLightVersions'
|
|
55
|
+
import type { FeedChunkPacketPayload } from '../worldView/types'
|
|
37
56
|
|
|
38
57
|
function mod(x, n) {
|
|
39
58
|
return ((x % n) + n) % n
|
|
@@ -47,6 +66,8 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
47
66
|
worldSizeParams = { minY: 0, worldHeight: 256 }
|
|
48
67
|
/** Block columns for third-person voxel DDA (renderer thread). */
|
|
49
68
|
cameraCollisionBlockCache: CameraCollisionBlockCache
|
|
69
|
+
/** Packed block/sky light for entity sampling (renderer thread). */
|
|
70
|
+
rendererLightCache: RendererLightCache
|
|
50
71
|
reactiveDebugParams = proxy({
|
|
51
72
|
stopRendering: false,
|
|
52
73
|
chunksRenderAboveOverride: undefined as number | undefined,
|
|
@@ -86,6 +107,8 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
86
107
|
}>
|
|
87
108
|
customTexturesDataUrl = undefined as string | undefined
|
|
88
109
|
workers: any[] = []
|
|
110
|
+
/** Client light owner session and the version bookkeeping that keeps owner-lit meshes consistent. */
|
|
111
|
+
readonly lightCoordinator = new ClientLightCoordinator(this.createLightHost())
|
|
89
112
|
viewerChunkPosition?: Vec3
|
|
90
113
|
// Last viewer chunk-grid coords for which `onViewerChunkPositionChanged`
|
|
91
114
|
// fired — throttles the hook to chunk-grid changes.
|
|
@@ -125,6 +148,8 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
125
148
|
// Cumulative cache hit/miss counters (column-mode conversion cache).
|
|
126
149
|
workersPreCacheHitsTotal = 0
|
|
127
150
|
workersPreCacheMissesTotal = 0
|
|
151
|
+
workersPreWasmParseHitsTotal = 0
|
|
152
|
+
workersPreWasmParseMissesTotal = 0
|
|
128
153
|
private static readonly PHASE_PERF_LOG_INTERVAL = 64
|
|
129
154
|
geometryReceiveCount = {} as Record<number, number>
|
|
130
155
|
allLoadedIn: undefined | number
|
|
@@ -154,7 +179,8 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
154
179
|
// Geometry throttle: first dirty per section is instant, subsequent within window are grouped
|
|
155
180
|
private sectionDirtyCount = new Map<string, number>()
|
|
156
181
|
private sectionDirtyTimers = new Map<string, ReturnType<typeof setTimeout>>()
|
|
157
|
-
private sectionDirtyPendingArgs = new Map<string, { pos: Vec3; value: boolean; useChangeWorker: boolean }>()
|
|
182
|
+
private sectionDirtyPendingArgs = new Map<string, { pos: Vec3; value: boolean; useChangeWorker: boolean; urgent?: boolean }>()
|
|
183
|
+
private sectionDirtyLightMeta = new Map<string, { lightPublicationVersion: number; worldGeneration?: number }>()
|
|
158
184
|
private static readonly GEOMETRY_THROTTLE_THRESHOLD = 1
|
|
159
185
|
private static readonly GEOMETRY_THROTTLE_DELAY = 100 // ms
|
|
160
186
|
|
|
@@ -224,11 +250,17 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
224
250
|
public initOptions: GraphicsInitOptions
|
|
225
251
|
) {
|
|
226
252
|
this.cameraCollisionBlockCache = new CameraCollisionBlockCache(this.displayOptions.version)
|
|
253
|
+
this.rendererLightCache = new RendererLightCache(this.displayOptions.version)
|
|
227
254
|
this.snapshotInitialValues()
|
|
228
255
|
this.worldRendererConfig = displayOptions.inWorldRenderingConfig
|
|
229
256
|
this.playerStateReactive = displayOptions.playerStateReactive!
|
|
230
257
|
this.playerStateUtils = getPlayerStateUtils(this.playerStateReactive)
|
|
231
258
|
this.reactiveState = displayOptions.rendererState!
|
|
259
|
+
maybeEnableClientLightTraceFromLocation()
|
|
260
|
+
if (isClientLightTraceEnabled()) bumpClientLightTraceSessionEpoch()
|
|
261
|
+
setClientLightTraceConfigListener(() => {
|
|
262
|
+
this.lightCoordinator.syncTraceConfig()
|
|
263
|
+
})
|
|
232
264
|
// this.mesherLogReader = new MesherLogReader(this)
|
|
233
265
|
this.renderUpdateEmitter.on('update', () => {
|
|
234
266
|
const loadedChunks = Object.keys(this.finishedChunks).length
|
|
@@ -355,6 +387,81 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
355
387
|
for (let i = 0; i < numWorkers; i++) {
|
|
356
388
|
this.workers.push(this.createMesherWorker())
|
|
357
389
|
}
|
|
390
|
+
this.lightCoordinator.maybeSpawn()
|
|
391
|
+
this.lightCoordinator.syncTraceConfig()
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
setClientLightProbe(points: Array<[number, number, number]>) {
|
|
395
|
+
this.lightCoordinator.setProbe(points)
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
hasClientLightOwner(): boolean {
|
|
399
|
+
return this.lightCoordinator.hasOwner()
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
getClientLightOwnerWorker(): Worker | null {
|
|
403
|
+
return this.lightCoordinator.getWorker()
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
getClientLightOwnerFailureReason(): string | null {
|
|
407
|
+
return this.lightCoordinator.getFailureReason()
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
columnIncarnationFor(columnKey: string): number {
|
|
411
|
+
return this.lightCoordinator.columnIncarnationFor(columnKey)
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
private createLightHost(): ClientLightHost {
|
|
415
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
416
|
+
const renderer = this
|
|
417
|
+
return {
|
|
418
|
+
get version() {
|
|
419
|
+
return renderer.version
|
|
420
|
+
},
|
|
421
|
+
get workers() {
|
|
422
|
+
return renderer.workers
|
|
423
|
+
},
|
|
424
|
+
get rendererLightCache() {
|
|
425
|
+
return renderer.rendererLightCache
|
|
426
|
+
},
|
|
427
|
+
get worldRendererConfig() {
|
|
428
|
+
return renderer.worldRendererConfig
|
|
429
|
+
},
|
|
430
|
+
get worldSizeParams() {
|
|
431
|
+
return renderer.worldSizeParams
|
|
432
|
+
},
|
|
433
|
+
get playerStateReactive() {
|
|
434
|
+
return renderer.playerStateReactive
|
|
435
|
+
},
|
|
436
|
+
get sectionsWaiting() {
|
|
437
|
+
return renderer.sectionsWaiting
|
|
438
|
+
},
|
|
439
|
+
get loadedChunks() {
|
|
440
|
+
return renderer.loadedChunks
|
|
441
|
+
},
|
|
442
|
+
get sectionDirtyLightMeta() {
|
|
443
|
+
return renderer.sectionDirtyLightMeta
|
|
444
|
+
},
|
|
445
|
+
getSectionHeight: () => renderer.getSectionHeight(),
|
|
446
|
+
getWorkerNumber: (pos, updateAction) => renderer.getWorkerNumber(pos, updateAction),
|
|
447
|
+
setSectionDirty: (pos, value, useChangeWorker) => renderer.setSectionDirty(pos, value, useChangeWorker),
|
|
448
|
+
dispatchDirtyImmediate: (pos, value, useChangeWorker) => renderer._dispatchDirtyImmediate(pos, value, useChangeWorker),
|
|
449
|
+
createLightOwnerWorker: onMessage => initMesherWorker(onMessage, LIGHT_OWNER_WORKER_SCRIPT)
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
feedChunkPacket(payload: FeedChunkPacketPayload) {
|
|
454
|
+
const { kind, ...rest } = payload
|
|
455
|
+
const message = { type: kind, ...rest }
|
|
456
|
+
const isLightPacket = kind === 'setUpdateLightV17' || kind === 'setUpdateLightV16'
|
|
457
|
+
const ownerSession = this.lightCoordinator.session
|
|
458
|
+
if (ownerSession && isLightPacket) {
|
|
459
|
+
ownerSession.pushRawUpdateLight(kind, rest as Record<string, unknown>)
|
|
460
|
+
if (ownerSession.isReady) return
|
|
461
|
+
}
|
|
462
|
+
for (const worker of this.workers) {
|
|
463
|
+
worker.postMessage(message)
|
|
464
|
+
}
|
|
358
465
|
}
|
|
359
466
|
|
|
360
467
|
private syncMesherPoolSnapshot() {
|
|
@@ -402,6 +509,8 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
402
509
|
this.sectionDirtyTimers.clear()
|
|
403
510
|
this.sectionDirtyCount.clear()
|
|
404
511
|
this.sectionDirtyPendingArgs.clear()
|
|
512
|
+
this.sectionDirtyLightMeta.clear()
|
|
513
|
+
this.lightCoordinator.clearPendingMesherState()
|
|
405
514
|
this.reactiveState.world.mesherWork = false
|
|
406
515
|
}
|
|
407
516
|
|
|
@@ -424,6 +533,9 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
424
533
|
if (!this.active) return
|
|
425
534
|
|
|
426
535
|
this.clearMesherPendingState()
|
|
536
|
+
// Worker topology revisions belong to the previous mesher lifetime.
|
|
537
|
+
// Reloaded columns replay owner snapshots through identical ingest.
|
|
538
|
+
this.lightCoordinator.forgetRequiredTopology()
|
|
427
539
|
this.terminateAllMesherWorkers()
|
|
428
540
|
this.initWorkers()
|
|
429
541
|
await this.bootstrapMesherWorkers()
|
|
@@ -490,6 +602,15 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
490
602
|
this.changeCardinalLight(value)
|
|
491
603
|
})
|
|
492
604
|
)
|
|
605
|
+
this.valtioUnsubs.push(
|
|
606
|
+
this.onReactivePlayerStateUpdated(
|
|
607
|
+
'lightingDisabled',
|
|
608
|
+
value => {
|
|
609
|
+
this.lightCoordinator.session?.setSkyLightEnabled(skyLightEnabledFromRendererState({ lightingDisabled: value }))
|
|
610
|
+
},
|
|
611
|
+
false
|
|
612
|
+
)
|
|
613
|
+
)
|
|
493
614
|
}
|
|
494
615
|
|
|
495
616
|
watchReactiveConfig() {
|
|
@@ -498,6 +619,16 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
498
619
|
setSkinsConfig({ apiEnabled: value })
|
|
499
620
|
})
|
|
500
621
|
)
|
|
622
|
+
this.valtioUnsubs.push(
|
|
623
|
+
this.onReactiveConfigUpdated(
|
|
624
|
+
'enableClientLightOwner',
|
|
625
|
+
enabled => {
|
|
626
|
+
if (enabled) this.lightCoordinator.maybeSpawn()
|
|
627
|
+
else this.lightCoordinator.terminate()
|
|
628
|
+
},
|
|
629
|
+
false
|
|
630
|
+
)
|
|
631
|
+
)
|
|
501
632
|
}
|
|
502
633
|
|
|
503
634
|
async processMessageQueue(source: string) {
|
|
@@ -549,15 +680,49 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
549
680
|
}
|
|
550
681
|
|
|
551
682
|
handleMessage(rawData: any) {
|
|
552
|
-
const data = rawData as MesherMainEvent
|
|
683
|
+
const data = rawData as MesherMainEvent & {
|
|
684
|
+
worldGeneration?: number
|
|
685
|
+
lightPublicationVersion?: number
|
|
686
|
+
clientLightRequestId?: number
|
|
687
|
+
clientLightEditSeq?: number
|
|
688
|
+
clientLightSessionEpoch?: number
|
|
689
|
+
}
|
|
553
690
|
if (!this.active) return
|
|
691
|
+
if (isClientLightTraceMessage(rawData)) {
|
|
692
|
+
ingestRemoteClientLightTraceMessage(rawData)
|
|
693
|
+
return
|
|
694
|
+
}
|
|
554
695
|
this.mesherLogReader?.workerMessageReceived(data.type, data)
|
|
555
|
-
|
|
696
|
+
const staleOwnerGeometry = this.lightCoordinator.rejectStaleOwnerGeometry(data)
|
|
697
|
+
if (staleOwnerGeometry && data.type === 'geometry' && isClientLightTraceEnabled()) {
|
|
698
|
+
recordClientLightTrace({
|
|
699
|
+
phase: 'reject',
|
|
700
|
+
sectionKey: data.key,
|
|
701
|
+
requestId: data.clientLightRequestId,
|
|
702
|
+
editSeq: data.clientLightEditSeq,
|
|
703
|
+
sessionEpoch: data.clientLightSessionEpoch,
|
|
704
|
+
lightVersion: data.lightPublicationVersion,
|
|
705
|
+
worldGeneration: data.worldGeneration
|
|
706
|
+
})
|
|
707
|
+
}
|
|
708
|
+
if ((data.type !== 'geometry' || !this.debugStopGeometryUpdate) && !staleOwnerGeometry) {
|
|
556
709
|
const start = performance.now()
|
|
557
710
|
this.handleWorkerMessage(data as WorkerReceive)
|
|
558
711
|
this.workerCustomHandleTime += performance.now() - start
|
|
559
712
|
}
|
|
560
713
|
if (data.type === 'geometry') {
|
|
714
|
+
if (!staleOwnerGeometry && isClientLightTraceEnabled()) {
|
|
715
|
+
recordClientLightTrace({
|
|
716
|
+
phase: 'receive',
|
|
717
|
+
sectionKey: data.key,
|
|
718
|
+
requestId: data.clientLightRequestId,
|
|
719
|
+
editSeq: data.clientLightEditSeq,
|
|
720
|
+
sessionEpoch: data.clientLightSessionEpoch,
|
|
721
|
+
lightVersion: data.lightPublicationVersion,
|
|
722
|
+
worldGeneration: data.worldGeneration,
|
|
723
|
+
workerIndex: data.workerIndex
|
|
724
|
+
})
|
|
725
|
+
}
|
|
561
726
|
this.logWorkerWork(() => `-> ${data.workerIndex} geometry ${data.key} ${JSON.stringify({ dataSize: JSON.stringify(data).length })}`)
|
|
562
727
|
this.geometryReceiveCount[data.workerIndex] ??= 0
|
|
563
728
|
this.geometryReceiveCount[data.workerIndex]++
|
|
@@ -567,6 +732,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
567
732
|
if (data.type === 'sectionFinished') {
|
|
568
733
|
// on after load & unload section
|
|
569
734
|
this.logWorkerWork(`<- ${data.workerIndex} sectionFinished ${data.key} ${JSON.stringify({ processTime: data.processTime })}`)
|
|
735
|
+
const rejectedFinish = this.lightCoordinator.consumeRejectedFinish(data.key)
|
|
570
736
|
if (!this.sectionsWaiting.has(data.key)) {
|
|
571
737
|
console.debug(`sectionFinished for non-outstanding section ${data.key} (viewDistance=${this.viewDistance})`)
|
|
572
738
|
return
|
|
@@ -574,7 +740,14 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
574
740
|
this.sectionsWaiting.set(data.key, this.sectionsWaiting.get(data.key)! - 1)
|
|
575
741
|
if (this.sectionsWaiting.get(data.key) === 0) {
|
|
576
742
|
this.sectionsWaiting.delete(data.key)
|
|
577
|
-
|
|
743
|
+
if (!rejectedFinish) {
|
|
744
|
+
this.lightCoordinator.forgetCovering(data.key)
|
|
745
|
+
this.finishedSections[data.key] = true
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
if (rejectedFinish) {
|
|
749
|
+
this.lightCoordinator.maybeDispatchCoveringRemesh(data.key)
|
|
750
|
+
if (!this.finishedSections[data.key]) return
|
|
578
751
|
}
|
|
579
752
|
|
|
580
753
|
const chunkCoords = data.key.split(',').map(Number)
|
|
@@ -648,6 +821,8 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
648
821
|
this.workersPreOtherAverageTime = (this.workersPreOtherAverageTime * (n - 1) + po) / n
|
|
649
822
|
this.workersPreCacheHitsTotal += pch
|
|
650
823
|
this.workersPreCacheMissesTotal += pcm
|
|
824
|
+
if (typeof data.preWasmParseHits === 'number') this.workersPreWasmParseHitsTotal += data.preWasmParseHits
|
|
825
|
+
if (typeof data.preWasmParseMisses === 'number') this.workersPreWasmParseMissesTotal += data.preWasmParseMisses
|
|
651
826
|
if (this.worldRendererConfig.debugWasmPerf && n % WorldRendererCommon.PHASE_PERF_LOG_INTERVAL === 0) {
|
|
652
827
|
const total = this.workersPreAverageTime + this.workersWasmAverageTime + this.workersPostAverageTime
|
|
653
828
|
const prePct = total > 0 ? (this.workersPreAverageTime / total) * 100 : 0
|
|
@@ -662,9 +837,11 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
662
837
|
const nbrPerAvg = nbrCnt > 0 ? this.workersPreNeighborConvertAverageTime / nbrCnt : 0
|
|
663
838
|
const cacheTotal = this.workersPreCacheHitsTotal + this.workersPreCacheMissesTotal
|
|
664
839
|
const cacheHitPct = cacheTotal > 0 ? (this.workersPreCacheHitsTotal / cacheTotal) * 100 : 0
|
|
840
|
+
const wasmParseTotal = this.workersPreWasmParseHitsTotal + this.workersPreWasmParseMissesTotal
|
|
841
|
+
const wasmParseHitPct = wasmParseTotal > 0 ? (this.workersPreWasmParseHitsTotal / wasmParseTotal) * 100 : 0
|
|
665
842
|
// eslint-disable-next-line no-console
|
|
666
843
|
console.log(
|
|
667
|
-
`[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)`
|
|
844
|
+
`[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) | pre.wasmParse hits=${this.workersPreWasmParseHitsTotal} misses=${this.workersPreWasmParseMissesTotal} (${wasmParseHitPct.toFixed(1)}% hit)`
|
|
668
845
|
)
|
|
669
846
|
}
|
|
670
847
|
}
|
|
@@ -727,6 +904,8 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
727
904
|
|
|
728
905
|
timeUpdated?(newTime: number): void
|
|
729
906
|
|
|
907
|
+
moonPhaseUpdated?(phase: number): void
|
|
908
|
+
|
|
730
909
|
/** Called when day-cycle sky-light bucket changes; Three.js overrides to remesh. */
|
|
731
910
|
protected onDayCycleSkyLightChanged?(_skyLight: number): void
|
|
732
911
|
|
|
@@ -774,11 +953,13 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
774
953
|
abstract updateShowChunksBorder(value: boolean): void
|
|
775
954
|
|
|
776
955
|
resetWorld() {
|
|
956
|
+
this.lightCoordinator.interactiveSections.clear()
|
|
777
957
|
// destroy workers
|
|
778
958
|
for (const worker of this.workers) {
|
|
779
959
|
worker.terminate()
|
|
780
960
|
}
|
|
781
961
|
this.workers = []
|
|
962
|
+
this.lightCoordinator.terminate()
|
|
782
963
|
}
|
|
783
964
|
|
|
784
965
|
async resetWorkers() {
|
|
@@ -887,7 +1068,8 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
887
1068
|
x,
|
|
888
1069
|
z,
|
|
889
1070
|
chunk,
|
|
890
|
-
customBlockModels: customBlockModels || undefined
|
|
1071
|
+
customBlockModels: customBlockModels || undefined,
|
|
1072
|
+
isLightUpdate
|
|
891
1073
|
})
|
|
892
1074
|
}
|
|
893
1075
|
// WASM mesher pushes heightmaps from `processColumnTick` after each
|
|
@@ -908,6 +1090,15 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
908
1090
|
)
|
|
909
1091
|
this.mesherLogReader?.chunkReceived(x, z, chunk.length)
|
|
910
1092
|
this.cameraCollisionBlockCache.ingestColumn(x, z, chunk)
|
|
1093
|
+
this.rendererLightCache.ingestColumn(x, z, chunk)
|
|
1094
|
+
this.lightCoordinator.session?.ingestColumn({
|
|
1095
|
+
chunkX: x,
|
|
1096
|
+
chunkZ: z,
|
|
1097
|
+
chunkJson: chunk,
|
|
1098
|
+
version: this.version,
|
|
1099
|
+
worldMinY: this.worldSizeParams.minY,
|
|
1100
|
+
worldHeight: this.worldSizeParams.worldHeight
|
|
1101
|
+
})
|
|
911
1102
|
const sectionHeight = this.getSectionHeight()
|
|
912
1103
|
const CHUNK_SIZE = 16
|
|
913
1104
|
|
|
@@ -933,7 +1124,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
933
1124
|
this.checkAllFinished()
|
|
934
1125
|
}
|
|
935
1126
|
|
|
936
|
-
removeColumn(x, z) {
|
|
1127
|
+
removeColumn(x, z, opts?: { preserveClientLight?: boolean }) {
|
|
937
1128
|
delete this.loadedChunks[`${x},${z}`]
|
|
938
1129
|
// Cancel any pending heightmap debounce for this chunk
|
|
939
1130
|
const debounceKey = `${x},${z}`
|
|
@@ -949,6 +1140,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
949
1140
|
this.sectionDirtyTimers.delete(key)
|
|
950
1141
|
this.sectionDirtyCount.delete(key)
|
|
951
1142
|
this.sectionDirtyPendingArgs.delete(key)
|
|
1143
|
+
this.sectionDirtyLightMeta.delete(key)
|
|
952
1144
|
}
|
|
953
1145
|
}
|
|
954
1146
|
for (let i = 0; i < this.workers.length; i++) {
|
|
@@ -973,10 +1165,13 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
973
1165
|
)
|
|
974
1166
|
}
|
|
975
1167
|
this.sectionsWaiting.delete(sectionKey)
|
|
1168
|
+
this.lightCoordinator.forgetInteractiveSection(sectionKey)
|
|
976
1169
|
delete this.finishedSections[sectionKey]
|
|
977
1170
|
}
|
|
978
1171
|
this.highestBlocksByChunks.delete(`${x},${z}`)
|
|
979
1172
|
this.cameraCollisionBlockCache.removeColumn(x, z)
|
|
1173
|
+
this.rendererLightCache.removeColumn(x, z)
|
|
1174
|
+
this.lightCoordinator.onColumnRemoved(x, z, opts)
|
|
980
1175
|
const heightmapKey = `${Math.floor(x / 16)},${Math.floor(z / 16)}`
|
|
981
1176
|
delete this.reactiveState.world.heightmaps[heightmapKey]
|
|
982
1177
|
|
|
@@ -1054,15 +1249,19 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
1054
1249
|
({ x, z, chunk, worldConfig, isLightUpdate }) => {
|
|
1055
1250
|
this.worldSizeParams = worldConfig
|
|
1056
1251
|
this.cameraCollisionBlockCache.setWorldBounds(worldConfig.minY, worldConfig.worldHeight)
|
|
1252
|
+
this.rendererLightCache.setWorldBounds(worldConfig.minY, worldConfig.worldHeight)
|
|
1057
1253
|
this.queuedChunks.add(`${x},${z}`)
|
|
1058
1254
|
const args = [x, z, chunk, isLightUpdate]
|
|
1255
|
+
const incarnation = this.columnIncarnationFor(`${x},${z}`)
|
|
1059
1256
|
if (!currentLoadChunkBatch) {
|
|
1060
1257
|
// add a setting to use debounce instead
|
|
1061
1258
|
currentLoadChunkBatch = {
|
|
1062
1259
|
data: [],
|
|
1063
1260
|
timeout: setTimeout(() => {
|
|
1064
|
-
for (const args of currentLoadChunkBatch!.data) {
|
|
1065
|
-
|
|
1261
|
+
for (const { args, incarnation } of currentLoadChunkBatch!.data) {
|
|
1262
|
+
const key = `${args[0]},${args[1]}`
|
|
1263
|
+
this.queuedChunks.delete(key)
|
|
1264
|
+
if (this.columnIncarnationFor(key) !== incarnation) continue
|
|
1066
1265
|
this.addColumn(...(args as Parameters<typeof this.addColumn>))
|
|
1067
1266
|
}
|
|
1068
1267
|
for (const fn of this.queuedFunctions) {
|
|
@@ -1073,7 +1272,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
1073
1272
|
}, this.worldRendererConfig.addChunksBatchWaitTime)
|
|
1074
1273
|
}
|
|
1075
1274
|
}
|
|
1076
|
-
currentLoadChunkBatch.data.push(args)
|
|
1275
|
+
currentLoadChunkBatch.data.push({ args, incarnation })
|
|
1077
1276
|
},
|
|
1078
1277
|
signal
|
|
1079
1278
|
)
|
|
@@ -1090,8 +1289,8 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
1090
1289
|
bindAbortableListener(
|
|
1091
1290
|
worldEmitter,
|
|
1092
1291
|
'unloadChunk',
|
|
1093
|
-
({ x, z }) => {
|
|
1094
|
-
this.removeColumn(x, z)
|
|
1292
|
+
({ x, z, preserveClientLight }) => {
|
|
1293
|
+
this.removeColumn(x, z, { preserveClientLight })
|
|
1095
1294
|
},
|
|
1096
1295
|
signal
|
|
1097
1296
|
)
|
|
@@ -1158,6 +1357,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
1158
1357
|
'onWorldSwitch',
|
|
1159
1358
|
() => {
|
|
1160
1359
|
this.cameraCollisionBlockCache.clear()
|
|
1360
|
+
this.rendererLightCache.clear()
|
|
1161
1361
|
for (const fn of this.onWorldSwitched) {
|
|
1162
1362
|
try {
|
|
1163
1363
|
fn()
|
|
@@ -1194,6 +1394,15 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
1194
1394
|
signal
|
|
1195
1395
|
)
|
|
1196
1396
|
|
|
1397
|
+
bindAbortableListener(
|
|
1398
|
+
worldEmitter,
|
|
1399
|
+
'moonPhase',
|
|
1400
|
+
phase => {
|
|
1401
|
+
this.moonPhaseUpdated?.(phase)
|
|
1402
|
+
},
|
|
1403
|
+
signal
|
|
1404
|
+
)
|
|
1405
|
+
|
|
1197
1406
|
bindAbortableListener(
|
|
1198
1407
|
worldEmitter,
|
|
1199
1408
|
'biomeUpdate',
|
|
@@ -1251,43 +1460,49 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
1251
1460
|
if (stateId !== undefined) {
|
|
1252
1461
|
this.cameraCollisionBlockCache.setBlockStateId(pos.x, pos.y, pos.z, stateId)
|
|
1253
1462
|
}
|
|
1254
|
-
this.
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
if (
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
if (
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1463
|
+
this.lightCoordinator.noteBlockChange(pos, stateId, this.neighborChunkUpdates)
|
|
1464
|
+
this.lightCoordinator.nextDirtyUrgent = true
|
|
1465
|
+
try {
|
|
1466
|
+
this.setSectionDirty(pos, true, true)
|
|
1467
|
+
if (this.neighborChunkUpdates) {
|
|
1468
|
+
const CHUNK_SIZE = 16
|
|
1469
|
+
const sectionHeight = this.getSectionHeight()
|
|
1470
|
+
if ((pos.x & 15) === 0) this.setSectionDirty(pos.offset(-CHUNK_SIZE, 0, 0), true, true)
|
|
1471
|
+
if ((pos.x & 15) === 15) this.setSectionDirty(pos.offset(CHUNK_SIZE, 0, 0), true, true)
|
|
1472
|
+
if ((pos.y & (sectionHeight - 1)) === 0) this.setSectionDirty(pos.offset(0, -sectionHeight, 0), true, true)
|
|
1473
|
+
if ((pos.y & (sectionHeight - 1)) === sectionHeight - 1) this.setSectionDirty(pos.offset(0, sectionHeight, 0), true, true)
|
|
1474
|
+
if ((pos.z & 15) === 0) this.setSectionDirty(pos.offset(0, 0, -CHUNK_SIZE), true, true)
|
|
1475
|
+
if ((pos.z & 15) === 15) this.setSectionDirty(pos.offset(0, 0, CHUNK_SIZE), true, true)
|
|
1476
|
+
|
|
1477
|
+
if (needAoRecalculation) {
|
|
1478
|
+
// top view neighbors
|
|
1479
|
+
if ((pos.x & 15) === 0 && (pos.z & 15) === 0) this.setSectionDirty(pos.offset(-CHUNK_SIZE, 0, -CHUNK_SIZE), true, true)
|
|
1480
|
+
if ((pos.x & 15) === 15 && (pos.z & 15) === 0) this.setSectionDirty(pos.offset(CHUNK_SIZE, 0, -CHUNK_SIZE), true, true)
|
|
1481
|
+
if ((pos.x & 15) === 0 && (pos.z & 15) === 15) this.setSectionDirty(pos.offset(-CHUNK_SIZE, 0, CHUNK_SIZE), true, true)
|
|
1482
|
+
if ((pos.x & 15) === 15 && (pos.z & 15) === 15) this.setSectionDirty(pos.offset(CHUNK_SIZE, 0, CHUNK_SIZE), true, true)
|
|
1483
|
+
|
|
1484
|
+
// side view neighbors (but ignore updates above)
|
|
1485
|
+
// z view neighbors
|
|
1486
|
+
if ((pos.x & 15) === 0 && (pos.y & (sectionHeight - 1)) === 0) this.setSectionDirty(pos.offset(-CHUNK_SIZE, -sectionHeight, 0), true, true)
|
|
1487
|
+
if ((pos.x & 15) === 15 && (pos.y & (sectionHeight - 1)) === 0) this.setSectionDirty(pos.offset(CHUNK_SIZE, -sectionHeight, 0), true, true)
|
|
1488
|
+
|
|
1489
|
+
// x view neighbors
|
|
1490
|
+
if ((pos.z & 15) === 0 && (pos.y & (sectionHeight - 1)) === 0) this.setSectionDirty(pos.offset(0, -sectionHeight, -CHUNK_SIZE), true, true)
|
|
1491
|
+
if ((pos.z & 15) === 15 && (pos.y & (sectionHeight - 1)) === 0) this.setSectionDirty(pos.offset(0, -sectionHeight, CHUNK_SIZE), true, true)
|
|
1492
|
+
|
|
1493
|
+
// x & z neighbors
|
|
1494
|
+
if ((pos.y & (sectionHeight - 1)) === 0 && (pos.x & 15) === 0 && (pos.z & 15) === 0)
|
|
1495
|
+
this.setSectionDirty(pos.offset(-CHUNK_SIZE, -sectionHeight, -CHUNK_SIZE), true, true)
|
|
1496
|
+
if ((pos.y & (sectionHeight - 1)) === 0 && (pos.x & 15) === 15 && (pos.z & 15) === 0)
|
|
1497
|
+
this.setSectionDirty(pos.offset(CHUNK_SIZE, -sectionHeight, -CHUNK_SIZE), true, true)
|
|
1498
|
+
if ((pos.y & (sectionHeight - 1)) === 0 && (pos.x & 15) === 0 && (pos.z & 15) === 15)
|
|
1499
|
+
this.setSectionDirty(pos.offset(-CHUNK_SIZE, -sectionHeight, CHUNK_SIZE), true, true)
|
|
1500
|
+
if ((pos.y & (sectionHeight - 1)) === 0 && (pos.x & 15) === 15 && (pos.z & 15) === 15)
|
|
1501
|
+
this.setSectionDirty(pos.offset(CHUNK_SIZE, -sectionHeight, CHUNK_SIZE), true, true)
|
|
1502
|
+
}
|
|
1290
1503
|
}
|
|
1504
|
+
} finally {
|
|
1505
|
+
this.lightCoordinator.nextDirtyUrgent = false
|
|
1291
1506
|
}
|
|
1292
1507
|
}
|
|
1293
1508
|
|
|
@@ -1378,6 +1593,28 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
1378
1593
|
const sectionHeight = this.getSectionHeight()
|
|
1379
1594
|
const key = `${Math.floor(pos.x / CHUNK_SIZE) * CHUNK_SIZE},${Math.floor(pos.y / sectionHeight) * sectionHeight},${Math.floor(pos.z / CHUNK_SIZE) * CHUNK_SIZE}`
|
|
1380
1595
|
|
|
1596
|
+
this.sectionDirtyLightMeta ??= new Map()
|
|
1597
|
+
if (this.lightCoordinator.nextDirtyLightMeta.lightPublicationVersion != null) {
|
|
1598
|
+
const prev = this.sectionDirtyLightMeta.get(key)
|
|
1599
|
+
const incomingVersion = this.lightCoordinator.nextDirtyLightMeta.lightPublicationVersion
|
|
1600
|
+
if (!prev || incomingVersion >= prev.lightPublicationVersion) {
|
|
1601
|
+
this.sectionDirtyLightMeta.set(key, {
|
|
1602
|
+
lightPublicationVersion: incomingVersion,
|
|
1603
|
+
worldGeneration: this.lightCoordinator.nextDirtyLightMeta.worldGeneration
|
|
1604
|
+
})
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
if (this.lightCoordinator.nextDirtyLightMeta.lightPublicationVersion != null || this.lightCoordinator.nextDirtyUrgent) {
|
|
1609
|
+
this._dispatchDirtyImmediate(pos, value, useChangeWorker)
|
|
1610
|
+
const pendingTimer = this.sectionDirtyTimers.get(key)
|
|
1611
|
+
if (pendingTimer) clearTimeout(pendingTimer)
|
|
1612
|
+
this.sectionDirtyTimers.delete(key)
|
|
1613
|
+
this.sectionDirtyCount.delete(key)
|
|
1614
|
+
this.sectionDirtyPendingArgs.delete(key)
|
|
1615
|
+
return
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1381
1618
|
const currentCount = (this.sectionDirtyCount.get(key) ?? 0) + 1
|
|
1382
1619
|
this.sectionDirtyCount.set(key, currentCount)
|
|
1383
1620
|
|
|
@@ -1395,14 +1632,19 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
1395
1632
|
this.sectionDirtyTimers.delete(key)
|
|
1396
1633
|
this.sectionDirtyPendingArgs.delete(key)
|
|
1397
1634
|
if (args) {
|
|
1398
|
-
this.
|
|
1635
|
+
this.dispatchPendingDirty(args)
|
|
1399
1636
|
}
|
|
1400
1637
|
}, WorldRendererCommon.GEOMETRY_THROTTLE_DELAY)
|
|
1401
1638
|
)
|
|
1402
1639
|
}
|
|
1403
1640
|
} else {
|
|
1404
1641
|
// Subsequent requests — throttle: store latest args, existing timer will dispatch
|
|
1405
|
-
this.sectionDirtyPendingArgs.set(key, {
|
|
1642
|
+
this.sectionDirtyPendingArgs.set(key, {
|
|
1643
|
+
pos,
|
|
1644
|
+
value,
|
|
1645
|
+
useChangeWorker,
|
|
1646
|
+
urgent: this.lightCoordinator.nextDirtyUrgent || this.sectionDirtyPendingArgs.get(key)?.urgent
|
|
1647
|
+
})
|
|
1406
1648
|
|
|
1407
1649
|
if (!this.sectionDirtyTimers.has(key)) {
|
|
1408
1650
|
this.sectionDirtyTimers.set(
|
|
@@ -1413,7 +1655,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
1413
1655
|
this.sectionDirtyTimers.delete(key)
|
|
1414
1656
|
this.sectionDirtyPendingArgs.delete(key)
|
|
1415
1657
|
if (args) {
|
|
1416
|
-
this.
|
|
1658
|
+
this.dispatchPendingDirty(args)
|
|
1417
1659
|
}
|
|
1418
1660
|
}, WorldRendererCommon.GEOMETRY_THROTTLE_DELAY)
|
|
1419
1661
|
)
|
|
@@ -1421,6 +1663,12 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
1421
1663
|
}
|
|
1422
1664
|
}
|
|
1423
1665
|
|
|
1666
|
+
private dispatchPendingDirty(args: { pos: Vec3; value: boolean; useChangeWorker: boolean; urgent?: boolean }) {
|
|
1667
|
+
this.lightCoordinator.withDirtyUrgency(!!args.urgent, () => {
|
|
1668
|
+
this._dispatchDirtyImmediate(args.pos, args.value, args.useChangeWorker)
|
|
1669
|
+
})
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1424
1672
|
/** Dispatch dirty message to worker without throttle (original logic) */
|
|
1425
1673
|
private _dispatchDirtyImmediate(pos: Vec3, value: boolean, useChangeWorker: boolean) {
|
|
1426
1674
|
this.reactiveState.world.mesherWork = true
|
|
@@ -1434,25 +1682,47 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
1434
1682
|
// is always dispatched to the same worker
|
|
1435
1683
|
const hash = this.getWorkerNumber(pos, useChangeWorker && (this.mesherLogger.active || this.worldRendererConfig.dedicatedChangeWorker))
|
|
1436
1684
|
this.sectionsWaiting.set(key, (this.sectionsWaiting.get(key) ?? 0) + 1)
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1685
|
+
this.sectionDirtyLightMeta ??= new Map()
|
|
1686
|
+
const { lightMeta, ownerVersions, traceIds } = this.lightCoordinator.describeDirty(pos, key)
|
|
1687
|
+
const dirtyMessage = {
|
|
1688
|
+
type: 'dirty' as const,
|
|
1689
|
+
x: pos.x,
|
|
1690
|
+
y: pos.y,
|
|
1691
|
+
z: pos.z,
|
|
1692
|
+
value,
|
|
1693
|
+
config: this.getMesherConfig(),
|
|
1694
|
+
...(lightMeta?.lightPublicationVersion != null
|
|
1695
|
+
? {
|
|
1696
|
+
lightPublicationVersion: lightMeta.lightPublicationVersion,
|
|
1697
|
+
worldGeneration: lightMeta.worldGeneration
|
|
1698
|
+
}
|
|
1699
|
+
: {}),
|
|
1700
|
+
...ownerVersions,
|
|
1701
|
+
...(this.lightCoordinator.nextDirtyUrgent ? { urgent: true as const } : {}),
|
|
1702
|
+
...(traceIds
|
|
1703
|
+
? {
|
|
1704
|
+
clientLightRequestId: traceIds.requestId,
|
|
1705
|
+
clientLightEditSeq: traceIds.editSeq,
|
|
1706
|
+
clientLightSessionEpoch: traceIds.sessionEpoch
|
|
1707
|
+
}
|
|
1708
|
+
: {})
|
|
1709
|
+
}
|
|
1710
|
+
if (traceIds) {
|
|
1711
|
+
recordClientLightTrace({
|
|
1712
|
+
phase: 'mesherEnqueue',
|
|
1713
|
+
sectionKey: key,
|
|
1714
|
+
requestId: traceIds.requestId,
|
|
1715
|
+
editSeq: traceIds.editSeq,
|
|
1716
|
+
sessionEpoch: traceIds.sessionEpoch,
|
|
1717
|
+
lightVersion: lightMeta?.lightPublicationVersion,
|
|
1718
|
+
worldGeneration: lightMeta?.worldGeneration
|
|
1445
1719
|
})
|
|
1720
|
+
}
|
|
1721
|
+
if (this.forceCallFromMesherReplayer) {
|
|
1722
|
+
this.workers[hash].postMessage(dirtyMessage)
|
|
1446
1723
|
} else {
|
|
1447
1724
|
this.toWorkerMessagesQueue[hash] ??= []
|
|
1448
|
-
this.toWorkerMessagesQueue[hash].push(
|
|
1449
|
-
type: 'dirty',
|
|
1450
|
-
x: pos.x,
|
|
1451
|
-
y: pos.y,
|
|
1452
|
-
z: pos.z,
|
|
1453
|
-
value,
|
|
1454
|
-
config: this.getMesherConfig()
|
|
1455
|
-
})
|
|
1725
|
+
this.toWorkerMessagesQueue[hash].push(dirtyMessage)
|
|
1456
1726
|
this.dispatchMessages()
|
|
1457
1727
|
}
|
|
1458
1728
|
}
|
|
@@ -1533,12 +1803,15 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
1533
1803
|
this.sectionDirtyTimers.clear()
|
|
1534
1804
|
this.sectionDirtyCount.clear()
|
|
1535
1805
|
this.sectionDirtyPendingArgs.clear()
|
|
1806
|
+
this.sectionDirtyLightMeta.clear()
|
|
1536
1807
|
|
|
1808
|
+
setClientLightTraceConfigListener(undefined)
|
|
1537
1809
|
// Stop all workers
|
|
1538
1810
|
for (const worker of this.workers) {
|
|
1539
1811
|
worker.terminate()
|
|
1540
1812
|
}
|
|
1541
1813
|
this.workers = []
|
|
1814
|
+
this.lightCoordinator.terminate()
|
|
1542
1815
|
|
|
1543
1816
|
// Stop and destroy sound system
|
|
1544
1817
|
if (this.soundSystem) {
|