minecraft-renderer 0.1.107 → 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 +80 -80
- package/dist/minecraft-renderer.js.meta.json +1 -1
- package/dist/threeWorker.js +513 -513
- 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 +5 -0
- package/src/graphicsBackend/rendererDefaultOptions.test.ts +51 -0
- package/src/graphicsBackend/rendererDefaultOptions.ts +13 -1
- package/src/graphicsBackend/rendererOptionsSync.ts +3 -3
- package/src/lib/blockEntityLightRegistry.ts +4 -0
- package/src/lib/worldrendererCommon.lightOwner.test.ts +916 -0
- package/src/lib/worldrendererCommon.removeColumn.test.ts +16 -0
- package/src/lib/worldrendererCommon.ts +329 -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 +77 -11
- package/src/three/entity/EntityMesh.ts +1 -0
- package/src/three/entity/legWalkAnimation.test.ts +11 -2
- package/src/three/entity/legWalkAnimation.ts +1 -9
- package/src/three/globalBlockBuffer.ts +94 -2
- package/src/three/globalLegacyBuffer.ts +94 -2
- package/src/three/graphicsBackendBase.ts +2 -9
- package/src/three/tests/globalBlockBuffer.test.ts +98 -0
- package/src/three/tests/globalLegacyBuffer.test.ts +108 -0
- package/src/three/worldRendererThree.ts +75 -34
- 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 +1 -0
- package/src/worldView/worldView.geometryReload.test.ts +68 -0
- package/src/worldView/worldView.spiral.test.ts +1 -1
- package/src/worldView/worldView.ts +46 -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
|
}
|
|
@@ -776,11 +953,13 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
776
953
|
abstract updateShowChunksBorder(value: boolean): void
|
|
777
954
|
|
|
778
955
|
resetWorld() {
|
|
956
|
+
this.lightCoordinator.interactiveSections.clear()
|
|
779
957
|
// destroy workers
|
|
780
958
|
for (const worker of this.workers) {
|
|
781
959
|
worker.terminate()
|
|
782
960
|
}
|
|
783
961
|
this.workers = []
|
|
962
|
+
this.lightCoordinator.terminate()
|
|
784
963
|
}
|
|
785
964
|
|
|
786
965
|
async resetWorkers() {
|
|
@@ -889,7 +1068,8 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
889
1068
|
x,
|
|
890
1069
|
z,
|
|
891
1070
|
chunk,
|
|
892
|
-
customBlockModels: customBlockModels || undefined
|
|
1071
|
+
customBlockModels: customBlockModels || undefined,
|
|
1072
|
+
isLightUpdate
|
|
893
1073
|
})
|
|
894
1074
|
}
|
|
895
1075
|
// WASM mesher pushes heightmaps from `processColumnTick` after each
|
|
@@ -910,6 +1090,15 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
910
1090
|
)
|
|
911
1091
|
this.mesherLogReader?.chunkReceived(x, z, chunk.length)
|
|
912
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
|
+
})
|
|
913
1102
|
const sectionHeight = this.getSectionHeight()
|
|
914
1103
|
const CHUNK_SIZE = 16
|
|
915
1104
|
|
|
@@ -935,7 +1124,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
935
1124
|
this.checkAllFinished()
|
|
936
1125
|
}
|
|
937
1126
|
|
|
938
|
-
removeColumn(x, z) {
|
|
1127
|
+
removeColumn(x, z, opts?: { preserveClientLight?: boolean }) {
|
|
939
1128
|
delete this.loadedChunks[`${x},${z}`]
|
|
940
1129
|
// Cancel any pending heightmap debounce for this chunk
|
|
941
1130
|
const debounceKey = `${x},${z}`
|
|
@@ -951,6 +1140,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
951
1140
|
this.sectionDirtyTimers.delete(key)
|
|
952
1141
|
this.sectionDirtyCount.delete(key)
|
|
953
1142
|
this.sectionDirtyPendingArgs.delete(key)
|
|
1143
|
+
this.sectionDirtyLightMeta.delete(key)
|
|
954
1144
|
}
|
|
955
1145
|
}
|
|
956
1146
|
for (let i = 0; i < this.workers.length; i++) {
|
|
@@ -975,10 +1165,13 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
975
1165
|
)
|
|
976
1166
|
}
|
|
977
1167
|
this.sectionsWaiting.delete(sectionKey)
|
|
1168
|
+
this.lightCoordinator.forgetInteractiveSection(sectionKey)
|
|
978
1169
|
delete this.finishedSections[sectionKey]
|
|
979
1170
|
}
|
|
980
1171
|
this.highestBlocksByChunks.delete(`${x},${z}`)
|
|
981
1172
|
this.cameraCollisionBlockCache.removeColumn(x, z)
|
|
1173
|
+
this.rendererLightCache.removeColumn(x, z)
|
|
1174
|
+
this.lightCoordinator.onColumnRemoved(x, z, opts)
|
|
982
1175
|
const heightmapKey = `${Math.floor(x / 16)},${Math.floor(z / 16)}`
|
|
983
1176
|
delete this.reactiveState.world.heightmaps[heightmapKey]
|
|
984
1177
|
|
|
@@ -1056,15 +1249,19 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
1056
1249
|
({ x, z, chunk, worldConfig, isLightUpdate }) => {
|
|
1057
1250
|
this.worldSizeParams = worldConfig
|
|
1058
1251
|
this.cameraCollisionBlockCache.setWorldBounds(worldConfig.minY, worldConfig.worldHeight)
|
|
1252
|
+
this.rendererLightCache.setWorldBounds(worldConfig.minY, worldConfig.worldHeight)
|
|
1059
1253
|
this.queuedChunks.add(`${x},${z}`)
|
|
1060
1254
|
const args = [x, z, chunk, isLightUpdate]
|
|
1255
|
+
const incarnation = this.columnIncarnationFor(`${x},${z}`)
|
|
1061
1256
|
if (!currentLoadChunkBatch) {
|
|
1062
1257
|
// add a setting to use debounce instead
|
|
1063
1258
|
currentLoadChunkBatch = {
|
|
1064
1259
|
data: [],
|
|
1065
1260
|
timeout: setTimeout(() => {
|
|
1066
|
-
for (const args of currentLoadChunkBatch!.data) {
|
|
1067
|
-
|
|
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
|
|
1068
1265
|
this.addColumn(...(args as Parameters<typeof this.addColumn>))
|
|
1069
1266
|
}
|
|
1070
1267
|
for (const fn of this.queuedFunctions) {
|
|
@@ -1075,7 +1272,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
1075
1272
|
}, this.worldRendererConfig.addChunksBatchWaitTime)
|
|
1076
1273
|
}
|
|
1077
1274
|
}
|
|
1078
|
-
currentLoadChunkBatch.data.push(args)
|
|
1275
|
+
currentLoadChunkBatch.data.push({ args, incarnation })
|
|
1079
1276
|
},
|
|
1080
1277
|
signal
|
|
1081
1278
|
)
|
|
@@ -1092,8 +1289,8 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
1092
1289
|
bindAbortableListener(
|
|
1093
1290
|
worldEmitter,
|
|
1094
1291
|
'unloadChunk',
|
|
1095
|
-
({ x, z }) => {
|
|
1096
|
-
this.removeColumn(x, z)
|
|
1292
|
+
({ x, z, preserveClientLight }) => {
|
|
1293
|
+
this.removeColumn(x, z, { preserveClientLight })
|
|
1097
1294
|
},
|
|
1098
1295
|
signal
|
|
1099
1296
|
)
|
|
@@ -1160,6 +1357,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
1160
1357
|
'onWorldSwitch',
|
|
1161
1358
|
() => {
|
|
1162
1359
|
this.cameraCollisionBlockCache.clear()
|
|
1360
|
+
this.rendererLightCache.clear()
|
|
1163
1361
|
for (const fn of this.onWorldSwitched) {
|
|
1164
1362
|
try {
|
|
1165
1363
|
fn()
|
|
@@ -1262,43 +1460,49 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
1262
1460
|
if (stateId !== undefined) {
|
|
1263
1461
|
this.cameraCollisionBlockCache.setBlockStateId(pos.x, pos.y, pos.z, stateId)
|
|
1264
1462
|
}
|
|
1265
|
-
this.
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
if (
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
if (
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
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
|
+
}
|
|
1301
1503
|
}
|
|
1504
|
+
} finally {
|
|
1505
|
+
this.lightCoordinator.nextDirtyUrgent = false
|
|
1302
1506
|
}
|
|
1303
1507
|
}
|
|
1304
1508
|
|
|
@@ -1389,6 +1593,28 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
1389
1593
|
const sectionHeight = this.getSectionHeight()
|
|
1390
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}`
|
|
1391
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
|
+
|
|
1392
1618
|
const currentCount = (this.sectionDirtyCount.get(key) ?? 0) + 1
|
|
1393
1619
|
this.sectionDirtyCount.set(key, currentCount)
|
|
1394
1620
|
|
|
@@ -1406,14 +1632,19 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
1406
1632
|
this.sectionDirtyTimers.delete(key)
|
|
1407
1633
|
this.sectionDirtyPendingArgs.delete(key)
|
|
1408
1634
|
if (args) {
|
|
1409
|
-
this.
|
|
1635
|
+
this.dispatchPendingDirty(args)
|
|
1410
1636
|
}
|
|
1411
1637
|
}, WorldRendererCommon.GEOMETRY_THROTTLE_DELAY)
|
|
1412
1638
|
)
|
|
1413
1639
|
}
|
|
1414
1640
|
} else {
|
|
1415
1641
|
// Subsequent requests — throttle: store latest args, existing timer will dispatch
|
|
1416
|
-
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
|
+
})
|
|
1417
1648
|
|
|
1418
1649
|
if (!this.sectionDirtyTimers.has(key)) {
|
|
1419
1650
|
this.sectionDirtyTimers.set(
|
|
@@ -1424,7 +1655,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
1424
1655
|
this.sectionDirtyTimers.delete(key)
|
|
1425
1656
|
this.sectionDirtyPendingArgs.delete(key)
|
|
1426
1657
|
if (args) {
|
|
1427
|
-
this.
|
|
1658
|
+
this.dispatchPendingDirty(args)
|
|
1428
1659
|
}
|
|
1429
1660
|
}, WorldRendererCommon.GEOMETRY_THROTTLE_DELAY)
|
|
1430
1661
|
)
|
|
@@ -1432,6 +1663,12 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
1432
1663
|
}
|
|
1433
1664
|
}
|
|
1434
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
|
+
|
|
1435
1672
|
/** Dispatch dirty message to worker without throttle (original logic) */
|
|
1436
1673
|
private _dispatchDirtyImmediate(pos: Vec3, value: boolean, useChangeWorker: boolean) {
|
|
1437
1674
|
this.reactiveState.world.mesherWork = true
|
|
@@ -1445,25 +1682,47 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
1445
1682
|
// is always dispatched to the same worker
|
|
1446
1683
|
const hash = this.getWorkerNumber(pos, useChangeWorker && (this.mesherLogger.active || this.worldRendererConfig.dedicatedChangeWorker))
|
|
1447
1684
|
this.sectionsWaiting.set(key, (this.sectionsWaiting.get(key) ?? 0) + 1)
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
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
|
|
1456
1719
|
})
|
|
1720
|
+
}
|
|
1721
|
+
if (this.forceCallFromMesherReplayer) {
|
|
1722
|
+
this.workers[hash].postMessage(dirtyMessage)
|
|
1457
1723
|
} else {
|
|
1458
1724
|
this.toWorkerMessagesQueue[hash] ??= []
|
|
1459
|
-
this.toWorkerMessagesQueue[hash].push(
|
|
1460
|
-
type: 'dirty',
|
|
1461
|
-
x: pos.x,
|
|
1462
|
-
y: pos.y,
|
|
1463
|
-
z: pos.z,
|
|
1464
|
-
value,
|
|
1465
|
-
config: this.getMesherConfig()
|
|
1466
|
-
})
|
|
1725
|
+
this.toWorkerMessagesQueue[hash].push(dirtyMessage)
|
|
1467
1726
|
this.dispatchMessages()
|
|
1468
1727
|
}
|
|
1469
1728
|
}
|
|
@@ -1544,12 +1803,15 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
1544
1803
|
this.sectionDirtyTimers.clear()
|
|
1545
1804
|
this.sectionDirtyCount.clear()
|
|
1546
1805
|
this.sectionDirtyPendingArgs.clear()
|
|
1806
|
+
this.sectionDirtyLightMeta.clear()
|
|
1547
1807
|
|
|
1808
|
+
setClientLightTraceConfigListener(undefined)
|
|
1548
1809
|
// Stop all workers
|
|
1549
1810
|
for (const worker of this.workers) {
|
|
1550
1811
|
worker.terminate()
|
|
1551
1812
|
}
|
|
1552
1813
|
this.workers = []
|
|
1814
|
+
this.lightCoordinator.terminate()
|
|
1553
1815
|
|
|
1554
1816
|
// Stop and destroy sound system
|
|
1555
1817
|
if (this.soundSystem) {
|