Commit graph

372 commits

Author SHA1 Message Date
Admin
a8427cda75 Arcade M2b: multiplayer — players in the sim, tiered replication, host/join
A room of devices now plays one game: host simulates, clients send input
and render replicated truth.

- libs/game/sim/player.rs: Players roster on GameWorld, slot 0 is always
  this device. Player 0's input stays in the world's original held/pressed/
  pad/cam_yaw fields and is mirrored into the roster, which is what keeps
  single-player numerics bit-identical (tape gate confirms). Ids are never
  reused, so a stale reference resolves to None, never to somebody else
- The camera-movement knot resolved: world.player_move(p) rotates that
  player's axes by THEIR cam_yaw, carried in their input packet. Player 0's
  branch is the original expression character-for-character (the f32 cos
  widened to f64 kept deliberately — tidying it would move the numbers)
- libs/game/session/replication.rs: Shared = pos/vel/size/kind/tag;
  Derived = facing/anim/scale/glow/blob shadows, recomputed client-side and
  costing zero wire bytes; Local = camera/audio/effects. Statics never enter
  the per-tick stream
- Protocol (additive, version unchanged): EntityDesc + Descriptors message
  splits rare reliable construction data from volatile unreliable state —
  without it a joiner sees poses for entities it cannot build. EntityDesc
  carries pos because statics never appear in the state stream (the
  late-joiner test caught ground arriving at the origin)
- Script: game.players/player_name/player_entity/player_input/bot/on_join/
  on_leave; blocks gained owner: PlayerId so a car reads its own driver
- Arcade: ARCADE_HOST=1 / ARCADE_JOIN=<addr>; clients skip world
  construction and don't simulate
- Racing wire volume, 6 players x 60Hz x 200 entities: 2400 pps,
  20.9 Mbit/s up (audit projected 74 Mbit for the XR stack). Asserted in
  racing_scenario_wire_volume_fits_a_living_room

Two more not-a-playable-default bugs, same class as M1b's rng-at-zero:
Entity::default() leaves gravity_scale 0 (weightless wheels) and ground
without friction gives no traction — only the DSL path filled these in.

255 tests green; tape probe BYTE_IDENTICAL; xr/arcade/gamemaker build.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 01:21:44 +02:00
Admin
c13c867547 Arcade M1b: libs/game/blocks — car/character/plane, brains, race kit; racing game in 72 lines
The thesis proof: examples/gamemaker/resources/fixtures/racing.splash is a
complete playable racing game in 72 lines (12-corner oval from a waypoint
loop, 4 cars, gates, standings, restart) with no physics, no AI and no lap
bookkeeping in script. Blocks run engine-side at 60Hz: Blocks::pre_step
(intent -> motion) before step_world, post_step after; Blocks is Clone and
snapshots beside GameWorld so a failed eval rolls both back together.

- game.car (4 suspension raycasts on a box3d rigid chassis), game.character
  (drives the existing mover sweep + owns idle/walk/run blending),
  game.plane; game.drive/autodrive/speed
- Brains: game.wander/chase/patrol/caught — the fixture's hand-rolled AI,
  absorbed engine-side
- Race kit: spawnpoint, checkpoint, place, race, standings, lap/rank/
  finished, score/score_of (Shared-tier data, ready for replication)

raycast_vehicle audit (defects documented in car.rs, still live in xr):
libm sin/cos in steering (unreplicable), a wrong side-impulse denominator
(iaj.dot(iaj) where it should be (I^-1 aJ).aJ), and an unguarded division.
Kept the structure, replaced Bullet's friction solver with an arcade force
model: suspension acts at the contact point, grip and drive through the
centre of mass, steering as yaw torque — no lateral force can generate
roll, so it is stable by construction rather than by roll_influence fudge.

Engine bug fixed at the source: GameWorld::new() left rng at 0 and
xorshift64* is a fixed point at zero, so rand() returned 0 forever for any
world built through the sim API. reset_content seeds on every eval, which
hid it from gamemaker entirely; found by a wander brain that never left home.

Tape probe byte-identical; racing fixture evals clean and drives (AI follow
the line, gates bank in order).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 00:54:22 +02:00
Admin
17263b6666 Arcade M1a: box3d as the dynamics layer (hybrid — movers keep the parity sweep)
- libs/game/sim/dynamics.rs: box3d world inside GameWorld, body mirror
  RECONCILED against the sorted entity list per tick (merge-walk), so
  retain/rollback/reset are correct by construction. Statics + kinematics
  mirrored (kinematics via target transforms so resting rigids inherit
  platform velocity); smooth terrain as a box3d heightfield
- New BodyKind::Rigid + body:"rigid" with density/friction/restitution;
  game.push = mass-scaled impulse; set_pos/set_vel detected via bit-exact
  pose caches (no new dispatch arms). Sphere rigids roll on real spheres
- Entity.orient quat read back per tick; renderer builds quat instance
  transforms for rigids (no shader change). Step order: mover sweep
  verbatim -> reconcile -> world_step(dt, 4) -> readback; rigid-free
  worlds skip the solver
- GameWorld stays Clone via box3d snapshot round-trip (bit-identical
  continuation proven by test)
- Determinism: double-run equality + golden hash 0xa8a2baf71e4a564f
  (aarch64, debug and release). Perf: 0.038 ms/tick for 100 movers +
  50 rigids + 65x65 terrain (budget 2 ms)
- Tape probe BYTE_IDENTICAL; box3d crate untouched; arcade demo gains a
  kicked crate stack (captures verified: settles upright, then topples
  with rotated resting poses)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 00:04:26 +02:00
Admin
ac5ed14285 Arcade M1c: skinned animated characters (glTF skins/clips, KayKit, CPU skinning)
- libs/game/render/skin.rs: GLB container + owned-JSON parser, dense
  accessors, multi-primitive skinned meshes, skin + inverse binds, full
  node-hierarchy palette, T/R/S clips (linear/step), nlerp blending.
  Hermetic tests via an in-code 2-joint GLB; real-asset test skips with a
  hint when the download hasn't run
- DrawGameSkinned shader (PbrVertex + albedo texture, terrain-style
  lighting); skinned batch draws between opaque and alpha passes.
  CPU skinning for now (sub-ms at 3716 verts): the uniform_buffer GPU path
  has zero in-tree runtime consumers — SkinnedModel::palette is the seam
  for the GPU swap
- apps/arcade: download_assets.sh (KayKit pinned commit + sha256,
  idempotent, dir gitignored, CC0 license note in-repo); Knight patrols
  the demo world with idle/walk blending, yaw follows path; captures
  verified (pose advances, depth-occludes correctly)
- renderer binary_search sites use the shared sorted-id helper from M0r

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 23:35:09 +02:00
Admin
7bb0c3be34 Arcade M0 stage B: rendering extracted to libs/game/render + arcade live viewport
- libs/game/render: the 5 game draw shaders (script_mod registration),
  shape geometry + winding test, terrain mesh, static-slab instancing,
  draw_scene pass (sky/terrain/opaque/alpha) as GameRenderer over GameDraws
  (draw structs stay #[live] on the host widget so script theming works),
  CameraRig + scene_state per-view (multi-view ready), HUD + billboard
  label drawing. game_view.rs 4640 -> 3599 lines
- apps/arcade: first engine-only viewport (arcade_view.rs) — GameWorld
  built via the sim API with no script VM, 60Hz tick, orbit camera,
  offscreen pass composite; ARCADE_CAPTURE=<png> GPU-capture test hook
- Verified: sandbox3d fixture evals clean headless; arcade demo frame
  GPU-captured and visually checked; suites green. Sim untouched — stage-A
  tape byte-parity (probe.txt identical vs pre-refactor binary) stands

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 22:56:22 +02:00
Admin
f2228a4654 Arcade M0 stage A: gamemaker sim extracted to libs/game/sim + deterministic math + apps/arcade shell
- libs/game/math: cross-arch bit-deterministic f32 kernel (f64-internal
  fdlibm-style polynomial kernels, exactly-rounded IEEE ops only, zero
  platform-libm transcendentals). Parity golden hashes recorded on aarch64;
  equal hashes on every other target IS the determinism test
- libs/game/sim: the gamemaker world moved verbatim (entity/terrain/world/
  queries/step — float expression order preserved so tapes replay
  bit-identically). No Cx, no draw types, no ScriptObjectRef: script
  callbacks are CallbackSlots resolved through a generation-tagged
  host-side CallbackTable in game_view.rs; sim reset no longer touches audio
- examples/gamemaker: game_view.rs 5545 -> 4640 lines (script boundary,
  rendering, input devices, host I/O remain); per-tick args/input objects
  built via unchecked pushes + release_transient (flat-heap tick path)
- apps/arcade: Makepad Arcade app shell + workspace membership
- Verified: headless sandbox3d fixture (90 entities) boots end-to-end;
  gamemaker/sim/math/script-test suites green

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 22:28:06 +02:00
Admin
f4af4db88d Map: growing-archive watcher — the app reloads as the world spiral weaves cells in
5s mtime watch on the active archive (root.mkidx for mkmap); on change,
Failed placeholders clear and the visible loop re-requests. Workers
already reopen per batch, so an atomic shard-set swap appears live —
apps/route now points at world.mkmap and starts empty until cell-001
(NL) lands.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:44 +02:00
Admin
c073c08596 Revert to known-good: pass batching opt-in (MAKEPAD_BATCH_PASSES=1), AI loading opt-in (MAKEPAD_AI=1)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:43 +02:00
Admin
c59fe0b0eb Metal: frame-batched command buffer for offscreen passes + big benchmark window
Texture-mode passes append to one retained command buffer flushed at the
window pass (safety flush at repaint start for texture-only frames) —
the 12-pass gauss pyramid paid ~1ms commit/schedule latency PER PASS.
Profiling mode (MAKEPAD_GPU_PROFILE) keeps per-pass buffers so spans
stay attributable. Startup window 3400x2050 for pixel-heavy iteration.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:43 +02:00
Admin
768fc340a7 route: NL-first iteration — app reads nl-kf-test 4-keyframe cut (15-18 + shadows)
Europe re-cut with these buckets happens on explicit go; the NL cut
lives in local/maps (untracked) for cross-session stability.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:43 +02:00
Admin
627f0bb04b Map: app reads .mkmap shards — MkmapReader + TileArchiveReader, varint-KV index v2
libs/mbtile_reader gains the .mkmap consumer: root.mkidx parse (Hilbert
tile ids, root ranges -> brotli leaf directories -> shard/offset/len),
positioned shard reads, same get_metadata/get_tile_decoded surface as
MbtilesReader; TileArchiveReader sniffs the path so mbtiles_path can
point at either. Index format v2 drops JSON: the metadata section is now
varint KV like the leaves (writer + reader; no serde in the container).
Loader, zoom-range probe and the headless harness go through the enum;
bridge-dz/overlay sidecars stay mbtiles. Harness parity via shards:
worst AMS rz16 164.7ms, z12 native 51.3ms — identical to mbtiles. App
repointed to local/maps/europe-base-br.mkmap (111 shards + root.mkidx,
the exact bytes a CDN would serve).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:42 +02:00
Admin
9b4d871075 Map: repoint app to europe-base-br-v2 (theme-independent final cut) + pipeline script
Final cut: 9.38M tiles, 133,431 baked (9.6GB streams, zooms 10-14,
threshold 100ms), 107 shards under cap, 253,525 sampled byte-identical.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:42 +02:00
Admin
fce6c68835 Map: repoint app to europe-base-br-faces-mz (mid-zoom baked) + midzoom pipeline script
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:42 +02:00
Admin
80d5967198 Map: Europe single-origin archive shipped — 51.8GB base + faces bake + .mkmap shards
Overnight run 2026-08-01: 9,380,390 tiles z2-14 in 3.2h, faces bake 1h
(+8GB, worker-side brotli q10), transmux to 99 shards all <510MB.
App repointed to europe-base-br-faces.mbtiles. nl-bridge-dz verified
byte-identical against the new base (deterministic feature indices).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:42 +02:00
Admin
9bddc458b9 Map: painter-cascade per-bucket bake (v2-faces-1) + z14 streets merge
- The boolean union cascade (59-77% of heavy tile builds) is now baked
  per (tile, bucket 14/15/16) as additive field 101: cascade output made
  deterministic (1/64 grid snap, canonical ring order, sorted tiers) and
  bit-reproducible; renderer consumes guarded by structure signature +
  coordinate checksum with runtime fallback; MAKEPAD_NO_BAKED_FACES=1.
  Roundtrip bit-identical on all keys x buckets x dz configs. New
  tools/map_bake producer (worker-pool incl. worker-side brotli).
- z14 streets merging never ran (early-out at DETAIL_ZOOM): fixed —
  1957->1285 ways (-34%), payload -20MB, cascade input shrunk at source.
- Slow-tile log now prints per-stage ms + a ready-to-paste headless repro
  command; harness honors archive/detail/dz/overlay/rz/3D envs.
- Worst AMS tile: 2-3s cold -> 175ms rz16 (61ms native); NL bake data
  882MB at 60ms threshold; Europe needs threshold 150ms + 2 buckets to
  fit shard budget (+2-4GB est).
- App -> nl-base-br3-faces archive; bridge-dz rebake required (joins
  fail closed against br3 feature indices).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:42 +02:00
Admin
7ad23460ea Route: user override interrupts the dispatcher; break tool loops
- Voice override ("ok nevermind / stop / wait, do X") and ANY typed
  prompt now cancel the in-flight local turn (cancel_prompt -> per-token
  stop; worker closes the assistant turn and drops its pending tool
  calls). Bare "stop"/"nevermind" just halts + silences TTS; otherwise
  the new command is sent immediately. Queueing remains for non-override
  follow-ups.
- Greedy-decoding tool loops (identical geo_search spam): an identical
  repeated call or >10 tool rounds per prompt gets a corrective error
  result instead of execution ("answer with what you have").
- geo_search rejects degenerate queries (>64 chars / >8 words) that
  crawl the fuzzy index.
- Append-only context has no recovery once full: at >90% usage after a
  turn, the local session restarts fresh (cheap with mmap weights);
  transcript stays, conversational memory resets.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:41 +02:00
Admin
053b1a9d15 Audio: constant gentle ducking beats half-duplex unit swapping
The mid-stream VPIO<->plain unit swap glitched the start of the
assistant's own speech; keep the voice-processing unit armed for the
whole capture (standard+Min ducking) instead. set_echo_cancellation and
the options-rebuild plumbing stay for apps that want the trade.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:41 +02:00
Admin
419d77c652 Audio: half-duplex echo control — duck only while the assistant speaks
Idle listening now runs PLAIN capture (no VPIO, zero ducking — music
untouched); the app arms the voice-processing unit only while its own
TTS is audibly playing (+0.8s tail), which is the only window where echo
cancellation matters. Options changes rebuild running input units
(AudioUnitAccess.last_input_options); WindowVoiceInput/VoiceWave gain
set_echo_cancellation; route app polls speech playback at 4Hz.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:41 +02:00
Admin
1afcedd8f6 NL-wide bridge-dz overlay + mbtiles-merge; fix VoiceInput unit handling
- bridge-dz baked for the entire Netherlands (17026 tiles, 29MB):
  AHN-refined over the 8 Amsterdam sheet pairs, solver-only elsewhere.
  Baked as 4 north-south strips with 2-tile overlap — the full-bbox
  global solve peaked past 60GB RSS — merged with the new mbtiles-merge
  subcommand (later input wins on overlaps, block-major write order).
  Route app bridge_dz path ams -> nl.
- audio_unit: the new VoiceInput (VoiceProcessingIO) kind takes the input
  setup/handler paths (set_input_handler panicked and aborted the app);
  VPIO capture forced mono on every platform.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 12:38:48 +02:00
Admin
723b7928bb Route assistant: mmap LLM weights, fix batched-attention corruption, kokoro voice out
- ggml: read-only mmap module (unix-gated) + two-region Context (mapped
  weights / dirty caches) + segmented Metal buffer binding; llama loads
  GGUF weights as file-backed clean pages (jetsam-exempt) with owned-arena
  fallback (MAKEPAD_LLAMA_NO_MMAP=1). Route app dirty footprint 12GB -> 4-7GB;
  model load becomes lazy page-in; A/B byte-identical on 4B + 9B.
- llama: fix graph-cache keying corruption — a graph keyed wider than the
  KV cache corrupted attention for any prefill batch >= 2 (flash op reads
  permute-node dims baked at build; view reconfigure never reached the
  kernel; masks were written cache-narrow). Masks now always fill the full
  graph key width and graphs key by 1024-buckets; reconfigure path removed.
  Verified byte-exact vs per-length reference across batch 1/2/8/64/512,
  short+long prompts, mmap on/off, plus a two-session concurrency probe.
- voice: passive VoiceWaves no longer register the global audio-input
  callback (the invisible caption-bar wave stole mic audio — last
  registrant wins — and spawned duplicate whisper workers); whisper back
  to F16 default (voice Metal library has no quantized kernels; q5_0
  failed every GPU matmul); raw transcripts render immediately.
- route: kokoro TTS voice output (speaker toggle; streams reply sentences,
  announces nav maneuvers + arrival) with barge-in — voice activity on the
  mic stops playback instantly; dispatcher context 8k -> 32k (hybrid KV is
  12/48 layers, ~48KB/token); window caption bar suppressed under studio.
- llama-generate: --max-context/--prefill-batch-size + state fingerprints;
  new llama_concurrent_probe bin.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 12:13:31 +02:00
Admin
676255dfc2 Land route app + platform geo/permission APIs, map nav layer, geodata radar
apps/route: AI trip planner on MapView — tool broker + local/cloud agent
dispatch, nav session + simulated drive, layers/theme state, DDG image
search, trip history, tilt-shift DOF layer (linear circle-of-confusion:
level = log2 of radius, constant growth rate, tilt raises only the
ceiling; tilt-shift on by default).

Platform: Cx geo location API (macOS/iOS/Android/web) + permission
plumbing, memory watchdog, headless build cfg. Map: nav layer M0 API,
landcover drape, bridge dz. Geodata: 250m dual-radar compositor
(radar_volume) + KNMI sync. Docs for the route/glass/blur/shiny work.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 00:10:52 +02:00
Admin
ff4e3183c1 Add apps/route AI trip planner M0 skeleton to workspace
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 00:10:52 +02:00