spoint 0.1.673 → 0.1.675

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.
@@ -227,8 +227,12 @@ export class BrowserServer extends BaseClient {
227
227
  }
228
228
  }
229
229
 
230
+ // Matches relative ('./x', '../x') AND root-absolute ('/x') import specifiers: the gh-pages deploy's
231
+ // path-patch step (gh-pages.yml "Patch paths for gh-pages") absolutizes deep relative src imports
232
+ // (apps/**/*.js climbing out via '../../src/...') into '/spoint/src/...' at build time, so a shipped
233
+ // app's own source can carry either form depending on how many directories deep it lives.
230
234
  async function _resolveRelativeDeps(source, baseUrl, seen = new Map()) {
231
- const re = /(?:from|import)\s*['"](\.[^'"]+)['"]/g
235
+ const re = /(?:from|import)\s*['"](\.[^'"]+|\/[^'"]+)['"]/g
232
236
  const out = {}
233
237
  const tasks = []
234
238
  let m
package/client/app.js CHANGED
@@ -2753,6 +2753,10 @@ function buildFrameSectionNodes() {
2753
2753
  {
2754
2754
  id: 'frame-clock',
2755
2755
  reads: [], writes: ['frameDt', 'isEditorFrame', 'lerpFactor', 'localId'],
2756
+ // lerpFactor is a debug mirror (inspectable via window.__renderGraph) with no downstream graph
2757
+ // reader by design; frameDt/isEditorFrame/localId ARE read elsewhere, so only lerpFactor is
2758
+ // exempt here, not the whole node.
2759
+ debugMirrors: ['lerpFactor'],
2756
2760
  run(ctx) {
2757
2761
  const now = ctx.now
2758
2762
  ctx.res.frameDt = Math.min(Math.max((now - lastFrameTime) / 1000, 0.001), 0.1)
@@ -2797,6 +2801,9 @@ function buildFrameSectionNodes() {
2797
2801
  {
2798
2802
  id: 'scene-graph-tick',
2799
2803
  reads: ['frameDt', 'isEditorFrame', 'localId'], writes: ['sceneGraphMoved'],
2804
+ // Intentionally-terminal completion marker: no downstream node reads sceneGraphMoved, it just
2805
+ // records that this frame's scene-graph tick happened (inspectable via window.__renderGraph).
2806
+ terminal: true,
2800
2807
  run(ctx) {
2801
2808
  const lid = ctx.res.localId
2802
2809
  if (_hierarchyDirty && latestState && latestState.entities.length > 0) { el.rebuildEntityHierarchy(latestState.entities); _hierarchyDirty = false }
@@ -2830,6 +2837,9 @@ function buildFrameSectionNodes() {
2830
2837
  {
2831
2838
  id: 'app-dispatch-frame',
2832
2839
  reads: ['frameDt'], writes: ['appFrameDispatched'],
2840
+ // Intentionally-terminal completion marker: no downstream node reads appFrameDispatched, it
2841
+ // just records that this frame's app dispatch happened (inspectable via window.__renderGraph).
2842
+ terminal: true,
2833
2843
  run(ctx) {
2834
2844
  ams.dispatchFrame(ctx.res.frameDt, engineCtx)
2835
2845
  if (engineCtx.facial) engineCtx.facial.update(ctx.res.frameDt)
@@ -2853,6 +2863,10 @@ function buildFrameSectionNodes() {
2853
2863
  {
2854
2864
  id: 'camera-input-update',
2855
2865
  reads: ['frameDt', 'isEditorFrame', 'localId'], writes: ['localState', 'vegFocus'],
2866
+ // localState is a debug mirror (inspectable via window.__renderGraph) with no downstream
2867
+ // graph reader by design; vegFocus IS read (floating-origin-rebase), so only localState is
2868
+ // exempt here, not the whole node.
2869
+ debugMirrors: ['localState'],
2856
2870
  run(ctx) {
2857
2871
  const lid = ctx.res.localId
2858
2872
  const local = client.getLocalState() || pm.playerStates.get(lid)
@@ -2901,6 +2915,9 @@ function buildFrameSectionNodes() {
2901
2915
  // *shape* changes from {position:[...]} to {x,y,z}, both read the same way by consumers below).
2902
2916
  id: 'floating-origin-rebase',
2903
2917
  reads: ['vegFocus'], writes: ['originRebased'],
2918
+ // Intentionally-terminal completion marker: no downstream node reads originRebased, it just
2919
+ // records whether this frame rebased (inspectable via window.__renderGraph).
2920
+ terminal: true,
2904
2921
  run(ctx) {
2905
2922
  const p = camera.position
2906
2923
  const rebased = floatingOrigin.update(p.x, p.y, p.z)
@@ -2962,6 +2979,9 @@ function buildFrameSectionNodes() {
2962
2979
  {
2963
2980
  id: 'shadow-move-gate',
2964
2981
  reads: ['localId'], writes: ['shadowMoved'],
2982
+ // Intentionally-terminal completion marker: no downstream node reads shadowMoved, it just
2983
+ // records whether this frame's shadow camera moved (inspectable via window.__renderGraph).
2984
+ terminal: true,
2965
2985
  run(ctx) {
2966
2986
  const _shadowTgt = pm.playerMeshes.get(ctx.res.localId)?.position || camera.position
2967
2987
  // ShadowPipeline (single owner) follows the player with a TEXEL-SNAPPED shadow camera so the
@@ -77,16 +77,6 @@ export function createRenderGraph(nodes, opts = {}) {
77
77
  if (!readersOf.has(key)) readersOf.set(key, [])
78
78
  readersOf.get(key).push(n.id)
79
79
  }
80
- // written-never-read: a construction-time warning, not an error (a terminal debug mirror is legal,
81
- // but an unread resource is usually a consumer someone forgot to wire -- the silently-dropped-
82
- // bridge class this repo keeps re-finding).
83
- const unreadKeys = []
84
- for (const [key, writer] of writerOf) {
85
- if (!readersOf.has(key)) {
86
- unreadKeys.push(key)
87
- console.warn(`[render-graph] resource '${key}' (written by '${writer}') has no reader -- debug mirror or missing consumer?`)
88
- }
89
- }
90
80
  // Render-target aliasing (the other tractable half of the row): group resource KEYS by their
91
81
  // declared physical target (node.targets[key], defaulting to the key itself -- i.e. "no sharing
92
82
  // assumed" unless a node author opts in). Two distinct keys sharing one target is legitimate
@@ -112,6 +102,25 @@ export function createRenderGraph(nodes, opts = {}) {
112
102
  if (!keysByTarget.has(target)) keysByTarget.set(target, [])
113
103
  keysByTarget.get(target).push({ key, writer: writerOf.get(key) })
114
104
  }
105
+ // written-never-read: a construction-time warning, not an error (a terminal debug mirror is legal,
106
+ // but an unread resource is usually a consumer someone forgot to wire -- the silently-dropped-
107
+ // bridge class this repo keeps re-finding). Exempt: (1) a declared non-self target -- the
108
+ // canvas/physical-target IS the real consumer, same as dead-pass detection below; (2) a writer
109
+ // node marked required/terminal -- the WHOLE node is an intentionally-terminal completion marker;
110
+ // (3) a key listed in the writer's own `debugMirrors` -- a PER-KEY escape for a node that writes
111
+ // some resources real consumers read and others that are deliberately self-contained debug
112
+ // mirrors (e.g. terrain-depth-color's 'camera-context', read inline via ctx.res, not a declared
113
+ // `reads` edge), so marking the whole node terminal would wrongly also silence a genuinely-
114
+ // missing consumer on one of its OTHER writes.
115
+ const unreadKeys = []
116
+ for (const [key, writer] of writerOf) {
117
+ if (readersOf.has(key) || hasDeclaredTarget.has(key)) continue
118
+ const writerNode = byId.get(writer)
119
+ if (writerNode && (writerNode.required || writerNode.terminal)) continue
120
+ if (writerNode && writerNode.debugMirrors && writerNode.debugMirrors.includes(key)) continue
121
+ unreadKeys.push(key)
122
+ console.warn(`[render-graph] resource '${key}' (written by '${writer}') has no reader -- debug mirror or missing consumer?`)
123
+ }
115
124
  // Dead-pass detection (construction-time, static -- the tractable half of "auto-cull dead
116
125
  // passes"): a node whose EVERY declared write is BOTH unread by another node's `reads` AND not
117
126
  // aliased onto a shared physical target (targets[key]) is a real candidate for a forgotten/
@@ -83,6 +83,10 @@ export function buildRenderSectionNodes() {
83
83
  reads: ['hostNearFar'],
84
84
  writes: ['camera-context', 'terrainDepth', 'terrainColor'],
85
85
  targets: { terrainDepth: 'canvas', terrainColor: 'canvas' },
86
+ // 'camera-context' is read inline via ctx.res['camera-context'] below (a same-node self-
87
+ // consume), never through a declared `reads` edge -- a real, intentional debug mirror, not a
88
+ // forgotten consumer.
89
+ debugMirrors: ['camera-context'],
86
90
  shouldRun: ctx => !!ctx.terrainBackdrop,
87
91
  run(ctx) {
88
92
  ctx.terrainBackdrop.renderPlanet(ctx.camera, ctx.now / 1000, ctx.sun, ctx.floatingOrigin ? ctx.floatingOrigin.toAuthoritative : undefined)
@@ -256,6 +260,9 @@ export function buildRenderSectionNodes() {
256
260
  id: 'visibility-commit',
257
261
  reads: ['sceneDepth'],
258
262
  writes: ['occlusionCommitted'],
263
+ // Intentionally-terminal completion marker (see comment below): no downstream node reads
264
+ // occlusionCommitted, it just stamps that this frame's queries were issued.
265
+ terminal: true,
259
266
  // occlusionCommitted is a pure "queries issued this frame" stamp -- no other node reads it;
260
267
  // this node's real effect is entirely external (GPU occlusion-query submission whose results
261
268
  // land next frame via modelPool/terrainBackdrop/sceneOcclusion's own internal state, not a
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spoint",
3
- "version": "0.1.673",
3
+ "version": "0.1.675",
4
4
  "description": "Physics and netcode SDK for multiplayer game servers",
5
5
  "type": "module",
6
6
  "workspaces": [
@@ -250,7 +250,7 @@ export class AppLoader {
250
250
  revokes.push(url)
251
251
  urlMap[spec] = url
252
252
  }
253
- return source.replace(/((?:from|import)\s*)(['"])(\.[^'"]+)\2/g, (m, pre, q, spec) =>
253
+ return source.replace(/((?:from|import)\s*)(['"])(\.[^'"]+|\/[^'"]+)\2/g, (m, pre, q, spec) =>
254
254
  urlMap[spec] ? `${pre}${q}${urlMap[spec]}${q}` : m
255
255
  )
256
256
  }