Compare commits
2
Commits
main
..
ab96608cdd
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab96608cdd | ||
|
|
da97ed7780 |
@@ -0,0 +1,182 @@
|
|||||||
|
# TODO — Wings of the 90s
|
||||||
|
|
||||||
|
Working notes from the 6 Oct 2026 audit. Everything below was measured by running the
|
||||||
|
code, not inferred from the README. Upstream: `mac-ai-playground/retro-flight-sim`
|
||||||
|
(read-only for `kevinhermes`); work happens in this fork.
|
||||||
|
|
||||||
|
## Status check (verified)
|
||||||
|
|
||||||
|
- `npm install` + `npm run build` — clean, 13 modules, 512 kB bundle, 581 ms.
|
||||||
|
- `npm test` — passes, but see T4: it only counts braces.
|
||||||
|
- Terrain is deterministic across instances (`getHeightAt(317,-812)` identical in two
|
||||||
|
separate `Terrain` objects) — the "persistent world" claim holds.
|
||||||
|
- Rendering **verified in software** (SwiftShader, see "How to run it headless" below):
|
||||||
|
WebGL 2 context, 60 fps, 143 distinct colours, 63,792/64,000 non-black pixels, HUD
|
||||||
|
canvas drawing 6,905 lit pixels. No GPU is involved — this LXC has no `/dev/dri` and no
|
||||||
|
`/dev/nvidia*`, so all rendering is CPU-rasterised.
|
||||||
|
- **T8 found during that verification**: the CRT post-processing shader does not compile,
|
||||||
|
so the 3D canvas renders pure black on any machine. See T8.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## T1 — Takeoff does not work (highest priority)
|
||||||
|
|
||||||
|
Simulated 40 s at full throttle against the real `Terrain` heightmap:
|
||||||
|
|
||||||
|
| t (s) | knots | alt (ft) | onGround |
|
||||||
|
|------:|------:|---------:|:--------:|
|
||||||
|
| 0 | 0.3 | 19.7 | false |
|
||||||
|
| 1 | 23.2 | 2.8 | false |
|
||||||
|
| 2 | 49.3 | 0 | true |
|
||||||
|
| 6 | 340.2 | 12.2 | false |
|
||||||
|
| 9 | 542.5 | 105.2 | false |
|
||||||
|
| 10 | 564.6 | 0 | true |
|
||||||
|
| 39 | 2,543.3 | 0 | true |
|
||||||
|
|
||||||
|
The aircraft becomes briefly airborne, then never leaves the ground and accelerates to
|
||||||
|
Mach 4.1. Causes:
|
||||||
|
|
||||||
|
- `js/flight-model.js:77` — ground friction is `-hSpeed * 20`, i.e. 20 N per m/s,
|
||||||
|
against `maxThrust = 50000` N on a 1200 kg airframe. Friction loses every argument.
|
||||||
|
- `js/flight-model.js:60-64` — lift is applied while `onGround`, scaled only by
|
||||||
|
`GROUND_LIFT_REDUCTION`, so the plane floats off the runway at arbitrary speed and
|
||||||
|
then the ground-collision clamp at `:95` slams it back down.
|
||||||
|
- No weight-on-wheels state: `onGround` is a position test, not a contact state, so
|
||||||
|
there is no normal force and no way to model a proper rotation/tail-up.
|
||||||
|
- `PITCH_LIMIT = 85°` and the auto-levelling at `:134-135` (`x *= 0.995`, `z *= 0.985`)
|
||||||
|
fight the player continuously.
|
||||||
|
|
||||||
|
**Acceptance criteria:** full throttle from rest → airborne within a plausible
|
||||||
|
takeoff roll (order 200–400 m), climb sustained, stall if nose raised too high, and
|
||||||
|
speed bounded near a sane VMO instead of 2,500 kt.
|
||||||
|
|
||||||
|
## T2 — No WebGL fallback
|
||||||
|
|
||||||
|
`js/main.js:36-43` constructs `THREE.WebGLRenderer` with no try/catch. On a machine
|
||||||
|
without WebGL the page throws an uncaught `Error creating WebGL context` and the loading
|
||||||
|
screen never advances — a blank screen with no message.
|
||||||
|
|
||||||
|
**Fix:** wrap renderer init, show a period-appropriate failure card ("GRAPHICS CARD NOT
|
||||||
|
DETECTED"), and keep the loading/menu screens usable without 3D.
|
||||||
|
|
||||||
|
## T3 — Stall speed is defined but unused
|
||||||
|
|
||||||
|
`STALL_SPEED = 25` (`js/flight-model.js:4`) is used only to scale lift. Nothing in
|
||||||
|
`hud.js` or `sound.js` references stalling. A period-accurate sim of this era should
|
||||||
|
shove a warning in your face.
|
||||||
|
|
||||||
|
**Fix:** stall flag on the flight model → HUD warning + stick shaker / audio cue.
|
||||||
|
|
||||||
|
## T4 — The test suite is theatre
|
||||||
|
|
||||||
|
`scripts/check-syntax.js` checks that each file is >10 chars and that `{` and `}` counts
|
||||||
|
match. It cannot catch T1. CI (`.gitea/workflows/*.yml`) runs it on every push, so the
|
||||||
|
green tick currently means nothing.
|
||||||
|
|
||||||
|
**Fix:** real unit tests on `FlightModel` (takeoff profile, stall, ground contact) and
|
||||||
|
`Terrain` (determinism, airport flattening radius). Node has `node:test` built in — no
|
||||||
|
new dependency needed. Add a headless smoke test that asserts the game reaches the
|
||||||
|
`FLIGHT` state.
|
||||||
|
|
||||||
|
## T5 — No LICENSE
|
||||||
|
|
||||||
|
README and repo carry no licence. Decide one (MIT matches the sibling `wings88`) and add
|
||||||
|
the file.
|
||||||
|
|
||||||
|
## T6 — No save/load
|
||||||
|
|
||||||
|
The sibling project's brief promised persistent worlds. There is no persistence here —
|
||||||
|
no `localStorage`, no world seed stored. Terrain is deterministic per process, which is
|
||||||
|
not the same thing.
|
||||||
|
|
||||||
|
**Fix:** persist the world seed + aircraft state; add a save/load menu entry.
|
||||||
|
|
||||||
|
## T8 — The CRT shader does not compile, so the 3D canvas is black
|
||||||
|
|
||||||
|
Found by running the game under software WebGL, not by reading the code. `js/retro.js:29`:
|
||||||
|
|
||||||
|
```glsl
|
||||||
|
gl_Position = vec4(position, 0.0, 1.0);
|
||||||
|
```
|
||||||
|
|
||||||
|
In three.js `position` is a **vec3**, so this asks for `vec4(vec3, float, float)` — five
|
||||||
|
components into a four-component vector. Invalid GLSL ES on both WebGL 1 and 2:
|
||||||
|
|
||||||
|
```
|
||||||
|
THREE.WebGLProgram: Shader Error 0 - VALIDATE_STATUS false
|
||||||
|
Program Info Log: Vertex shader is not compiled.
|
||||||
|
ERROR: 0:71: 'constructor' : too many arguments
|
||||||
|
```
|
||||||
|
|
||||||
|
The whole post-processing pass therefore fails and the game canvas reads back as **one
|
||||||
|
colour, 0/64,000 non-black pixels**. Verified in isolation: `vec4(vec3, float)` compiles,
|
||||||
|
`vec4(vec3, float, float)` does not, on both WebGL 1 and WebGL 2.
|
||||||
|
|
||||||
|
**Fix (verified):** `gl_Position = vec4(position.xy, 0.0, 1.0);` — compiles clean, and the
|
||||||
|
canvas then reports 143 distinct colours, 63,792/64,000 non-black pixels, scanline banding
|
||||||
|
visible every 25 rows, and the scene hash changing frame to frame once the aircraft moves.
|
||||||
|
Applied locally during the audit to confirm it, then reverted — not committed.
|
||||||
|
|
||||||
|
Note this is the kind of bug the current test suite cannot see (see T4): braces balance
|
||||||
|
perfectly.
|
||||||
|
|
||||||
|
## T7 — Minor / housekeeping
|
||||||
|
|
||||||
|
- `index.html` requests `/favicon.ico` → 404 on every load. Add one, or drop the request.
|
||||||
|
- Bundle is 512 kB (132 kB gzipped); Vite warns about the 500 kB chunk. Fine for now,
|
||||||
|
worth a `manualChunks` split if it grows.
|
||||||
|
- `package-lock.json` is 31 kB for a two-dependency project — expected, just noting it.
|
||||||
|
- No FPS cap. The README brags about a retro feel; `wings88`'s design notes suggest a
|
||||||
|
configurable 20 FPS cap, which would be cheap to add and very on-theme.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## How to run it headless (no GPU, verified working)
|
||||||
|
|
||||||
|
This LXC has no GPU access — `lspci`/`lsmod` inside an LXC report the **Proxmox host's**
|
||||||
|
hardware (an RTX 3090 shows up), but the container has no `/dev/dri` and no `/dev/nvidia*`,
|
||||||
|
so nothing is usable. Software rendering is the only option and it does work.
|
||||||
|
|
||||||
|
The combination that works:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
CHROME=/root/.cache/ms-playwright/chromium-1234/chrome-linux64/chrome
|
||||||
|
$CHROME --headless=new --no-sandbox --disable-gpu-sandbox \
|
||||||
|
--use-angle=swiftshader --enable-unsafe-swiftshader \
|
||||||
|
--remote-debugging-port=9223 --user-data-dir=/tmp/probe --no-first-run
|
||||||
|
# then attach with playwright-core: chromium.connectOverCDP('http://127.0.0.1:9223')
|
||||||
|
```
|
||||||
|
|
||||||
|
Gotchas, all learned the hard way:
|
||||||
|
|
||||||
|
- **Playwright's own launcher defeats this.** `chromium.launch()` injects defaults
|
||||||
|
(`--disable-gpu` among them) that silently kill the GPU process; every attempt via
|
||||||
|
`launch()` returned `webgl: NULL` or crashed the page. Launch Chrome directly and attach
|
||||||
|
over CDP instead.
|
||||||
|
- `--use-gl=swiftshader` (legacy path) crashes the page. Use `--use-angle=swiftshader`.
|
||||||
|
- `--use-angle=vulkan` + Mesa lavapipe (`VK_DRIVER_FILES=/usr/share/vulkan/icd.d/lvp_icd.json`)
|
||||||
|
brings the GPU process up (`GPU.EstablishGpuChannel` records samples) but still yields no
|
||||||
|
WebGL context. SwiftShader is the path that works.
|
||||||
|
- Headful under `xvfb-run` crashes the page. `--headless=new` is required.
|
||||||
|
- `--dump-dom` races the page script — it captures before the probe writes its result.
|
||||||
|
- WebGL clears the drawing buffer after each frame (`preserveDrawingBuffer: false`), so a
|
||||||
|
synchronous `readPixels` returns all-black. Sample inside a `requestAnimationFrame`
|
||||||
|
callback, or the measurement is meaningless.
|
||||||
|
- `readPixels` origin is bottom-left; flip rows before reporting them as screen rows.
|
||||||
|
- Screenshots via CDP fail here (`Unable to capture screenshot`), so verification is by
|
||||||
|
pixel readback and colour histograms rather than images.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Suggested order
|
||||||
|
|
||||||
|
T1 → T4 (tests first would be better, but T1 is the reason the project is worth keeping)
|
||||||
|
→ T2 → T3 → T5 → T6 → T7.
|
||||||
|
|
||||||
|
## Notes on provenance
|
||||||
|
|
||||||
|
Two commits, both authored by `mac-container-dev` on 5 May 2026. Not a fork of `wings88`
|
||||||
|
(`fork: false`, no cross-references in code) — an independent take on the same brief.
|
||||||
|
`wings88` has 214 source lines and no Three.js usage at all; its useful parts are the
|
||||||
|
Jest/Playwright setup and the design guidelines (16–32 colour palette, no anti-aliasing,
|
||||||
|
20 FPS cap), which would slot in here.
|
||||||
Reference in new issue
Block a user