minecraft-renderer 0.1.22 → 0.1.23

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "minecraft-renderer",
3
- "version": "0.1.22",
3
+ "version": "0.1.23",
4
4
  "description": "The most Modular Minecraft world renderer with Three.js WebGL backend",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -59,7 +59,11 @@ export const defaultWorldRendererConfig = {
59
59
  // World settings
60
60
  clipWorldBelowY: undefined as undefined | number,
61
61
  isPlayground: false,
62
- instantCameraUpdate: false
62
+ instantCameraUpdate: false,
63
+ isRaining: false,
64
+
65
+ // Module states: 'enabled' = force on, 'disabled' = force off, 'auto' = use autoEnableCheck
66
+ moduleStates: {} as Record<string, 'enabled' | 'disabled' | 'auto'>
63
67
  }
64
68
 
65
69
  export type WorldRendererConfig = typeof defaultWorldRendererConfig
@@ -5,7 +5,7 @@ import { Vec3 } from 'vec3'
5
5
  import TypedEmitter from 'typed-emitter'
6
6
  import { WorldBlockProvider } from 'mc-assets/dist/worldBlockProvider'
7
7
  import { subscribeKey } from 'valtio/utils'
8
- import { proxy } from 'valtio'
8
+ 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'
@@ -106,6 +106,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
106
106
  debugStopGeometryUpdate = false
107
107
 
108
108
  protocolCustomBlocks = new Map<string, CustomBlockModels>()
109
+ private heightmapDebounceTimers = new Map<string, ReturnType<typeof setTimeout>>()
109
110
 
110
111
  blockStateModelInfo = new Map<string, BlockStateModelInfo>()
111
112
 
@@ -115,6 +116,11 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
115
116
 
116
117
  abstract changeBackgroundColor(color: [number, number, number]): void
117
118
 
119
+ /** Override in subclass to check if any enabled module requires heightmap data */
120
+ protected anyModuleRequiresHeightmap(): boolean {
121
+ return false
122
+ }
123
+
118
124
  worldRendererConfig: WorldRendererConfig
119
125
  playerStateReactive: PlayerStateReactive
120
126
  playerStateUtils: PlayerStateUtils
@@ -279,7 +285,11 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
279
285
 
280
286
  onReactiveConfigUpdated<T extends keyof typeof this.worldRendererConfig>(key: T, callback: (value: typeof this.worldRendererConfig[T]) => void) {
281
287
  callback(this.worldRendererConfig[key])
282
- subscribeKey(this.worldRendererConfig, key, callback)
288
+ if ((key as any) === '*') {
289
+ subscribe(this.worldRendererConfig, callback as any)
290
+ } else {
291
+ subscribeKey(this.worldRendererConfig, key, callback)
292
+ }
283
293
  }
284
294
 
285
295
  onReactiveDebugUpdated<T extends keyof typeof this.reactiveDebugParams>(key: T, callback: (value: typeof this.reactiveDebugParams[T]) => void) {
@@ -665,6 +675,13 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
665
675
 
666
676
  removeColumn(x, z) {
667
677
  delete this.loadedChunks[`${x},${z}`]
678
+ // Cancel any pending heightmap debounce for this chunk
679
+ const debounceKey = `${x},${z}`
680
+ const pendingTimer = this.heightmapDebounceTimers.get(debounceKey)
681
+ if (pendingTimer) {
682
+ clearTimeout(pendingTimer)
683
+ this.heightmapDebounceTimers.delete(debounceKey)
684
+ }
668
685
  for (const worker of this.workers) {
669
686
  worker.postMessage({ type: 'unloadChunk', x, z })
670
687
  }
@@ -849,10 +866,18 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
849
866
  customBlockModels
850
867
  })
851
868
  }
852
- // Re-request heightmap for the affected chunk after block change
853
- const chunkCornerX = Math.floor(pos.x / CHUNK_SIZE) * CHUNK_SIZE
854
- const chunkCornerZ = Math.floor(pos.z / CHUNK_SIZE) * CHUNK_SIZE
855
- this.workers[0].postMessage({ type: 'getHeightmap', x: chunkCornerX, z: chunkCornerZ })
869
+ // Re-request heightmap for the affected chunk after block change (debounced)
870
+ if (this.anyModuleRequiresHeightmap()) {
871
+ const chunkCornerX = Math.floor(pos.x / CHUNK_SIZE) * CHUNK_SIZE
872
+ const chunkCornerZ = Math.floor(pos.z / CHUNK_SIZE) * CHUNK_SIZE
873
+ const chunkKey2 = `${chunkCornerX},${chunkCornerZ}`
874
+ const existing = this.heightmapDebounceTimers.get(chunkKey2)
875
+ if (existing) clearTimeout(existing)
876
+ this.heightmapDebounceTimers.set(chunkKey2, setTimeout(() => {
877
+ this.heightmapDebounceTimers.delete(chunkKey2)
878
+ this.workers[0]?.postMessage({ type: 'getHeightmap', x: chunkCornerX, z: chunkCornerZ })
879
+ }, 100))
880
+ }
856
881
  this.logWorkerWork(`-> blockUpdate ${JSON.stringify({ pos, stateId, customBlockModels })}`)
857
882
  this.setSectionDirty(pos, true, true)
858
883
  if (this.neighborChunkUpdates) {
@@ -1023,6 +1048,12 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
1023
1048
  }
1024
1049
 
1025
1050
  destroy() {
1051
+ // Cancel all pending heightmap debounce timers
1052
+ for (const timer of this.heightmapDebounceTimers.values()) {
1053
+ clearTimeout(timer)
1054
+ }
1055
+ this.heightmapDebounceTimers.clear()
1056
+
1026
1057
  // Stop all workers
1027
1058
  for (const worker of this.workers) {
1028
1059
  worker.terminate()
@@ -48,6 +48,10 @@ export class RainModule implements RendererModuleController {
48
48
  }
49
49
  }
50
50
 
51
+ autoEnableCheck(): boolean {
52
+ return this.worldRenderer.worldRendererConfig.isRaining === true
53
+ }
54
+
51
55
  render?: () => void = () => {
52
56
  if (!this.enabled || !this.instancedMesh) return
53
57
 
@@ -10,6 +10,7 @@ export interface RendererModuleController {
10
10
  dispose(): void
11
11
 
12
12
  enablementCheck?: () => boolean
13
+ autoEnableCheck?: () => boolean // Called when config updates, returns true to enable, false to disable
13
14
  render?: () => void
14
15
  }
15
16
 
@@ -297,11 +297,8 @@ export class WorldRendererThree extends WorldRendererCommon {
297
297
  * Initialize all registered modules
298
298
  */
299
299
  private initializeModules(): void {
300
- for (const [id, module] of Object.entries(this.modules)) {
301
- if (module.manifest.enabledDefault) {
302
- this.enableModule(id)
303
- }
304
- }
300
+ // Use updateModulesFromConfig to handle initial state correctly (respects force states and auto-enable)
301
+ this.updateModulesFromConfig()
305
302
  }
306
303
 
307
304
  /**
@@ -311,6 +308,10 @@ export class WorldRendererThree extends WorldRendererCommon {
311
308
  return this.modules[moduleId]?.controller as T | undefined
312
309
  }
313
310
 
311
+ protected override anyModuleRequiresHeightmap(): boolean {
312
+ return Object.values(this.modules).some(m => m.enabled && m.manifest.requiresHeightmap)
313
+ }
314
+
314
315
  get cameraObject() {
315
316
  return this.cameraGroupVr ?? this.cameraContainer
316
317
  }
@@ -408,6 +409,61 @@ export class WorldRendererThree extends WorldRendererCommon {
408
409
  this.onReactiveConfigUpdated('defaultSkybox', (value) => {
409
410
  this.skyboxRenderer.updateDefaultSkybox(value)
410
411
  })
412
+
413
+ // Watch for config changes that affect modules
414
+ this.onReactiveConfigUpdated('*' as any, () => {
415
+ this.updateModulesFromConfig()
416
+ })
417
+
418
+ // Initial update
419
+ this.updateModulesFromConfig()
420
+ }
421
+
422
+ /**
423
+ * Update module states based on config (force states and auto-enable checks)
424
+ */
425
+ private updateModulesFromConfig(): void {
426
+ const { moduleStates } = this.worldRendererConfig
427
+
428
+ for (const [moduleId, module] of Object.entries(this.modules)) {
429
+ const forceState = moduleStates[moduleId]
430
+
431
+ // Check force states first
432
+ if (forceState === 'enabled') {
433
+ if (!module.enabled) {
434
+ this.toggleModule(moduleId, true)
435
+ }
436
+ continue
437
+ }
438
+
439
+ if (forceState === 'disabled') {
440
+ if (module.enabled && !module.manifest.cannotBeDisabled) {
441
+ this.toggleModule(moduleId, false)
442
+ }
443
+ continue
444
+ }
445
+
446
+ // Auto mode: use autoEnableCheck if available, otherwise use enabledDefault
447
+ if (forceState === 'auto' || forceState === undefined) {
448
+ if (module.controller.autoEnableCheck) {
449
+ const shouldEnable = module.controller.autoEnableCheck()
450
+
451
+ if (shouldEnable && !module.enabled) {
452
+ this.toggleModule(moduleId, true)
453
+ } else if (!shouldEnable && module.enabled && !module.manifest.cannotBeDisabled) {
454
+ this.toggleModule(moduleId, false)
455
+ }
456
+ } else {
457
+ // No autoEnableCheck: use enabledDefault
458
+ const shouldEnable = module.manifest.enabledDefault ?? false
459
+ if (shouldEnable && !module.enabled) {
460
+ this.toggleModule(moduleId, true)
461
+ } else if (!shouldEnable && module.enabled && !module.manifest.cannotBeDisabled) {
462
+ this.toggleModule(moduleId, false)
463
+ }
464
+ }
465
+ }
466
+ }
411
467
  }
412
468
 
413
469
  changeHandSwingingState(isAnimationPlaying: boolean, isLeft = false) {
@@ -7,9 +7,9 @@
7
7
  * Input: Serialized chunk data as TypedArrays
8
8
  * Output: Geometry data (positions, normals, colors, uvs, indices)
9
9
  */
10
- export function generate_geometry(section_x: number, section_y: number, section_z: number, section_height: number, world_min_y: number, world_max_y: number, block_states: Uint16Array, block_light: Uint8Array, sky_light: Uint8Array, biomes: Uint8Array, invisible_blocks: Uint16Array, transparent_blocks: Uint16Array, no_ao_blocks: Uint16Array, cull_identical_blocks: Uint16Array, occluding_blocks: Uint16Array, enable_lighting: boolean, smooth_lighting: boolean, sky_light_value: number): any;
10
+ export function generate_geometry(section_x: number, section_y: number, section_z: number, section_height: number, world_min_y: number, world_max_y: number, section_data_start_y: number, block_states: Uint16Array, block_light: Uint8Array, sky_light: Uint8Array, biomes: Uint8Array, invisible_blocks: Uint16Array, transparent_blocks: Uint16Array, no_ao_blocks: Uint16Array, cull_identical_blocks: Uint16Array, occluding_blocks: Uint16Array, enable_lighting: boolean, smooth_lighting: boolean, sky_light_value: number): any;
11
11
 
12
- export function generate_geometry_multi(section_x: number, section_y: number, section_z: number, section_height: number, world_min_y: number, world_max_y: number, chunk_xs: Int32Array, chunk_zs: Int32Array, block_states: Uint16Array, block_light: Uint8Array, sky_light: Uint8Array, biomes: Uint8Array, invisible_blocks: Uint16Array, transparent_blocks: Uint16Array, no_ao_blocks: Uint16Array, cull_identical_blocks: Uint16Array, occluding_blocks: Uint16Array, enable_lighting: boolean, smooth_lighting: boolean, sky_light_value: number): any;
12
+ export function generate_geometry_multi(section_x: number, section_y: number, section_z: number, section_height: number, world_min_y: number, world_max_y: number, section_data_start_y: number, chunk_xs: Int32Array, chunk_zs: Int32Array, block_states: Uint16Array, block_light: Uint8Array, sky_light: Uint8Array, biomes: Uint8Array, invisible_blocks: Uint16Array, transparent_blocks: Uint16Array, no_ao_blocks: Uint16Array, cull_identical_blocks: Uint16Array, occluding_blocks: Uint16Array, enable_lighting: boolean, smooth_lighting: boolean, sky_light_value: number): any;
13
13
 
14
14
  export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
15
15
 
@@ -227,6 +227,7 @@ let WASM_VECTOR_LEN = 0;
227
227
  * @param {number} section_height
228
228
  * @param {number} world_min_y
229
229
  * @param {number} world_max_y
230
+ * @param {number} section_data_start_y
230
231
  * @param {Uint16Array} block_states
231
232
  * @param {Uint8Array} block_light
232
233
  * @param {Uint8Array} sky_light
@@ -241,13 +242,14 @@ let WASM_VECTOR_LEN = 0;
241
242
  * @param {number} sky_light_value
242
243
  * @returns {any}
243
244
  */
244
- export function generate_geometry(section_x, section_y, section_z, section_height, world_min_y, world_max_y, block_states, block_light, sky_light, biomes, invisible_blocks, transparent_blocks, no_ao_blocks, cull_identical_blocks, occluding_blocks, enable_lighting, smooth_lighting, sky_light_value) {
245
+ export function generate_geometry(section_x, section_y, section_z, section_height, world_min_y, world_max_y, section_data_start_y, block_states, block_light, sky_light, biomes, invisible_blocks, transparent_blocks, no_ao_blocks, cull_identical_blocks, occluding_blocks, enable_lighting, smooth_lighting, sky_light_value) {
245
246
  _assertNum(section_x);
246
247
  _assertNum(section_y);
247
248
  _assertNum(section_z);
248
249
  _assertNum(section_height);
249
250
  _assertNum(world_min_y);
250
251
  _assertNum(world_max_y);
252
+ _assertNum(section_data_start_y);
251
253
  const ptr0 = passArray16ToWasm0(block_states, wasm.__wbindgen_malloc);
252
254
  const len0 = WASM_VECTOR_LEN;
253
255
  const ptr1 = passArray8ToWasm0(block_light, wasm.__wbindgen_malloc);
@@ -269,7 +271,7 @@ export function generate_geometry(section_x, section_y, section_z, section_heigh
269
271
  _assertBoolean(enable_lighting);
270
272
  _assertBoolean(smooth_lighting);
271
273
  _assertNum(sky_light_value);
272
- const ret = wasm.generate_geometry(section_x, section_y, section_z, section_height, world_min_y, world_max_y, ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3, ptr4, len4, ptr5, len5, ptr6, len6, ptr7, len7, ptr8, len8, enable_lighting, smooth_lighting, sky_light_value);
274
+ const ret = wasm.generate_geometry(section_x, section_y, section_z, section_height, world_min_y, world_max_y, section_data_start_y, ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3, ptr4, len4, ptr5, len5, ptr6, len6, ptr7, len7, ptr8, len8, enable_lighting, smooth_lighting, sky_light_value);
273
275
  return ret;
274
276
  }
275
277
 
@@ -296,13 +298,14 @@ export function generate_geometry(section_x, section_y, section_z, section_heigh
296
298
  * @param {number} sky_light_value
297
299
  * @returns {any}
298
300
  */
299
- export function generate_geometry_multi(section_x, section_y, section_z, section_height, world_min_y, world_max_y, chunk_xs, chunk_zs, block_states, block_light, sky_light, biomes, invisible_blocks, transparent_blocks, no_ao_blocks, cull_identical_blocks, occluding_blocks, enable_lighting, smooth_lighting, sky_light_value) {
301
+ export function generate_geometry_multi(section_x, section_y, section_z, section_height, world_min_y, world_max_y, section_data_start_y, chunk_xs, chunk_zs, block_states, block_light, sky_light, biomes, invisible_blocks, transparent_blocks, no_ao_blocks, cull_identical_blocks, occluding_blocks, enable_lighting, smooth_lighting, sky_light_value) {
300
302
  _assertNum(section_x);
301
303
  _assertNum(section_y);
302
304
  _assertNum(section_z);
303
305
  _assertNum(section_height);
304
306
  _assertNum(world_min_y);
305
307
  _assertNum(world_max_y);
308
+ _assertNum(section_data_start_y);
306
309
  const ptr0 = passArray32ToWasm0(chunk_xs, wasm.__wbindgen_malloc);
307
310
  const len0 = WASM_VECTOR_LEN;
308
311
  const ptr1 = passArray32ToWasm0(chunk_zs, wasm.__wbindgen_malloc);
@@ -328,7 +331,7 @@ export function generate_geometry_multi(section_x, section_y, section_z, section
328
331
  _assertBoolean(enable_lighting);
329
332
  _assertBoolean(smooth_lighting);
330
333
  _assertNum(sky_light_value);
331
- const ret = wasm.generate_geometry_multi(section_x, section_y, section_z, section_height, world_min_y, world_max_y, ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3, ptr4, len4, ptr5, len5, ptr6, len6, ptr7, len7, ptr8, len8, ptr9, len9, ptr10, len10, enable_lighting, smooth_lighting, sky_light_value);
334
+ const ret = wasm.generate_geometry_multi(section_x, section_y, section_z, section_height, world_min_y, world_max_y, section_data_start_y, ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3, ptr4, len4, ptr5, len5, ptr6, len6, ptr7, len7, ptr8, len8, ptr9, len9, ptr10, len10, enable_lighting, smooth_lighting, sky_light_value);
332
335
  return ret;
333
336
  }
334
337