minecraft-renderer 0.1.63 → 0.1.64

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.63",
3
+ "version": "0.1.64",
4
4
  "description": "The most Modular Minecraft world renderer with Three.js WebGL backend",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,12 +1,12 @@
1
1
  //@ts-nocheck
2
2
  import {
3
- FUTURISTIC_CAMERA_IDS,
4
- FUTURISTIC_CAMERA_LABELS,
5
- FUTURISTIC_SCENE_IDS,
6
- FUTURISTIC_SCENE_LABELS,
3
+ V2_CAMERA_IDS,
4
+ V2_CAMERA_LABELS,
5
+ V2_SCENE_IDS,
6
+ V2_SCENE_LABELS,
7
7
  MINECRAFT_BLOCK_GROUP_IDS,
8
8
  MINECRAFT_BLOCK_GROUP_LABELS
9
- } from '../three/menuBackground/futuristicMeta'
9
+ } from '../three/menuBackground/v2Meta'
10
10
  import { MENU_BACKGROUND_OPTION_DEFAULTS } from '../three/menuBackground/config'
11
11
  import type { RendererGpuPreference } from '../three/menuBackground/gpuPreference'
12
12
 
@@ -54,11 +54,11 @@ export const RENDERER_DEFAULT_OPTIONS = {
54
54
  defaultSkybox: true as boolean,
55
55
  menuBackgroundMode: MB.mode,
56
56
  menuBackgroundMinecraftTextures: MB.minecraftTextures as boolean,
57
- menuBackgroundFuturisticScene: MB.futuristicScene,
58
- menuBackgroundFuturisticCamera: MB.futuristicCamera,
59
- menuBackgroundFuturisticBlockGroup: MB.futuristicBlockGroup,
60
- menuBackgroundFuturisticCameraSpeed: MB.futuristicCameraSpeedPercent,
61
- menuBackgroundFuturisticBlockSpeed: MB.futuristicBlockSpeedPercent,
57
+ menuBackgroundV2Scene: MB.v2Scene,
58
+ menuBackgroundV2Camera: MB.v2Camera,
59
+ menuBackgroundV2BlockGroup: MB.v2BlockGroup,
60
+ menuBackgroundV2CameraSpeed: MB.v2CameraSpeedPercent,
61
+ menuBackgroundV2BlockSpeed: MB.v2BlockSpeedPercent,
62
62
  rendererFuturisticReveal: false as boolean,
63
63
  rendererPerfDebugOverlay: false as boolean,
64
64
  disableBlockEntityTextures: false as boolean,
@@ -110,37 +110,54 @@ export function migrateRendererOptions(saved: Record<string, unknown>): void {
110
110
  }
111
111
  delete saved.wasmExperimentalMesher
112
112
  delete saved.rendererWasmMesher
113
+
114
+ if (saved.menuBackgroundMode === 'futuristic') {
115
+ saved.menuBackgroundMode = 'v2'
116
+ }
117
+ const futuristicToV2: Array<[string, string]> = [
118
+ ['menuBackgroundFuturisticScene', 'menuBackgroundV2Scene'],
119
+ ['menuBackgroundFuturisticCamera', 'menuBackgroundV2Camera'],
120
+ ['menuBackgroundFuturisticBlockGroup', 'menuBackgroundV2BlockGroup'],
121
+ ['menuBackgroundFuturisticCameraSpeed', 'menuBackgroundV2CameraSpeed'],
122
+ ['menuBackgroundFuturisticBlockSpeed', 'menuBackgroundV2BlockSpeed'],
123
+ ]
124
+ for (const [oldKey, newKey] of futuristicToV2) {
125
+ if (saved[oldKey] !== undefined && saved[newKey] === undefined) {
126
+ saved[newKey] = saved[oldKey]
127
+ }
128
+ delete saved[oldKey]
129
+ }
113
130
  }
114
131
 
115
132
  /** Settings UI metadata for {@link RENDERER_DEFAULT_OPTIONS} keys. */
116
133
  export const RENDERER_OPTIONS_META: Partial<Record<RendererDefaultOptionKey, RendererOptionMeta>> = {
117
134
  menuBackgroundMode: {
118
- possibleValues: [['classic', 'Classic'], ['futuristic', 'Futuristic']],
135
+ possibleValues: [['classic', 'Classic'], ['v2', 'V2']],
119
136
  requiresRestart: true
120
137
  },
121
138
  menuBackgroundMinecraftTextures: {
122
139
  text: 'Minecraft block textures',
123
- tooltip: 'Use block atlas on futuristic menu cubes (loads assets on menu)'
140
+ tooltip: 'Use block atlas on V2 menu cubes (loads assets on menu)'
124
141
  },
125
- menuBackgroundFuturisticScene: {
126
- possibleValues: FUTURISTIC_SCENE_IDS.map(id => [id, FUTURISTIC_SCENE_LABELS[id]] as [string, string])
142
+ menuBackgroundV2Scene: {
143
+ possibleValues: V2_SCENE_IDS.map(id => [id, V2_SCENE_LABELS[id]] as [string, string])
127
144
  },
128
- menuBackgroundFuturisticCamera: {
129
- possibleValues: FUTURISTIC_CAMERA_IDS.map(id => [id, FUTURISTIC_CAMERA_LABELS[id]] as [string, string])
145
+ menuBackgroundV2Camera: {
146
+ possibleValues: V2_CAMERA_IDS.map(id => [id, V2_CAMERA_LABELS[id]] as [string, string])
130
147
  },
131
- menuBackgroundFuturisticBlockGroup: {
148
+ menuBackgroundV2BlockGroup: {
132
149
  possibleValues: MINECRAFT_BLOCK_GROUP_IDS.map(id => [id, MINECRAFT_BLOCK_GROUP_LABELS[id]] as [string, string]),
133
150
  text: 'Block pool',
134
151
  tooltip: 'Block set for textured menu cubes (requires Minecraft textures)'
135
152
  },
136
- menuBackgroundFuturisticCameraSpeed: {
153
+ menuBackgroundV2CameraSpeed: {
137
154
  text: 'Camera speed',
138
155
  tooltip: 'Orbit / fly-through camera path speed. 0 freezes the path; mouse parallax still works.',
139
156
  min: 0,
140
157
  max: 200,
141
158
  unit: '%'
142
159
  },
143
- menuBackgroundFuturisticBlockSpeed: {
160
+ menuBackgroundV2BlockSpeed: {
144
161
  text: 'Block speed',
145
162
  tooltip: 'Floating blocks and sky rotation. Independent of camera path speed.',
146
163
  min: 0,
@@ -315,11 +332,11 @@ export const RENDERER_RENDER_GUI_SECTIONS: ReadonlyArray<{
315
332
  keys: [
316
333
  'menuBackgroundMode',
317
334
  'menuBackgroundMinecraftTextures',
318
- 'menuBackgroundFuturisticScene',
319
- 'menuBackgroundFuturisticCamera',
320
- 'menuBackgroundFuturisticBlockGroup',
321
- 'menuBackgroundFuturisticCameraSpeed',
322
- 'menuBackgroundFuturisticBlockSpeed'
335
+ 'menuBackgroundV2Scene',
336
+ 'menuBackgroundV2Camera',
337
+ 'menuBackgroundV2BlockGroup',
338
+ 'menuBackgroundV2CameraSpeed',
339
+ 'menuBackgroundV2BlockSpeed'
323
340
  ]
324
341
  },
325
342
  {
@@ -11,7 +11,7 @@ import { rendererShaderCubeDebugModeToValue } from './rendererDefaultOptions'
11
11
  import type { MenuBackgroundOptions } from '../three/menuBackground/types'
12
12
  import type { MenuBackgroundRenderer } from '../three/menuBackground/renderer'
13
13
  import { menuBackgroundSpeedToMultiplier } from '../three/menuBackground/config'
14
- import type { FuturisticCameraId, FuturisticSceneId, MinecraftBlockGroupId } from '../three/menuBackground/futuristic'
14
+ import type { V2CameraId, V2SceneId, MinecraftBlockGroupId } from '../three/menuBackground/v2'
15
15
  import { setSkinsConfig } from '../lib/utils/skins'
16
16
 
17
17
  export type { RendererStorageOptions } from './rendererDefaultOptions'
@@ -37,20 +37,20 @@ export function menuBackgroundOptionsFromStorage(o: Pick<
37
37
  RendererStorageOptions,
38
38
  | 'menuBackgroundMode'
39
39
  | 'menuBackgroundMinecraftTextures'
40
- | 'menuBackgroundFuturisticScene'
41
- | 'menuBackgroundFuturisticCamera'
42
- | 'menuBackgroundFuturisticBlockGroup'
43
- | 'menuBackgroundFuturisticCameraSpeed'
44
- | 'menuBackgroundFuturisticBlockSpeed'
40
+ | 'menuBackgroundV2Scene'
41
+ | 'menuBackgroundV2Camera'
42
+ | 'menuBackgroundV2BlockGroup'
43
+ | 'menuBackgroundV2CameraSpeed'
44
+ | 'menuBackgroundV2BlockSpeed'
45
45
  >): MenuBackgroundOptions {
46
46
  return {
47
47
  mode: o.menuBackgroundMode as MenuBackgroundOptions['mode'],
48
48
  useMinecraftTextures: o.menuBackgroundMinecraftTextures,
49
- futuristicScene: o.menuBackgroundFuturisticScene as FuturisticSceneId,
50
- futuristicCamera: o.menuBackgroundFuturisticCamera as FuturisticCameraId,
51
- futuristicBlockGroup: o.menuBackgroundFuturisticBlockGroup as MinecraftBlockGroupId,
52
- futuristicCameraSpeed: menuBackgroundSpeedToMultiplier(o.menuBackgroundFuturisticCameraSpeed),
53
- futuristicBlockSpeed: menuBackgroundSpeedToMultiplier(o.menuBackgroundFuturisticBlockSpeed),
49
+ v2Scene: o.menuBackgroundV2Scene as V2SceneId,
50
+ v2Camera: o.menuBackgroundV2Camera as V2CameraId,
51
+ v2BlockGroup: o.menuBackgroundV2BlockGroup as MinecraftBlockGroupId,
52
+ v2CameraSpeed: menuBackgroundSpeedToMultiplier(o.menuBackgroundV2CameraSpeed),
53
+ v2BlockSpeed: menuBackgroundSpeedToMultiplier(o.menuBackgroundV2BlockSpeed),
54
54
  }
55
55
  }
56
56
 
@@ -58,20 +58,20 @@ export function applyMenuBackgroundLiveOptions(
58
58
  menu: MenuBackgroundRenderer,
59
59
  o: Pick<
60
60
  RendererStorageOptions,
61
- | 'menuBackgroundFuturisticScene'
62
- | 'menuBackgroundFuturisticCamera'
63
- | 'menuBackgroundFuturisticBlockGroup'
64
- | 'menuBackgroundFuturisticCameraSpeed'
65
- | 'menuBackgroundFuturisticBlockSpeed'
61
+ | 'menuBackgroundV2Scene'
62
+ | 'menuBackgroundV2Camera'
63
+ | 'menuBackgroundV2BlockGroup'
64
+ | 'menuBackgroundV2CameraSpeed'
65
+ | 'menuBackgroundV2BlockSpeed'
66
66
  >
67
67
  ): void {
68
- const futuristic = menu.futuristic
69
- if (!futuristic) return
70
- futuristic.setScene?.(o.menuBackgroundFuturisticScene)
71
- futuristic.setCamera?.(o.menuBackgroundFuturisticCamera)
72
- void futuristic.setBlockGroup?.(o.menuBackgroundFuturisticBlockGroup)
73
- futuristic.setCameraSpeed?.(menuBackgroundSpeedToMultiplier(o.menuBackgroundFuturisticCameraSpeed))
74
- futuristic.setBlockSpeed?.(menuBackgroundSpeedToMultiplier(o.menuBackgroundFuturisticBlockSpeed))
68
+ const v2 = menu.v2
69
+ if (!v2) return
70
+ v2.setScene?.(o.menuBackgroundV2Scene)
71
+ v2.setCamera?.(o.menuBackgroundV2Camera)
72
+ void v2.setBlockGroup?.(o.menuBackgroundV2BlockGroup)
73
+ v2.setCameraSpeed?.(menuBackgroundSpeedToMultiplier(o.menuBackgroundV2CameraSpeed))
74
+ v2.setBlockSpeed?.(menuBackgroundSpeedToMultiplier(o.menuBackgroundV2BlockSpeed))
75
75
  }
76
76
 
77
77
  function resolveWasmMesherActive(o: RendererStorageOptions): boolean {
package/src/index.ts CHANGED
@@ -85,21 +85,21 @@ export type {
85
85
  MenuBackgroundMode,
86
86
  MenuBackgroundOptions,
87
87
  MenuBackgroundView,
88
- FuturisticSceneId,
89
- FuturisticCameraId,
90
- FuturisticMenuBackgroundOptions,
88
+ V2SceneId,
89
+ V2CameraId,
90
+ V2MenuBackgroundOptions,
91
91
  MinecraftBlockGroupId
92
92
  } from './three/menuBackground'
93
93
  export {
94
94
  MenuBackgroundRenderer,
95
95
  ClassicMenuBackground,
96
- FuturisticMenuBackground,
96
+ V2MenuBackground,
97
97
  WorldBlocksMenuBackground,
98
98
  MENU_BACKGROUND_MC_VERSION,
99
- FUTURISTIC_SCENE_IDS,
100
- FUTURISTIC_CAMERA_IDS,
101
- FUTURISTIC_SCENE_LABELS,
102
- FUTURISTIC_CAMERA_LABELS,
99
+ V2_SCENE_IDS,
100
+ V2_CAMERA_IDS,
101
+ V2_SCENE_LABELS,
102
+ V2_CAMERA_LABELS,
103
103
  MINECRAFT_BLOCK_GROUPS,
104
104
  MINECRAFT_BLOCK_GROUP_IDS,
105
105
  MINECRAFT_BLOCK_GROUP_LABELS,
@@ -0,0 +1,182 @@
1
+ //@ts-nocheck
2
+ import { EventEmitter } from 'events'
3
+ import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
4
+ import { Vec3 } from 'vec3'
5
+ import { proxy } from 'valtio'
6
+ import { WorldRendererCommon } from './worldrendererCommon'
7
+ import { defaultWorldRendererConfig } from '../graphicsBackend/config'
8
+ import { getInitialPlayerState } from '../playerState/playerState'
9
+ import type { DisplayWorldOptions, GraphicsInitOptions } from '../graphicsBackend/types'
10
+
11
+ vi.mock('./ui/newStats', () => ({
12
+ addNewStat: vi.fn(() => ({ updateText: vi.fn(), setVisibility: vi.fn() })),
13
+ updateStatText: vi.fn(),
14
+ removeAllStats: vi.fn(),
15
+ updatePanesVisibility: vi.fn(),
16
+ MC_RENDERER_DEBUG_OVERLAY_CLASS: 'mc-renderer-debug-overlay',
17
+ }))
18
+
19
+ vi.mock('./utils/skins', () => ({
20
+ setSkinsConfig: vi.fn(),
21
+ steveTexture: {},
22
+ stevePngUrl: '',
23
+ }))
24
+
25
+ function ensurePromiseWithResolvers() {
26
+ if (!Promise.withResolvers) {
27
+ Promise.withResolvers = function <T>() {
28
+ let resolve!: (value: T | PromiseLike<T>) => void
29
+ let reject!: (reason?: unknown) => void
30
+ const promise = new Promise<T>((res, rej) => {
31
+ resolve = res
32
+ reject = rej
33
+ })
34
+ return { promise, resolve, reject }
35
+ }
36
+ }
37
+ }
38
+
39
+ class TestWorldRenderer extends WorldRendererCommon {
40
+ outputFormat = 'threeJs' as const
41
+
42
+ changeBackgroundColor() {}
43
+ changeCardinalLight() {}
44
+ handleWorkerMessage() {}
45
+ updateCamera() {}
46
+ render() {}
47
+ updateShowChunksBorder() {}
48
+ }
49
+
50
+ function createRenderer() {
51
+ const rendererState = proxy({
52
+ world: {
53
+ chunksLoaded: new Set<string>(),
54
+ heightmaps: new Map<string, Int16Array>(),
55
+ allChunksLoaded: false,
56
+ mesherWork: false,
57
+ instabilityFactors: {},
58
+ intersectMedia: null,
59
+ },
60
+ renderer: '',
61
+ preventEscapeMenu: false,
62
+ })
63
+
64
+ const displayOptions = {
65
+ version: '1.21.1',
66
+ worldView: new EventEmitter() as DisplayWorldOptions['worldView'],
67
+ inWorldRenderingConfig: { ...defaultWorldRendererConfig },
68
+ playerStateReactive: getInitialPlayerState(),
69
+ rendererState,
70
+ nonReactiveState: {
71
+ fps: 0,
72
+ worstRenderTime: 0,
73
+ avgRenderTime: 0,
74
+ world: {
75
+ chunksLoaded: new Set<string>(),
76
+ chunksTotalNumber: 0,
77
+ chunksFullInfo: '',
78
+ },
79
+ renderer: {
80
+ timeline: { live: [], frozen: [], lastSecond: [] },
81
+ },
82
+ },
83
+ resourcesManager: {} as DisplayWorldOptions['resourcesManager'],
84
+ }
85
+
86
+ const initOptions: GraphicsInitOptions = {
87
+ config: { sceneBackground: '#000' },
88
+ rendererSpecificSettings: {},
89
+ callbacks: {
90
+ displayCriticalError: vi.fn(),
91
+ setRendererSpecificSettings: vi.fn(),
92
+ fireCustomEvent: vi.fn(),
93
+ },
94
+ }
95
+
96
+ const renderer = new TestWorldRenderer(displayOptions.resourcesManager, displayOptions, initOptions)
97
+ renderer.active = true
98
+ renderer.workers = [{ postMessage: vi.fn() }, { postMessage: vi.fn() }]
99
+ renderer.viewDistance = 16
100
+ renderer.viewerChunkPosition = new Vec3(0, 64, 0)
101
+ renderer.worldSizeParams = { minY: 0, worldHeight: 256 }
102
+ renderer.loadedChunks['160,0'] = true
103
+ return renderer
104
+ }
105
+
106
+ function sectionKeysForColumn(renderer: TestWorldRenderer, x: number, z: number): string[] {
107
+ const keys: string[] = []
108
+ const sectionHeight = renderer.getSectionHeight()
109
+ for (let y = renderer.worldMinYRender; y < renderer.worldSizeParams.worldHeight; y += sectionHeight) {
110
+ keys.push(`${x},${y},${z}`)
111
+ }
112
+ return keys
113
+ }
114
+
115
+ describe('WorldRendererCommon.removeColumn sectionsWaiting reconciliation', () => {
116
+ beforeEach(() => {
117
+ ensurePromiseWithResolvers()
118
+ vi.useFakeTimers()
119
+ vi.stubGlobal('location', { href: 'http://localhost/' })
120
+ })
121
+
122
+ afterEach(() => {
123
+ vi.useRealTimers()
124
+ vi.unstubAllGlobals()
125
+ })
126
+
127
+ test('clears sectionsWaiting when viewDistance gate blocks setSectionDirty(false)', () => {
128
+ const renderer = createRenderer()
129
+ const columnX = 160
130
+ const columnZ = 0
131
+ const sectionPos = new Vec3(columnX, 64, columnZ)
132
+
133
+ renderer.setSectionDirty(sectionPos, true)
134
+ expect(renderer.sectionsWaiting.get(`${columnX},64,${columnZ}`)).toBe(1)
135
+
136
+ renderer.viewDistance = 4
137
+ renderer.removeColumn(columnX, columnZ)
138
+
139
+ for (const key of sectionKeysForColumn(renderer, columnX, columnZ)) {
140
+ expect(renderer.sectionsWaiting.has(key)).toBe(false)
141
+ }
142
+ })
143
+
144
+ test('treats late sectionFinished as a no-op after removeColumn', () => {
145
+ const renderer = createRenderer()
146
+ const sectionKey = '160,64,0'
147
+ const debugSpy = vi.spyOn(console, 'debug').mockImplementation(() => {})
148
+
149
+ renderer.sectionsWaiting.set(sectionKey, 1)
150
+ renderer.viewDistance = 4
151
+ renderer.removeColumn(160, 0)
152
+
153
+ expect(() => {
154
+ renderer.handleMessage({ type: 'sectionFinished', key: sectionKey, workerIndex: 0 })
155
+ }).not.toThrow()
156
+
157
+ expect(renderer.sectionsWaiting.has(sectionKey)).toBe(false)
158
+ expect(debugSpy).toHaveBeenCalledWith(
159
+ expect.stringContaining('sectionFinished for non-outstanding section'),
160
+ )
161
+
162
+ debugSpy.mockRestore()
163
+ })
164
+
165
+ test('clears sectionsWaiting when unload happens before batched dirty flush', () => {
166
+ const renderer = createRenderer()
167
+ renderer.forceCallFromMesherReplayer = false
168
+ const columnX = 160
169
+ const columnZ = 0
170
+
171
+ renderer.setSectionDirty(new Vec3(columnX, 64, columnZ), true)
172
+ expect(renderer.sectionsWaiting.get(`${columnX},64,${columnZ}`)).toBe(1)
173
+
174
+ renderer.viewDistance = 4
175
+ renderer.removeColumn(columnX, columnZ)
176
+ vi.advanceTimersByTime(0)
177
+
178
+ for (const key of sectionKeysForColumn(renderer, columnX, columnZ)) {
179
+ expect(renderer.sectionsWaiting.has(key)).toBe(false)
180
+ }
181
+ })
182
+ })
@@ -424,7 +424,10 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
424
424
  }
425
425
  if (data.type === 'sectionFinished') { // on after load & unload section
426
426
  this.logWorkerWork(`<- ${data.workerIndex} sectionFinished ${data.key} ${JSON.stringify({ processTime: data.processTime })}`)
427
- if (!this.sectionsWaiting.has(data.key)) throw new Error(`sectionFinished event for non-outstanding section ${data.key}`)
427
+ if (!this.sectionsWaiting.has(data.key)) {
428
+ console.debug(`sectionFinished for non-outstanding section ${data.key} (viewDistance=${this.viewDistance})`)
429
+ return
430
+ }
428
431
  this.sectionsWaiting.set(data.key, this.sectionsWaiting.get(data.key)! - 1)
429
432
  if (this.sectionsWaiting.get(data.key) === 0) {
430
433
  this.sectionsWaiting.delete(data.key)
@@ -787,9 +790,11 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
787
790
  this.sectionDirtyPendingArgs.delete(key)
788
791
  }
789
792
  }
790
- for (const worker of this.workers) {
791
- worker.postMessage({ type: 'unloadChunk', x, z })
793
+ for (let i = 0; i < this.workers.length; i++) {
794
+ this.toWorkerMessagesQueue[i] ??= []
795
+ this.toWorkerMessagesQueue[i].push({ type: 'unloadChunk', x, z })
792
796
  }
797
+ this.dispatchMessages()
793
798
  this.logWorkerWork(`-> unloadChunk ${JSON.stringify({ x, z })}`)
794
799
  delete this.finishedChunks[`${x},${z}`]
795
800
  this.allChunksFinished = Object.keys(this.finishedChunks).length === this.chunksLength
@@ -798,9 +803,14 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
798
803
  this.initialChunkLoadWasStartedIn = undefined
799
804
  }
800
805
  const sectionHeight = this.getSectionHeight()
801
- for (let y = this.worldSizeParams.minY; y < this.worldSizeParams.worldHeight; y += sectionHeight) {
802
- this.setSectionDirty(new Vec3(x, y, z), false)
803
- delete this.finishedSections[`${x},${y},${z}`]
806
+ for (let y = this.worldMinYRender; y < this.worldSizeParams.worldHeight; y += sectionHeight) {
807
+ const sectionKey = `${x},${y},${z}`
808
+ const waitingCount = this.sectionsWaiting.get(sectionKey)
809
+ if (waitingCount !== undefined && waitingCount > 0) {
810
+ console.debug(`[removeColumn] clearing non-zero sectionsWaiting for ${sectionKey}: ${waitingCount} (chunk ${x},${z}, viewDistance=${this.viewDistance})`)
811
+ }
812
+ this.sectionsWaiting.delete(sectionKey)
813
+ delete this.finishedSections[sectionKey]
804
814
  }
805
815
  this.highestBlocksByChunks.delete(`${x},${z}`)
806
816
  const heightmapKey = `${Math.floor(x / 16)},${Math.floor(z / 16)}`
@@ -1393,8 +1393,20 @@ export class Entities {
1393
1393
  })
1394
1394
  .start()
1395
1395
  }
1396
- if (typeof entity.yaw === 'number' && Number.isFinite(entity.yaw)) {
1397
- const dy = shortestYawRadians(e.rotation.y, entity.yaw)
1396
+ /** World yaw for the whole model: for PlayerObject skins, rotate body to head look dir; head mesh stays yaw-fixed (pitch only). */
1397
+ let targetYaw: number | undefined
1398
+ if (e.playerObject && overrides?.rotation?.head) {
1399
+ const hy = overrides.rotation.head.y
1400
+ const headYawWorld =
1401
+ typeof hy === 'number' && Number.isFinite(hy) ? hy : entity.yaw
1402
+ if (typeof headYawWorld === 'number' && Number.isFinite(headYawWorld)) {
1403
+ targetYaw = headYawWorld
1404
+ }
1405
+ } else if (typeof entity.yaw === 'number' && Number.isFinite(entity.yaw)) {
1406
+ targetYaw = entity.yaw
1407
+ }
1408
+ if (typeof targetYaw === 'number' && Number.isFinite(targetYaw)) {
1409
+ const dy = shortestYawRadians(e.rotation.y, targetYaw)
1398
1410
  // Stop previous rotation tween to prevent accumulation (mirror _posTween)
1399
1411
  e.userData._rotTween?.stop()
1400
1412
  e.userData._rotTween = new TWEEN.Tween(e.rotation)
@@ -1404,14 +1416,7 @@ export class Entities {
1404
1416
 
1405
1417
  if (e?.playerObject && overrides?.rotation?.head) {
1406
1418
  const { playerObject } = e
1407
- const hy = overrides.rotation.head.y
1408
- const headYawWorld =
1409
- typeof hy === 'number' && Number.isFinite(hy) ? hy : entity.yaw
1410
- const headYawOffset =
1411
- typeof headYawWorld === 'number' && typeof entity.yaw === 'number' && Number.isFinite(headYawWorld) && Number.isFinite(entity.yaw)
1412
- ? shortestYawRadians(entity.yaw, headYawWorld)
1413
- : 0
1414
- playerObject.skin.head.rotation.y = headYawOffset
1419
+ playerObject.skin.head.rotation.y = 0
1415
1420
 
1416
1421
  const hp = overrides.rotation.head.x
1417
1422
  playerObject.skin.head.rotation.x =
@@ -1,7 +1,7 @@
1
1
  //@ts-nocheck
2
2
  import * as THREE from 'three'
3
3
 
4
- /** Contract for a main-menu background implementation (classic cubemap, futuristic scene, etc.). */
4
+ /** Contract for a main-menu background implementation (classic cubemap, v2 scene, etc.). */
5
5
  export interface MenuBackgroundView {
6
6
  readonly scene: THREE.Scene
7
7
  readonly camera: THREE.PerspectiveCamera
@@ -1,23 +1,23 @@
1
1
  //@ts-nocheck
2
2
  import type { MenuBackgroundMode } from './types'
3
- import type { FuturisticCameraId, FuturisticSceneId, MinecraftBlockGroupId } from './futuristic'
3
+ import type { V2CameraId, V2SceneId, MinecraftBlockGroupId } from './v2'
4
4
 
5
5
  /** Single source of truth for menu-background defaults (settings + runtime fallbacks). */
6
6
  export const MENU_BACKGROUND_OPTION_DEFAULTS = {
7
- mode: 'futuristic' as MenuBackgroundMode,
7
+ mode: 'v2' as MenuBackgroundMode,
8
8
  minecraftTextures: true as boolean,
9
- futuristicScene: 'light' as FuturisticSceneId,
10
- futuristicCamera: 'dive' as FuturisticCameraId,
11
- futuristicBlockGroup: 'stainedGlass' as MinecraftBlockGroupId,
9
+ v2Scene: 'light' as V2SceneId,
10
+ v2Camera: 'dive' as V2CameraId,
11
+ v2BlockGroup: 'stainedGlass' as MinecraftBlockGroupId,
12
12
  /** 0–200 (%). 100 = 1× motion. */
13
- futuristicCameraSpeedPercent: 80,
14
- futuristicBlockSpeedPercent: 40
13
+ v2CameraSpeedPercent: 80,
14
+ v2BlockSpeedPercent: 40
15
15
  } as const
16
16
 
17
17
  export const menuBackgroundSpeedToMultiplier = (percent: number) => percent / 100
18
18
 
19
19
  /** Default camera / block motion multipliers (1 = 100%). */
20
20
  export const MENU_BACKGROUND_MOTION_DEFAULTS = {
21
- camera: menuBackgroundSpeedToMultiplier(MENU_BACKGROUND_OPTION_DEFAULTS.futuristicCameraSpeedPercent),
22
- block: menuBackgroundSpeedToMultiplier(MENU_BACKGROUND_OPTION_DEFAULTS.futuristicBlockSpeedPercent)
21
+ camera: menuBackgroundSpeedToMultiplier(MENU_BACKGROUND_OPTION_DEFAULTS.v2CameraSpeedPercent),
22
+ block: menuBackgroundSpeedToMultiplier(MENU_BACKGROUND_OPTION_DEFAULTS.v2BlockSpeedPercent)
23
23
  } as const
@@ -6,21 +6,21 @@ export type { MenuBackgroundMode, MenuBackgroundOptions } from './types'
6
6
  export { resolveMenuBackgroundMode } from './types'
7
7
  export { ClassicMenuBackground } from './classic'
8
8
  export type {
9
- FuturisticSceneId,
10
- FuturisticCameraId,
11
- FuturisticMenuBackgroundOptions,
9
+ V2SceneId,
10
+ V2CameraId,
11
+ V2MenuBackgroundOptions,
12
12
  MinecraftBlockGroupId
13
- } from './futuristic'
13
+ } from './v2'
14
14
  export {
15
- FuturisticMenuBackground,
16
- FUTURISTIC_SCENE_IDS,
17
- FUTURISTIC_CAMERA_IDS,
18
- FUTURISTIC_SCENE_LABELS,
19
- FUTURISTIC_CAMERA_LABELS,
15
+ V2MenuBackground,
16
+ V2_SCENE_IDS,
17
+ V2_CAMERA_IDS,
18
+ V2_SCENE_LABELS,
19
+ V2_CAMERA_LABELS,
20
20
  MINECRAFT_BLOCK_GROUPS,
21
21
  MINECRAFT_BLOCK_GROUP_IDS,
22
22
  MINECRAFT_BLOCK_GROUP_LABELS
23
- } from './futuristic'
23
+ } from './v2'
24
24
  export { WorldBlocksMenuBackground } from './worldBlocks'
25
25
  export { MenuBackgroundRenderer } from './renderer'
26
26
  export {
@@ -3,14 +3,14 @@ import * as THREE from 'three'
3
3
  import type { GraphicsInitOptions } from '../../graphicsBackend/types'
4
4
  import type { DocumentRenderer } from '../documentRenderer'
5
5
  import { ClassicMenuBackground } from './classic'
6
- import { FuturisticMenuBackground } from './futuristic'
6
+ import { V2MenuBackground } from './v2'
7
7
  import { WorldBlocksMenuBackground } from './worldBlocks'
8
8
  import type { MenuBackgroundView } from './activeView'
9
9
  import type { MenuBackgroundOptions } from './types'
10
10
  import { resolveMenuBackgroundMode } from './types'
11
11
 
12
12
  /**
13
- * Orchestrates main-menu background rendering (dispatches to classic / futuristic / world-blocks).
13
+ * Orchestrates main-menu background rendering (dispatches to classic / v2 / world-blocks).
14
14
  */
15
15
  export class MenuBackgroundRenderer {
16
16
  private active?: MenuBackgroundView
@@ -27,9 +27,9 @@ export class MenuBackgroundRenderer {
27
27
  this.mode = resolveMenuBackgroundMode(menuBackgroundOptions, singleFileBuild)
28
28
  }
29
29
 
30
- /** Active futuristic instance when that style is running. */
31
- get futuristic(): FuturisticMenuBackground | undefined {
32
- return this.active instanceof FuturisticMenuBackground ? this.active : undefined
30
+ /** Active v2 instance when that style is running. */
31
+ get v2(): V2MenuBackground | undefined {
32
+ return this.active instanceof V2MenuBackground ? this.active : undefined
33
33
  }
34
34
 
35
35
  get scene(): THREE.Scene | undefined {
@@ -64,16 +64,16 @@ export class MenuBackgroundRenderer {
64
64
 
65
65
  private createImplementation(options: MenuBackgroundOptions): MenuBackgroundView {
66
66
  switch (this.mode) {
67
- case 'futuristic':
68
- return new FuturisticMenuBackground(
67
+ case 'v2':
68
+ return new V2MenuBackground(
69
69
  this.documentRenderer,
70
70
  {
71
71
  useMinecraftTextures: options.useMinecraftTextures,
72
- initialScene: options.futuristicScene,
73
- initialCamera: options.futuristicCamera,
74
- initialBlockGroup: options.futuristicBlockGroup,
75
- initialCameraSpeed: options.futuristicCameraSpeed,
76
- initialBlockSpeed: options.futuristicBlockSpeed,
72
+ initialScene: options.v2Scene,
73
+ initialCamera: options.v2Camera,
74
+ initialBlockGroup: options.v2BlockGroup,
75
+ initialCameraSpeed: options.v2CameraSpeed,
76
+ initialBlockSpeed: options.v2BlockSpeed,
77
77
  resourcesManager: options.resourcesManager
78
78
  },
79
79
  this.abortController.signal
@@ -1,25 +1,25 @@
1
1
  //@ts-nocheck
2
2
  import type { ResourcesManager } from '../../resourcesManager/resourcesManager'
3
- import type { FuturisticCameraId, FuturisticSceneId, MinecraftBlockGroupId } from './futuristic'
3
+ import type { V2CameraId, V2SceneId, MinecraftBlockGroupId } from './v2'
4
4
  import { MENU_BACKGROUND_OPTION_DEFAULTS } from './config'
5
5
 
6
- export type { FuturisticCameraId, FuturisticSceneId, MinecraftBlockGroupId } from './futuristic'
6
+ export type { V2CameraId, V2SceneId, MinecraftBlockGroupId } from './v2'
7
7
 
8
- export type MenuBackgroundMode = 'classic' | 'futuristic' | 'worldBlocks'
8
+ export type MenuBackgroundMode = 'classic' | 'v2' | 'worldBlocks'
9
9
 
10
10
  export interface MenuBackgroundOptions {
11
11
  /** Visual style. Defaults to {@link MENU_BACKGROUND_OPTION_DEFAULTS.mode}, or `worldBlocks` in single-file build. */
12
12
  mode?: MenuBackgroundMode
13
- /** Futuristic style: load block atlas and render textured cubes (requires assets / mcData). */
13
+ /** V2 style: load block atlas and render textured cubes (requires assets / mcData). */
14
14
  useMinecraftTextures?: boolean
15
- futuristicScene?: FuturisticSceneId
16
- futuristicCamera?: FuturisticCameraId
15
+ v2Scene?: V2SceneId
16
+ v2Camera?: V2CameraId
17
17
  /** Block pool when {@link useMinecraftTextures} is enabled. */
18
- futuristicBlockGroup?: MinecraftBlockGroupId
18
+ v2BlockGroup?: MinecraftBlockGroupId
19
19
  /** Camera path speed (1 = 100%). */
20
- futuristicCameraSpeed?: number
20
+ v2CameraSpeed?: number
21
21
  /** Block fly-through + sky drift speed (1 = 100%). */
22
- futuristicBlockSpeed?: number
22
+ v2BlockSpeed?: number
23
23
  /**
24
24
  * Optional shared resource manager (e.g. appViewer.resourcesManager).
25
25
  * Caller should run `updateAssetsData` after mcData is loaded when using textured cubes.