You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: CLAUDE.md
+1-4Lines changed: 1 addition & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -27,7 +27,6 @@ SKILL.md and CLAUDE.md MUST be updated whenever code changes. SKILL.md is the ag
27
27
-**gh-pages deployment**: the Worker needs the full server-side `src/` tree at runtime. The gh-pages workflow must copy ALL `src/` subdirs (sdk, connection, debug, netcode, physics, apps, stage, storage, transport, spatial) plus `src/math.js` and `jolt-physics` npm package — not just `src/client`, `src/protocol`, `src/shared`. The importmap sed uses a generic `s|"/node_modules/|"/spoint/node_modules/|` to patch all packages at once.
28
28
-**App sources**: `WorkerEntry` receives `{ name, source }` pairs and calls `appLoader.loadFromString()` — no filesystem access needed
29
29
- **`AppLoader.loadFromString` (browser Worker path)**: uses `URL.createObjectURL + import()` instead of `new Function` — handles full ES module syntax (`export default`, top-level `await`). Apps loaded via this path **cannot use relative `import` statements** (blob URL has no base path for resolution); all inter-app dependencies must be inlined or passed via `engineCtx`. **`AppLoader.BLOCKED_PATTERNS` applies to ALL app sources** — both `loadApp()` (Node.js file path) and `loadFromString()` (browser Worker): any app containing `import(` (dynamic import), `eval(`, `require(`, `process.exit`, `child_process`, `__proto__`, `Object.prototype`, or `globalThis` is rejected silently. The environment app must NOT use dynamic `import()` inside its exported `setup()` — use top-level module initialization (guarded by `process.versions?.node`) for any Node.js-only features. The `"app"` field on entities in `singleplayer-world.json` must be set for `BrowserServer.connect()` to fetch and send the app source to the Worker — missing `"app"` means `setup()` never runs and no collider is created.
30
-
-**`singleplayer-world.json` entities**: each entity must have `"app"` set — without it, the app source is not fetched, `setup()` never runs, and no collider is created. The map entity (`env-sillos`) must have `"app": "environment"`.
31
30
-**Editor in singleplayer**: all editor handlers work via `AppRuntime` in-memory state; `LIST_APPS` reads `appRuntime._appDefs`, `SAVE_SOURCE` calls `loadFromString()` and broadcasts `APP_MODULE`
32
31
-**Inspector message intercept range**: `Inspector.handleMessage` in `src/debug/Inspector.js` intercepts messages by type range as debug/inspector messages, silently returning before `ServerHandlers` can process them. `TRIMESH_DATA (0x92)` must NOT fall in the intercepted range — if it does, trimesh bodies are never created in singleplayer and players fall through the floor with no error logged. Check the exclusion range covers all non-inspector message types when adding new message type constants.
33
32
@@ -79,8 +78,6 @@ All three client modes extend `BaseClient` (`src/client/BaseClient.js`) and expo
79
78
80
79
`TransformLerp.js` — SceneGraph imports only `lerpEntityTransform` and `applyPlayerTransform` (the lerp/apply half). `setEntityTarget` and `setPlayerTarget` exist in TransformLerp but expect a **Map** as first arg — SceneGraph does NOT use them (uses inline writes instead). Passing `node.target` plain object to these Map-based setters causes `targets.get is not a function`.
81
80
82
-
**Why**: previously `EntityLoader` owned `entityTargets`, `PlayerManager` owned `playerTargets`, and `app.js` had `_dirty` Set plus two separate update functions (`updatePlayerPositions`, `updateEntityPositions`). SceneGraph centralizes all of this — one `tick()` call handles both, nodes own their own target state.
83
-
84
81
## Renderer
85
82
86
83
`createRenderer(isMobile)` in `client/SceneSetup.js` returns a `THREE.WebGLRenderer` (synchronous). No WebGPU — it was removed due to persistent GPU OOM crashes across browsers.
@@ -233,8 +230,8 @@ Set `entity.model = null` and populate `entity.custom`:
233
230
234
231
**LOD index cache** (`getLodIndex` / `storeLodIndex`): After MeshoptSimplifier produces a simplified index buffer in `_simplifyObject`, it is stored keyed by `url:lod0` or `url:lod1`. On next load, `_scheduleLodUpgrades` checks for cached indices before calling `MeshoptSimplifier.simplify`. `url` is carried via `model.userData.url` set at parse time and passed through `_generateLODEager` → `_lodUpgradeQueue` entries.
235
232
236
-
237
233
**`_generateLODEager` scale inheritance**: `_generateLODEager` copies `model.position/quaternion/scale` to the LOD wrapper, then resets the model to identity before `lod.addLevel(model, 0)`. This is required — if model retains its scale when added as a child of the LOD, THREE.js compounds parent and child scales (e.g. [3,3,3] × [3,3,3] = 9× visual size). The LOD wrapper owns the transform; the model child must be at identity.
234
+
238
235
### Shader Warmup Persistence
239
236
240
237
`warmupShaders` in `client/SceneSetup.js` stores `lastShaderWarmupKey` in `localStorage`. The scene key includes entity count and ID hash — same world across browser sessions skips re-compilation.
0 commit comments