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.
Files changed (98) hide show
  1. package/dist/lightOwnerWorker.js +9 -0
  2. package/dist/lightOwnerWorker.js.map +7 -0
  3. package/dist/mesher.js +1 -1
  4. package/dist/mesher.js.map +2 -2
  5. package/dist/mesherWasm.js +222 -222
  6. package/dist/minecraft-renderer.js +105 -76
  7. package/dist/minecraft-renderer.js.meta.json +1 -1
  8. package/dist/threeWorker.js +537 -508
  9. package/package.json +1 -1
  10. package/src/bundler/bundlePrepare.test.ts +25 -0
  11. package/src/bundler/bundlePrepare.ts +1 -1
  12. package/src/graphicsBackend/appViewer.ts +15 -0
  13. package/src/graphicsBackend/config.ts +8 -0
  14. package/src/graphicsBackend/rendererDefaultOptions.test.ts +51 -0
  15. package/src/graphicsBackend/rendererDefaultOptions.ts +18 -1
  16. package/src/graphicsBackend/rendererOptionsSync.ts +4 -3
  17. package/src/lib/blockEntityLightRegistry.ts +4 -0
  18. package/src/lib/skyLight.ts +17 -0
  19. package/src/lib/worldrendererCommon.lightOwner.test.ts +916 -0
  20. package/src/lib/worldrendererCommon.removeColumn.test.ts +16 -0
  21. package/src/lib/worldrendererCommon.ts +340 -67
  22. package/src/lighting/cache/rendererLightCache.test.ts +158 -0
  23. package/src/lighting/cache/rendererLightCache.ts +248 -0
  24. package/src/lighting/coordinator/clientLightCoordinator.test.ts +102 -0
  25. package/src/lighting/coordinator/clientLightCoordinator.ts +574 -0
  26. package/src/lighting/debug/clientLightProbe.test.ts +24 -0
  27. package/src/lighting/debug/clientLightProbe.ts +20 -0
  28. package/src/lighting/debug/clientLightTrace.test.ts +271 -0
  29. package/src/lighting/debug/clientLightTrace.ts +335 -0
  30. package/src/lighting/entity/entityLightController.test.ts +216 -0
  31. package/src/lighting/entity/entityLightController.ts +322 -0
  32. package/src/lighting/owner/clientLightOwner.test.ts +467 -0
  33. package/src/lighting/owner/clientLightOwner.ts +523 -0
  34. package/src/lighting/owner/lightOwnerHost.test.ts +78 -0
  35. package/src/lighting/owner/lightOwnerHost.ts +202 -0
  36. package/src/lighting/owner/lightOwnerWorker.ts +154 -0
  37. package/src/lighting/owner/updateLight1171OwnerPath.test.ts +68 -0
  38. package/src/lighting/owner/updateLightToOwnerEvents.ts +49 -0
  39. package/src/lighting/tables/lightTables1171.generated.json +10 -0
  40. package/src/lighting/tables/lightTables1171.test.ts +168 -0
  41. package/src/lighting/tables/lightTables1171.ts +59 -0
  42. package/src/lighting/versioning/clientLightVersions.test.ts +241 -0
  43. package/src/lighting/versioning/clientLightVersions.ts +203 -0
  44. package/src/lighting/versioning/pendingSectionFlush.test.ts +214 -0
  45. package/src/lighting/versioning/pendingSectionFlush.ts +123 -0
  46. package/src/mesher-shared/lightNibblePack.ts +26 -0
  47. package/src/mesher-shared/mcDataWorkerShim.cjs +9 -7
  48. package/src/mesher-shared/modelsGeometryCommon.test.ts +6 -1
  49. package/src/mesher-shared/shared.ts +48 -1
  50. package/src/three/chunkMeshManager.ts +26 -0
  51. package/src/three/entities.ts +97 -8
  52. package/src/three/entity/EntityMesh.ts +1 -0
  53. package/src/three/entity/legWalkAnimation.test.ts +154 -0
  54. package/src/three/entity/legWalkAnimation.ts +193 -0
  55. package/src/three/globalBlockBuffer.ts +94 -2
  56. package/src/three/globalLegacyBuffer.ts +94 -2
  57. package/src/three/graphicsBackendBase.ts +2 -9
  58. package/src/three/modules/clouds.ts +191 -0
  59. package/src/three/modules/index.ts +4 -0
  60. package/src/three/modules/sunMoon.ts +151 -0
  61. package/src/three/skyboxRenderer.ts +15 -8
  62. package/src/three/tests/clouds.test.ts +59 -0
  63. package/src/three/tests/globalBlockBuffer.test.ts +98 -0
  64. package/src/three/tests/globalLegacyBuffer.test.ts +108 -0
  65. package/src/three/tests/waterFog.test.ts +25 -0
  66. package/src/three/waterFog.ts +28 -0
  67. package/src/three/worldRendererThree.ts +84 -39
  68. package/src/wasm-mesher/bridge/render-from-wasm.ts +46 -1
  69. package/src/wasm-mesher/runtime-build/wasm_mesher.d.ts +31 -5
  70. package/src/wasm-mesher/runtime-build/wasm_mesher.js +180 -5
  71. package/src/wasm-mesher/runtime-build/wasm_mesher_bg.wasm +0 -0
  72. package/src/wasm-mesher/runtime-build/wasm_mesher_bg.wasm.d.ts +11 -0
  73. package/src/wasm-mesher/tests/mesherLightDependencies.test.ts +83 -0
  74. package/src/wasm-mesher/tests/mesherWasmColumnTickSelect.test.ts +39 -0
  75. package/src/wasm-mesher/tests/mesherWasmConversionCache.test.ts +30 -1
  76. package/src/wasm-mesher/tests/mesherWasmTickParseCache.test.ts +73 -0
  77. package/src/wasm-mesher/tests/mesherWasmTickSchedule.test.ts +51 -0
  78. package/src/wasm-mesher/tests/mesherWasmTopologyPostCache.test.ts +78 -0
  79. package/src/wasm-mesher/worker/clientLightDarkPublication.probe.test.ts +32 -0
  80. package/src/wasm-mesher/worker/mesherLightDependencies.ts +43 -0
  81. package/src/wasm-mesher/worker/mesherWasm.ownerLightSync.test.ts +198 -0
  82. package/src/wasm-mesher/worker/mesherWasm.ts +506 -96
  83. package/src/wasm-mesher/worker/mesherWasmChunkBuffer.ts +1 -0
  84. package/src/wasm-mesher/worker/mesherWasmColumnTickSelect.ts +47 -0
  85. package/src/wasm-mesher/worker/mesherWasmConversionCache.ts +16 -5
  86. package/src/wasm-mesher/worker/mesherWasmLightDirty.test.ts +17 -1
  87. package/src/wasm-mesher/worker/mesherWasmLightDirty.ts +10 -0
  88. package/src/wasm-mesher/worker/mesherWasmLightMerge.test.ts +208 -0
  89. package/src/wasm-mesher/worker/mesherWasmLightMerge.ts +228 -0
  90. package/src/wasm-mesher/worker/mesherWasmOwnerLight.test.ts +161 -0
  91. package/src/wasm-mesher/worker/mesherWasmOwnerLight.ts +112 -0
  92. package/src/wasm-mesher/worker/mesherWasmTickParseCache.ts +45 -0
  93. package/src/wasm-mesher/worker/mesherWasmTickSchedule.ts +44 -0
  94. package/src/wasm-mesher/worker/mesherWasmTopologyPostCache.ts +63 -0
  95. package/src/worldView/types.ts +3 -0
  96. package/src/worldView/worldView.geometryReload.test.ts +68 -0
  97. package/src/worldView/worldView.spiral.test.ts +1 -1
  98. package/src/worldView/worldView.ts +49 -1
@@ -0,0 +1,32 @@
1
+ //@ts-nocheck
2
+ import { describe, it, expect } from 'vitest'
3
+ import { applyOwnerPublicationToColumnCaches } from './mesherWasmOwnerLight'
4
+ import { displayLightColumn } from './mesherWasmLightMerge'
5
+
6
+ describe('dark publication omission diagnostic (current behavior)', () => {
7
+ const blockOnly = { sx: 0, sy: 0, sz: 0, blockLight: new Uint8Array(2048) }
8
+ const base = { worldMinY: 0, columnWorldX: 0, columnWorldZ: 0, numSections: 2 }
9
+ it('displays sky 15 when initial publication omits sky and no incoming light exists', () => {
10
+ const result = applyOwnerPublicationToColumnCaches({ ...base, incoming: undefined, display: undefined, sections: [blockOnly] })
11
+ expect(
12
+ displayLightColumn(result.display, 'sky')
13
+ .subarray(0, 4096)
14
+ .every(value => value === 15)
15
+ ).toBe(true)
16
+ })
17
+ it('retains dark sky when an incoming authoritative cache exists', () => {
18
+ const initial = applyOwnerPublicationToColumnCaches({
19
+ ...base,
20
+ incoming: undefined,
21
+ display: undefined,
22
+ sections: [{ ...blockOnly, skyLight: new Uint8Array(2048) }]
23
+ }).display
24
+ const result = applyOwnerPublicationToColumnCaches({ ...base, incoming: initial, display: initial, sections: [blockOnly] })
25
+ expect(
26
+ displayLightColumn(result.display, 'sky')
27
+ .subarray(0, 4096)
28
+ .every(value => value === 0)
29
+ ).toBe(true)
30
+ expect(result.display).not.toBe(initial)
31
+ })
32
+ })
@@ -0,0 +1,43 @@
1
+ //@ts-nocheck
2
+ /**
3
+ * Light-version stamp for one section mesh.
4
+ *
5
+ * Each column keeps only the latest applied owner version. The stamp is the
6
+ * newest of those versions whose sampled columns were actually meshed, and it
7
+ * stops before the first sampled column that was written but left out. The
8
+ * version requested on the dirty job is not an input.
9
+ */
10
+
11
+ export function sectionLightSampleColumns(sx: number, sz: number): string[] {
12
+ const columns: string[] = []
13
+ for (let dx = -16; dx <= 16; dx += 16) {
14
+ for (let dz = -16; dz <= 16; dz += 16) {
15
+ columns.push(`${sx + dx},${sz + dz}`)
16
+ }
17
+ }
18
+ return columns
19
+ }
20
+
21
+ export function provenMeshLightVersion(opts: {
22
+ sx: number
23
+ sz: number
24
+ usedColumns: ReadonlySet<string>
25
+ appliedVersionByColumn: ReadonlyMap<string, number>
26
+ }): number {
27
+ let gap = Number.POSITIVE_INFINITY
28
+ let covered = 0
29
+ for (const column of sectionLightSampleColumns(opts.sx, opts.sz)) {
30
+ const applied = opts.appliedVersionByColumn.get(column)
31
+ if (applied == null) continue
32
+ if (!opts.usedColumns.has(column)) gap = Math.min(gap, applied)
33
+ else covered = Math.max(covered, applied)
34
+ }
35
+ if (!Number.isFinite(gap)) return covered
36
+ let belowGap = 0
37
+ for (const column of sectionLightSampleColumns(opts.sx, opts.sz)) {
38
+ const applied = opts.appliedVersionByColumn.get(column)
39
+ if (applied == null || !opts.usedColumns.has(column) || applied >= gap) continue
40
+ belowGap = Math.max(belowGap, applied)
41
+ }
42
+ return belowGap
43
+ }
@@ -0,0 +1,198 @@
1
+ //@ts-nocheck
2
+ import MinecraftData from 'minecraft-data'
3
+ import Chunks from 'prismarine-chunk'
4
+ import { Vec3 } from 'vec3'
5
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
6
+ import { packUnpackedLightSection } from '../../mesher-shared/lightNibblePack'
7
+ import { LIGHT_BLOCKS_PER_SECTION, maskBitSet, worldSectionMaskBit, type ParsedUpdateLight } from './mesherWasmLightMerge'
8
+
9
+ // Drives the real worker message handler. The legacy mesh path reads light
10
+ // through `World.getChannelLightNorm`, so that is what these tests observe.
11
+
12
+ const { parseUpdateLightV17 } = vi.hoisted(() => ({ parseUpdateLightV17: vi.fn() }))
13
+
14
+ vi.mock('../runtime-build/wasm_mesher.js', () => ({
15
+ default: vi.fn(async () => undefined),
16
+ parseUpdateLightV17
17
+ }))
18
+ vi.mock('../../mesher-shared/models', async importOriginal => ({
19
+ ...(await importOriginal<object>()),
20
+ setBlockStatesData: vi.fn()
21
+ }))
22
+ vi.mock('./mesherWasmTickSchedule', () => ({
23
+ BULK_MESH_INTERVAL_MS: 50,
24
+ createMeshTickScheduler: () => ({ kick: vi.fn() })
25
+ }))
26
+
27
+ const VERSION = '1.17.1'
28
+ const NUM_SECTIONS = 16
29
+ const COLUMN_X = 16
30
+ const COLUMN_Z = 32
31
+ const PUBLISHED_Y = 64
32
+ const UNPUBLISHED_Y = 80
33
+ const PUBLISHED_POS = new Vec3(COLUMN_X + 3, PUBLISHED_Y + 1, COLUMN_Z + 5)
34
+ const UNPUBLISHED_POS = new Vec3(COLUMN_X + 3, UNPUBLISHED_Y + 5, COLUMN_Z + 5)
35
+ const INCOMING = {
36
+ [PUBLISHED_Y]: { sky: 3, block: 1 },
37
+ [UNPUBLISHED_Y]: { sky: 9, block: 4 }
38
+ }
39
+
40
+ type WorkerSelf = { onmessage: (event: { data: unknown }) => void }
41
+ type WorldLike = {
42
+ getColumn: (
43
+ x: number,
44
+ z: number
45
+ ) => {
46
+ setSkyLight: (pos: Vec3, light: number) => void
47
+ setBlockLight: (pos: Vec3, light: number) => void
48
+ skyLightSections: Array<unknown | null>
49
+ blockLightSections: Array<unknown | null>
50
+ }
51
+ getChannelLightNorm: (pos: Vec3) => { block: number; sky: number }
52
+ }
53
+
54
+ const send = (data: unknown) => (globalThis.self as unknown as WorkerSelf).onmessage({ data })
55
+ const world = () => (globalThis as unknown as { world: WorldLike }).world
56
+ const lightAt = (pos: Vec3) => world().getChannelLightNorm(pos)
57
+ const expectedLight = ({ sky, block }: { sky: number; block: number }) => ({
58
+ sky: Math.min(15, sky + 2) / 15,
59
+ block: Math.min(15, block + 2) / 15
60
+ })
61
+
62
+ function serverColumnJson() {
63
+ const Chunk = Chunks(VERSION) as any
64
+ const chunk = new Chunk(undefined as any)
65
+ const stone = MinecraftData(VERSION).blocksByName.stone!.defaultState
66
+ chunk.setBlockStateId(new Vec3(3, PUBLISHED_POS.y, 5), stone)
67
+ chunk.setBlockStateId(new Vec3(3, UNPUBLISHED_POS.y, 5), stone)
68
+ return chunk.toJson()
69
+ }
70
+
71
+ function serverLightPacket(): ParsedUpdateLight {
72
+ const columnBytes = NUM_SECTIONS * LIGHT_BLOCKS_PER_SECTION
73
+ const skyLight = new Uint8Array(columnBytes)
74
+ const blockLight = new Uint8Array(columnBytes)
75
+ const skyLightMask = new Uint32Array(2)
76
+ const blockLightMask = new Uint32Array(2)
77
+ for (const [sectionY, light] of Object.entries(INCOMING)) {
78
+ const sectionIndex = Number(sectionY) / 16
79
+ const start = sectionIndex * LIGHT_BLOCKS_PER_SECTION
80
+ skyLight.fill(light.sky, start, start + LIGHT_BLOCKS_PER_SECTION)
81
+ blockLight.fill(light.block, start, start + LIGHT_BLOCKS_PER_SECTION)
82
+ maskBitSet(skyLightMask, worldSectionMaskBit(sectionIndex))
83
+ maskBitSet(blockLightMask, worldSectionMaskBit(sectionIndex))
84
+ }
85
+ return {
86
+ x: COLUMN_X / 16,
87
+ z: COLUMN_Z / 16,
88
+ trustEdges: true,
89
+ numSections: NUM_SECTIONS,
90
+ skyLight,
91
+ blockLight,
92
+ skyLightMask,
93
+ blockLightMask,
94
+ emptySkyLightMask: new Uint32Array(2),
95
+ emptyBlockLightMask: new Uint32Array(2)
96
+ }
97
+ }
98
+
99
+ function publishOwnerLight(publicationVersion: number, sectionY: number, light: { sky: number; block: number }) {
100
+ const packed = (value: number) => packUnpackedLightSection(new Uint8Array(LIGHT_BLOCKS_PER_SECTION).fill(value))
101
+ send({
102
+ type: 'applyOwnerLightPublication',
103
+ worldGeneration: 1,
104
+ publicationVersion,
105
+ sections: [{ sx: COLUMN_X, sy: sectionY, sz: COLUMN_Z, blockLight: packed(light.block), skyLight: packed(light.sky) }]
106
+ })
107
+ }
108
+
109
+ /** Moves the JS column away from the caches, so a sync of this section is detectable. */
110
+ function markUnpublishedSectionInJsColumn(light: { sky: number; block: number }) {
111
+ const column = world().getColumn(COLUMN_X, COLUMN_Z)
112
+ const local = new Vec3(UNPUBLISHED_POS.x & 15, UNPUBLISHED_POS.y, UNPUBLISHED_POS.z & 15)
113
+ column.setSkyLight(local, light.sky)
114
+ column.setBlockLight(local, light.block)
115
+ }
116
+
117
+ describe('mesher worker JS column light with the client light owner', () => {
118
+ beforeEach(async () => {
119
+ vi.resetModules()
120
+ parseUpdateLightV17.mockReset()
121
+ parseUpdateLightV17.mockReturnValue(serverLightPacket())
122
+ vi.stubGlobal('self', {})
123
+ vi.stubGlobal('postMessage', vi.fn())
124
+ const realSetInterval = globalThis.setInterval
125
+ vi.stubGlobal('setInterval', vi.fn())
126
+ await import('./mesherWasm')
127
+ vi.stubGlobal('setInterval', realSetInterval)
128
+
129
+ send({ type: 'mesherData', config: { version: VERSION, worldMinY: 0, worldMaxY: 256, enableLighting: true } })
130
+ send({ type: 'chunk', x: COLUMN_X, z: COLUMN_Z, chunk: serverColumnJson() })
131
+ send({ type: 'setUpdateLightV17', rawPacket: new Uint8Array(1), numSections: NUM_SECTIONS })
132
+ await vi.waitFor(() => expect(parseUpdateLightV17).toHaveBeenCalledOnce())
133
+
134
+ expect(lightAt(PUBLISHED_POS)).toEqual(expectedLight(INCOMING[PUBLISHED_Y]))
135
+ expect(lightAt(UNPUBLISHED_POS)).toEqual(expectedLight(INCOMING[UNPUBLISHED_Y]))
136
+ }, 30_000)
137
+
138
+ afterEach(() => {
139
+ vi.unstubAllGlobals()
140
+ delete (globalThis as any).world
141
+ })
142
+
143
+ it('keeps authoritative zero light explicit in initially null sections', () => {
144
+ const sectionY = 96
145
+ const pos = new Vec3(COLUMN_X + 3, sectionY + 1, COLUMN_Z + 5)
146
+ expect(lightAt(pos)).toEqual({ block: 0, sky: 1 })
147
+ publishOwnerLight(1, sectionY, { sky: 0, block: 0 })
148
+ expect(lightAt(pos)).toEqual(expectedLight({ sky: 0, block: 0 }))
149
+ send({ type: 'chunk', x: COLUMN_X, z: COLUMN_Z, chunk: serverColumnJson() })
150
+ expect(lightAt(pos)).toEqual(expectedLight({ sky: 0, block: 0 }))
151
+ })
152
+
153
+ it('allocates only the authoritative channel and leaves omitted sections null', () => {
154
+ const sectionY = 96
155
+ send({
156
+ type: 'applyOwnerLightPublication',
157
+ worldGeneration: 1,
158
+ publicationVersion: 1,
159
+ sections: [{ sx: COLUMN_X, sy: sectionY, sz: COLUMN_Z, blockLight: new Uint8Array(2048) }]
160
+ })
161
+ const column = world().getColumn(COLUMN_X, COLUMN_Z)
162
+ expect(column.blockLightSections[sectionY / 16 + 1]).not.toBeNull()
163
+ expect(column.skyLightSections[sectionY / 16 + 1]).toBeNull()
164
+ expect(column.blockLightSections[sectionY / 16 + 2]).toBeNull()
165
+ expect(column.skyLightSections[sectionY / 16 + 2]).toBeNull()
166
+ expect(lightAt(new Vec3(COLUMN_X + 3, sectionY + 17, COLUMN_Z + 5))).toEqual({ block: 0, sky: 1 })
167
+ })
168
+
169
+ it('raises JS light when a publication brightens a section', () => {
170
+ const bright = { sky: 12, block: 7 }
171
+ publishOwnerLight(1, PUBLISHED_Y, bright)
172
+ expect(lightAt(PUBLISHED_POS)).toEqual(expectedLight(bright))
173
+ })
174
+
175
+ it('lowers JS light when a later publication darkens the section', () => {
176
+ const dark = { sky: 0, block: 0 }
177
+ publishOwnerLight(1, PUBLISHED_Y, { sky: 12, block: 7 })
178
+ publishOwnerLight(2, PUBLISHED_Y, dark)
179
+ expect(lightAt(PUBLISHED_POS)).toEqual(expectedLight(dark))
180
+ })
181
+
182
+ it('writes only the published section Ys into the JS column', () => {
183
+ const marker = { sky: 14, block: 13 }
184
+ const bright = { sky: 12, block: 7 }
185
+ markUnpublishedSectionInJsColumn(marker)
186
+ publishOwnerLight(1, PUBLISHED_Y, bright)
187
+ expect(lightAt(PUBLISHED_POS)).toEqual(expectedLight(bright))
188
+ expect(lightAt(UNPUBLISHED_POS)).toEqual(expectedLight(marker))
189
+ })
190
+
191
+ it('restores every incoming section in the JS column on revert', () => {
192
+ publishOwnerLight(1, PUBLISHED_Y, { sky: 12, block: 7 })
193
+ markUnpublishedSectionInJsColumn({ sky: 14, block: 13 })
194
+ send({ type: 'revertOwnerLightToIncoming' })
195
+ expect(lightAt(PUBLISHED_POS)).toEqual(expectedLight(INCOMING[PUBLISHED_Y]))
196
+ expect(lightAt(UNPUBLISHED_POS)).toEqual(expectedLight(INCOMING[UNPUBLISHED_Y]))
197
+ })
198
+ })