spoint 0.1.685 → 0.1.686
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/client/app.js +22 -15
- package/package.json +1 -1
package/client/app.js
CHANGED
|
@@ -905,7 +905,7 @@ const engineCtx = {
|
|
|
905
905
|
get worldConfig() { return worldConfig }, get inputConfig() { return inputConfig },
|
|
906
906
|
playerVrms: pm.playerVrms, entityAppMap, kit: _designKit,
|
|
907
907
|
network: { send: msg => client.send(0x33, msg) },
|
|
908
|
-
setInputConfig(cfg) { Object.assign(inputConfig,cfg); if (!inputConfig.pointerLock) { if (clickPrompt) clickPrompt.style.display='none'; if (document.pointerLockElement)
|
|
908
|
+
setInputConfig(cfg) { Object.assign(inputConfig,cfg); if (!inputConfig.pointerLock) { if (clickPrompt) clickPrompt.style.display='none'; if (document.pointerLockElement) _safeExitPointerLock() } },
|
|
909
909
|
players: { getMesh: id=>pm.playerMeshes.get(id), getState: id=>pm.playerStates.get(id), getAnimator: id=>pm.playerAnimators.get(id), setExpression: (id,n,v)=>pm.setVRMExpression(id,n,v), setAiming: (id,v)=>{ const s=pm.playerStates.get(id); if (s) s._aiming=v } },
|
|
910
910
|
// Pooled hit decals + hitscan tracers (roadmap #48/#93). point/normal/origin/target are [x,y,z] world-space.
|
|
911
911
|
decals: { spawnDecal: (point, normal) => decalSystem.spawnDecal(point, normal), spawnTracer: (origin, target) => decalSystem.spawnTracer(origin, target) },
|
|
@@ -1941,7 +1941,7 @@ editor.onSelectionChange((id,data) => {
|
|
|
1941
1941
|
// selectEntity's machine.send('DESELECT')) clears this client's presence for every other connected editor.
|
|
1942
1942
|
editorPresence.sendPresence(id, false)
|
|
1943
1943
|
})
|
|
1944
|
-
editor.onEditModeChange(on => { cam.setEditMode(on, pm.playerMeshes.get(client.playerId)); if (on) { if (document.pointerLockElement)
|
|
1944
|
+
editor.onEditModeChange(on => { cam.setEditMode(on, pm.playerMeshes.get(client.playerId)); if (on) { if (document.pointerLockElement) _safeExitPointerLock(); editPanel.show(); client.send(MSG.SCENE_GRAPH,{}); client.send(MSG.LIST_APPS,{}) } else { editPanel.hide(); editorPresence.hide(); editorPresence.sendPresence(null, false) } })
|
|
1945
1945
|
// Reverse sync: editor.js's own Y/Alt+P keyboard shortcuts change _gizmoSpace/_pivotMode directly
|
|
1946
1946
|
// (drag math needs zero round-trip latency), so mirror the toolbar's displayed value after the fact.
|
|
1947
1947
|
editor.onGizmoSpaceChange(space => { try { editPanel.setGizmoSpace(space) } catch (_) {} })
|
|
@@ -2200,11 +2200,24 @@ document.addEventListener('keydown', e => { const _mod=e.ctrlKey||e.metaKey; con
|
|
|
2200
2200
|
// Match/Invite Friends, shown on Esc when pointer-lock exits DURING ACTIVE GAMEPLAY (not the
|
|
2201
2201
|
// initial click-to-play prompt -- _hasEverLocked below distinguishes the two, since pointerlockchange
|
|
2202
2202
|
// fires on both the very first click-prompt dismissal and every later in-match Esc).
|
|
2203
|
+
// document.exitPointerLock() can throw the same class of error as requestPointerLock() in unusual
|
|
2204
|
+
// document/element states (observed live: "The root document of this element is not valid for
|
|
2205
|
+
// pointer lock" from an exitPointerLock() call, not just request). 5 call sites across this file
|
|
2206
|
+
// called it raw; centralize the guard here so every future call site gets it for free.
|
|
2207
|
+
function _safeExitPointerLock() {
|
|
2208
|
+
try { document.exitPointerLock() } catch (e) { console.warn('[input] exitPointerLock failed:', e?.message || e) }
|
|
2209
|
+
}
|
|
2210
|
+
function _safeRequestPointerLock() {
|
|
2211
|
+
try {
|
|
2212
|
+
const p = renderer.domElement.requestPointerLock()
|
|
2213
|
+
if (p && typeof p.catch === 'function') p.catch(e => console.warn('[input] requestPointerLock rejected:', e?.message || e))
|
|
2214
|
+
} catch (e) { console.warn('[input] requestPointerLock failed:', e?.message || e) }
|
|
2215
|
+
}
|
|
2203
2216
|
const settingsMenu = createSettingsMenu({ getCam: () => cam, getRenderer: () => renderer })
|
|
2204
2217
|
if (window.__app) window.__app.settingsMenu = settingsMenu
|
|
2205
2218
|
let _hasEverLocked = false
|
|
2206
2219
|
const pauseMenu = createPauseMenu({
|
|
2207
|
-
requestPointerLock:
|
|
2220
|
+
requestPointerLock: _safeRequestPointerLock,
|
|
2208
2221
|
settingsMenu,
|
|
2209
2222
|
getRoomInfo: () => _wwRoom ? { code: _wwRoom, joinLink: `${location.origin}${location.pathname}?wwjoin&room=${_wwRoom}` } : null,
|
|
2210
2223
|
})
|
|
@@ -2219,7 +2232,7 @@ document.addEventListener('keydown', e => {
|
|
|
2219
2232
|
// Editor has its own Esc-adjacent flows (deselect etc.) via editor.onKeyDown above; don't also pop
|
|
2220
2233
|
// the gameplay pause menu while editing, and don't pop it over the lobby overlay either.
|
|
2221
2234
|
if (clientMachine.isEditor || clientMachine.isLobby) return
|
|
2222
|
-
if (_hasEverLocked && document.pointerLockElement === renderer.domElement) {
|
|
2235
|
+
if (_hasEverLocked && document.pointerLockElement === renderer.domElement) { _safeExitPointerLock() }
|
|
2223
2236
|
})
|
|
2224
2237
|
// Spectator mode entry/exit + follow-target-cycle keybinds. O enters spectator (S/Space are already
|
|
2225
2238
|
// gameplay movement keys); inside spectator: F toggles free<->follow, [ and ] cycle the follow target.
|
|
@@ -2247,7 +2260,7 @@ function _setViewportRelaxed(on) { if (_viewportMeta) _viewportMeta.setAttribute
|
|
|
2247
2260
|
clientMachine.subscribe(() => {
|
|
2248
2261
|
const wantLobby = clientMachine.isLobby
|
|
2249
2262
|
if (wantLobby && !(_lobby && _lobby.isOpen)) {
|
|
2250
|
-
if (document.pointerLockElement)
|
|
2263
|
+
if (document.pointerLockElement) _safeExitPointerLock()
|
|
2251
2264
|
_setViewportRelaxed(true)
|
|
2252
2265
|
// Re-check isLobby after the async import gap in case the player toggled again before it resolved.
|
|
2253
2266
|
_getLobby().then(lobby => { if (clientMachine.isLobby && !lobby.isOpen) lobby.open() })
|
|
@@ -2274,7 +2287,7 @@ let _specWasSpectator = false
|
|
|
2274
2287
|
clientMachine.subscribe(() => {
|
|
2275
2288
|
const isSpec = clientMachine.isSpectator
|
|
2276
2289
|
if (isSpec && !_specWasSpectator) {
|
|
2277
|
-
if (document.pointerLockElement)
|
|
2290
|
+
if (document.pointerLockElement) _safeExitPointerLock()
|
|
2278
2291
|
if (clientMachine.spectatorSubmode === 'free') cam.setEditMode(true)
|
|
2279
2292
|
} else if (!isSpec && _specWasSpectator) {
|
|
2280
2293
|
cam.setEditMode(false)
|
|
@@ -2426,15 +2439,9 @@ renderer.domElement.addEventListener('click', ()=>{
|
|
|
2426
2439
|
if (!inputConfig.pointerLock || document.pointerLockElement) return
|
|
2427
2440
|
// requestPointerLock() can both throw synchronously AND return a Promise that rejects
|
|
2428
2441
|
// asynchronously (spec-dependent by browser/context) -- e.g. "The root document of this
|
|
2429
|
-
// element is not valid for pointer lock".
|
|
2430
|
-
//
|
|
2431
|
-
|
|
2432
|
-
// live even after the sync try/catch was added). Guard both paths. The keydown fallback below
|
|
2433
|
-
// still recovers gameplay either way.
|
|
2434
|
-
try {
|
|
2435
|
-
const p = renderer.domElement.requestPointerLock()
|
|
2436
|
-
if (p && typeof p.catch === 'function') p.catch(e => console.warn('[input] requestPointerLock rejected:', e?.message || e))
|
|
2437
|
-
} catch (e) { console.warn('[input] requestPointerLock failed:', e?.message || e) }
|
|
2442
|
+
// element is not valid for pointer lock". _safeRequestPointerLock guards both. The keydown
|
|
2443
|
+
// fallback below still recovers gameplay either way.
|
|
2444
|
+
_safeRequestPointerLock()
|
|
2438
2445
|
})
|
|
2439
2446
|
// requestPointerLock() is a trusted-gesture-gated browser API that can silently reject (denied
|
|
2440
2447
|
// engagement heuristic, automation-driven click, embedded/iframe context) with no visible error --
|