spoint 0.1.691 → 0.1.693

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 CHANGED
@@ -3244,6 +3244,11 @@ client.connect().then(async ()=>{
3244
3244
  const { createMeshDebugPanel } = await import('./hud/MeshDebugPanel.js')
3245
3245
  window.__app.meshDebugPanel = createMeshDebugPanel(uiRoot)
3246
3246
  }
3247
+ // Optional on-screen render-toggle panel (?debugpanel=1) -- see client/hud/RenderDebugPanel.js.
3248
+ if (_params.has('debugpanel')) {
3249
+ const { createRenderDebugPanel } = await import('./hud/RenderDebugPanel.js')
3250
+ window.__app.renderDebugPanel = createRenderDebugPanel(uiRoot, RenderControls)
3251
+ }
3247
3252
  // Joiner side: WireweaveJoinClient.js's connect() (already resolved by the time
3248
3253
  // this .then() runs) constructs its own bridge and exposes it on window.__app.wireweave.
3249
3254
  // Mount the same room-wide voice widget the host gets below.
@@ -62,8 +62,14 @@ const PRESETS = {
62
62
  grassWind: true,
63
63
  halfResWater: false,
64
64
  thc: false,
65
- ssao: true,
66
- bloom: true,
65
+ // ssao/bloom OFF by default on every tier (2026-08-10): live user-witnessed the half-res SSAO
66
+ // upsample producing a persistent dark ground-level smear/ghosting artifact anchored to nearby
67
+ // geometry silhouettes (confirmed via the live ?debugpanel=1 toggle A/B -- disabling ssao alone
68
+ // removed it). Both remain fully wired, live-toggleable RenderControls knobs (window.__renderControls
69
+ // .set('ssao', true) / the debug panel checkbox) for whoever fixes the underlying half-res-upsample
70
+ // artifact next -- this only flips the SHIPPED default, not the feature.
71
+ ssao: false,
72
+ bloom: false,
67
73
  ssr: false,
68
74
  toneMappingExposure: 1.0,
69
75
  },
@@ -78,8 +84,8 @@ const PRESETS = {
78
84
  grassWind: true,
79
85
  halfResWater: false,
80
86
  thc: false,
81
- ssao: true,
82
- bloom: true,
87
+ ssao: false,
88
+ bloom: false,
83
89
  ssr: false,
84
90
  toneMappingExposure: 1.0,
85
91
  },
@@ -94,8 +100,8 @@ const PRESETS = {
94
100
  grassWind: true,
95
101
  halfResWater: false,
96
102
  thc: false,
97
- ssao: true,
98
- bloom: true,
103
+ ssao: false,
104
+ bloom: false,
99
105
  ssr: true,
100
106
  toneMappingExposure: 1.0,
101
107
  },
@@ -412,6 +412,8 @@ const FLAGS = [
412
412
  doc: 'Join an existing wireweave room (given by ?room=) as a peer, instead of hosting a new one.' },
413
413
  { flag: '?meshdebug', kind: 'query', group: 'networking', readAt: 'client/app.js, client/hud/MeshDebugPanel.js',
414
414
  doc: 'Mount a live P2P mesh-topology panel (peer list, connection states, current app-layer host) on top of a ?room= session. window.__meshDebug.list()/.snapshot() are always available regardless of this flag (see client/core/MeshDebug.js) -- this only adds the on-screen visual.' },
415
+ { flag: '?debugpanel=1', kind: 'query', group: 'render-debug', readAt: 'client/app.js, client/hud/RenderDebugPanel.js',
416
+ doc: 'Mount an on-screen checkbox panel for live A/B-testing render subsystem suspects (sun shadow, SSAO, decal system, grass decals, shadow cascades, player VAT) without console commands -- built for live-tester bisection of visual artifacts.' },
415
417
  { flag: '?netsim=<preset>', kind: 'query', group: 'networking', readAt: 'client/app.js, src/transport/NetworkSimTransport.js',
416
418
  doc: 'Transport simulation harness: wraps the real WebSocket/WebTransport connection in NetworkSimTransport, injecting loss/latency/jitter/reorder so netcode is tuned against a real degraded link instead of localhost. Preset names: clean, broadbandGood, wifiTypical, cellular4g, roadmapTarget (150ms/3% loss), degradedWan, brutal (see NETWORK_SIM_PRESETS). Live-retune after connecting via window.__netSim.configure({lossPct,latencyMs,jitterMs,reorderPct}); inspect drop/reorder counts via window.__netSim.getStats().' },
417
419
  { flag: '?world=<name>', kind: 'query', group: 'world', readAt: 'client/app.js',
@@ -68,7 +68,18 @@ export function createShadowPipeline(sun, opts = {}) {
68
68
  const scene = opts.scene || sun.parent || null
69
69
  const baseExtent = Number.isFinite(opts.extent) ? opts.extent : 60
70
70
  const requestedCascades = Number.isFinite(opts.cascades) ? opts.cascades : 1
71
- const cascadeCount = Math.max(1, Math.min(MAX_CASCADES, Math.round(requestedCascades)))
71
+ // Cascade 0 IS `sun` (see the loop below) -- if sun.castShadow is false (the documented tree-flicker
72
+ // workaround, SceneSetup.js's sun.castShadow=false), cascade 0's light never renders a shadow map at
73
+ // all, so its shadow.needsUpdate stays true forever and _lastSnapped/target are never placed
74
+ // (_updateCascade's `if (!light.castShadow) return false` early-return, hit every call). A >1 cascade
75
+ // pipeline built on top of that still installs the per-fragment CascadeShadowSelect blend shader,
76
+ // which samples directionalShadowMap[0] -- an uninitialized/never-rendered depth texture -- for every
77
+ // fragment within cascade 0's band (near geometry: the player, close trees, close architecture),
78
+ // producing a visible ghosting/duplication artifact live-witnessed as a real ~2-week-old user-reported
79
+ // bug (2026-08-09). A multi-cascade pipeline is only coherent when every cascade actually renders;
80
+ // clamp to 1 cascade whenever sun.castShadow is off so this file's own multi-cascade code path is
81
+ // never reached with a permanently-dead cascade 0 in it.
82
+ const cascadeCount = sun.castShadow === false ? 1 : Math.max(1, Math.min(MAX_CASCADES, Math.round(requestedCascades)))
72
83
  // Sun-from-target offset: DIRECTION is the live light direction, LENGTH places the ortho camera.
73
84
  const offset = (opts.offset && opts.offset.isVector3) ? opts.offset.clone() : new THREE.Vector3(40, 80, 30)
74
85
  let offsetLen = offset.length()
@@ -0,0 +1,99 @@
1
+ // Live render-toggle debug panel (?debugpanel=1) -- lets a live tester binary-search a visual
2
+ // artifact by flipping known suspect systems on/off, one at a time, without console commands.
3
+ // Built for the 2026-08-09 shadow/decal-ghosting investigation: every toggle below is a candidate
4
+ // this session ruled in/out by code inspection but could not confirm live (fps-degraded automation
5
+ // tab, screenshot tooling only partially working) -- this panel lets the user do the live A/B
6
+ // themselves and report which toggle changes the artifact.
7
+
8
+ function ensureStyle() {
9
+ if (document.getElementById('render-debug-panel-style')) return
10
+ const s = document.createElement('style')
11
+ s.id = 'render-debug-panel-style'
12
+ s.textContent = `
13
+ .rdp-card{position:fixed;top:max(8px,env(safe-area-inset-top));right:max(8px,env(safe-area-inset-right));z-index:1000;width:min(280px,calc(100vw - 16px));pointer-events:all;display:flex;flex-direction:column;gap:4px;padding:10px;background:rgba(10,12,18,0.92);border:1px solid rgba(255,255,255,0.15);border-radius:8px;font:12px/1.5 ui-monospace,monospace;color:#e6e6e6}
14
+ .rdp-card .rdp-h{font-size:13px;font-weight:600;margin-bottom:4px}
15
+ .rdp-card label{display:flex;align-items:center;gap:6px;cursor:pointer;padding:2px 0}
16
+ .rdp-card input{cursor:pointer}
17
+ `
18
+ document.head.appendChild(s)
19
+ }
20
+
21
+ // Each toggle: label, get() current state, set(bool) apply. Wraps whatever mechanism the target
22
+ // system actually uses (window.__renderControls for registered CONTROLS keys, a raw window.__flag
23
+ // for systems with no RenderControls entry yet).
24
+ function _toggles(renderControls) {
25
+ return [
26
+ {
27
+ label: 'Sun shadow (hostShadowOff)',
28
+ get: () => !(typeof window !== 'undefined' && window.__hostShadowOff),
29
+ set: (on) => { window.__hostShadowOff = !on },
30
+ },
31
+ {
32
+ label: 'SSAO',
33
+ get: () => !!renderControls?.get('ssao'),
34
+ set: (on) => renderControls?.set('ssao', on),
35
+ },
36
+ {
37
+ label: 'Decal system (bullet holes/tracers)',
38
+ get: () => !(typeof window !== 'undefined' && window.__decalsOff),
39
+ set: (on) => {
40
+ window.__decalsOff = !on
41
+ const ds = window.__app?.decals
42
+ if (ds && ds._pool) { for (const d of ds._pool) if (d.mesh) d.mesh.visible = on && d.mesh.visible }
43
+ },
44
+ },
45
+ {
46
+ label: 'Grass decals (scorch/trample overlay)',
47
+ get: () => !(typeof window !== 'undefined' && window.__grassDecalsOff),
48
+ set: (on) => { window.__grassDecalsOff = !on },
49
+ },
50
+ {
51
+ label: 'Shadow cascades: force to 1',
52
+ get: () => (window.__shadowPipeline?.cascadeCount ?? 1) <= 1,
53
+ set: (on) => {
54
+ if (!on) return
55
+ const sp = window.__shadowPipeline
56
+ if (sp && sp.lights) for (let i = 1; i < sp.lights.length; i++) sp.lights[i].castShadow = false
57
+ },
58
+ },
59
+ {
60
+ label: 'Player VAT crowd render',
61
+ get: () => !(typeof window !== 'undefined' && window.__playerVatOff),
62
+ set: (on) => { window.__playerVatOff = !on },
63
+ },
64
+ ]
65
+ }
66
+
67
+ export function createRenderDebugPanel(uiRoot, renderControls) {
68
+ ensureStyle()
69
+ const card = document.createElement('div')
70
+ card.className = 'rdp-card'
71
+ uiRoot.appendChild(card)
72
+
73
+ const toggles = _toggles(renderControls)
74
+ const title = document.createElement('div')
75
+ title.className = 'rdp-h'
76
+ title.textContent = 'Render Debug Toggles'
77
+ card.appendChild(title)
78
+
79
+ const hint = document.createElement('div')
80
+ hint.style.cssText = 'color:#999;font-size:11px;margin-bottom:6px'
81
+ hint.textContent = 'Uncheck one at a time to find what causes the ground-mark artifact.'
82
+ card.appendChild(hint)
83
+
84
+ for (const t of toggles) {
85
+ const label = document.createElement('label')
86
+ const input = document.createElement('input')
87
+ input.type = 'checkbox'
88
+ input.checked = t.get()
89
+ input.addEventListener('change', () => t.set(input.checked))
90
+ label.appendChild(input)
91
+ label.appendChild(document.createTextNode(t.label))
92
+ card.appendChild(label)
93
+ }
94
+
95
+ return {
96
+ node: card,
97
+ destroy() { card.remove() },
98
+ }
99
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spoint",
3
- "version": "0.1.691",
3
+ "version": "0.1.693",
4
4
  "description": "Physics and netcode SDK for multiplayer game servers",
5
5
  "type": "module",
6
6
  "workspaces": [