memory: Linux build resolved + published; record restart/false-True gotcha

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
megaproxy 2026-07-12 18:20:16 +01:00
parent 9ede7e3924
commit e8269c75dd

View file

@ -18,7 +18,7 @@ Durable memory for this project. Read at session start, update before session en
- [ ] **▶ M1 BUILD COMPLETE — only the 2-human playtest remains** (see `docs/prototype-plan.md`). Done so far (2026-07-10, all compiling clean + pushed): **packages** (NGO 2.13.0 + `com.unity.services.multiplayer` 2.2.4 — the *unified* SDK that supersedes the deprecated standalone relay/lobby packages; pulls transport 6.5.0 + core/auth), **4 scripts** in `Assets/Scripts/{Net,Bot}/`, **scene** `Assets/Scenes/M1_Arena.unity` (NetworkManager+UnityTransport+NetworkBootstrap, ground/cam/light, set as build scene), **replicated Player capsule prefab** (`Assets/Prefabs/Player.prefab`, registered as `NetworkConfig.PlayerPrefab`). - [ ] **▶ M1 BUILD COMPLETE — only the 2-human playtest remains** (see `docs/prototype-plan.md`). Done so far (2026-07-10, all compiling clean + pushed): **packages** (NGO 2.13.0 + `com.unity.services.multiplayer` 2.2.4 — the *unified* SDK that supersedes the deprecated standalone relay/lobby packages; pulls transport 6.5.0 + core/auth), **4 scripts** in `Assets/Scripts/{Net,Bot}/`, **scene** `Assets/Scenes/M1_Arena.unity` (NetworkManager+UnityTransport+NetworkBootstrap, ground/cam/light, set as build scene), **replicated Player capsule prefab** (`Assets/Prefabs/Player.prefab`, registered as `NetworkConfig.PlayerPrefab`).
- **✅✅✅ SHOVE BALL + FOLLOW CAMERA BUILT & SMOKE-VERIFIED (2026-07-12) — M1 build is DONE.** Wired both into `M1_Arena` via MCP, solo-host smoke test passed (ball spawns + rests on ground + shoves under force; camera moves to bot+offset and frames it). Committed `b21f2d9`. **Shove ball** = in-scene `NetworkObject` sphere `ShoveBall` (1.5m, Rigidbody 10kg, linearDamping/angularDamping 0.5, Interpolate, Continuous) at world `(0,0.75,6)` (6m in front of the bot's +Z spawn facing, resting on the Plane at y=0); `SphereCollider` + `Assets/Physics/ShoveBall.physicsMaterial` (dyn/stat friction 0.4/0.5, bounce 0.3, Average combines); `NetworkRigidbody`+`NetworkTransform` → host-authoritative, exactly mirroring the Bot. **NO custom script** (passive prop; host PhysX + NetworkRigidbody replicate it for free — correct M1-minimal call). **Follow camera** = `Assets/Scripts/Camera/FollowCamera.cs` (plain client-local MonoBehaviour, NOT networked) on `Main Camera`, `target`=Bot chassis, `worldOffset (0,8,-12)`. Deliberately **position-follow-only** (fixed world offset + separately-slerped look-at, ignores the bot's roll/yaw) so the janky bot's spin/tip doesn't nauseate; escalation lever if it feels detached = follow bot YAW only. Auto-finds "Bot" by name if `target` empty. **Control-scheme note confirmed this session:** TreadPart v0.1 is **forward + steer only, NO reverse** (throttle W/↑ is forward-only per its own comment; turning = asymmetric throttle or lock-pivot; lean=anti-tip). **Unity 6 (6000.5.3f1) API gotchas hit:** `PhysicMaterial``PhysicsMaterial`, `PhysicMaterialCombine``PhysicsMaterialCombine`, Rigidbody `.drag`/`.angularDrag``.linearDamping`/`.angularDamping` (old names still readable but set the new ones). `NetworkBootstrap` is in the **global namespace** (no `Jankenbots.Prototype`), `HostGame()` is async `Task<string>`. **▶ ONLY M1 STEP LEFT = the real 2-human friends playtest over Relay** (this session's test was solo host + a scripted impulse as a ball-shove proxy). Then M2 = hammer. - **✅✅✅ SHOVE BALL + FOLLOW CAMERA BUILT & SMOKE-VERIFIED (2026-07-12) — M1 build is DONE.** Wired both into `M1_Arena` via MCP, solo-host smoke test passed (ball spawns + rests on ground + shoves under force; camera moves to bot+offset and frames it). Committed `b21f2d9`. **Shove ball** = in-scene `NetworkObject` sphere `ShoveBall` (1.5m, Rigidbody 10kg, linearDamping/angularDamping 0.5, Interpolate, Continuous) at world `(0,0.75,6)` (6m in front of the bot's +Z spawn facing, resting on the Plane at y=0); `SphereCollider` + `Assets/Physics/ShoveBall.physicsMaterial` (dyn/stat friction 0.4/0.5, bounce 0.3, Average combines); `NetworkRigidbody`+`NetworkTransform` → host-authoritative, exactly mirroring the Bot. **NO custom script** (passive prop; host PhysX + NetworkRigidbody replicate it for free — correct M1-minimal call). **Follow camera** = `Assets/Scripts/Camera/FollowCamera.cs` (plain client-local MonoBehaviour, NOT networked) on `Main Camera`, `target`=Bot chassis, `worldOffset (0,8,-12)`. Deliberately **position-follow-only** (fixed world offset + separately-slerped look-at, ignores the bot's roll/yaw) so the janky bot's spin/tip doesn't nauseate; escalation lever if it feels detached = follow bot YAW only. Auto-finds "Bot" by name if `target` empty. **Control-scheme note confirmed this session:** TreadPart v0.1 is **forward + steer only, NO reverse** (throttle W/↑ is forward-only per its own comment; turning = asymmetric throttle or lock-pivot; lean=anti-tip). **Unity 6 (6000.5.3f1) API gotchas hit:** `PhysicMaterial``PhysicsMaterial`, `PhysicMaterialCombine``PhysicsMaterialCombine`, Rigidbody `.drag`/`.angularDrag``.linearDamping`/`.angularDamping` (old names still readable but set the new ones). `NetworkBootstrap` is in the **global namespace** (no `Jankenbots.Prototype`), `HostGame()` is async `Task<string>`. **▶ ONLY M1 STEP LEFT = the real 2-human friends playtest over Relay** (this session's test was solo host + a scripted impulse as a ball-shove proxy). Then M2 = hammer.
- **✅ GIT LFS ACTIVE FOR BINARY ASSETS (2026-07-12).** `git lfs install` done repo-wide; `.gitattributes` routes images/audio/video/models/fonts/native-libs (+ binary `.asset` exceptions: `LightingData.asset`, `*.cubemap`) through LFS, while Unity YAML **text** assets (`.unity/.prefab/.mat/.asset/.anim/.controller`) stay normal mergeable git text (do NOT LFS those). **Forgejo LFS works** — server has LFS enabled + locking support; `git lfs` auths via the **Basic** credential from `~/.git-credentials` (the REST `Authorization: token` scheme does NOT work for the LFS batch endpoint, but plain `git push`/lfs picks up Basic auth from the stored cred automatically — no extra setup). First real art landed: 4 Asset Store packs (**Floreswa** characters, **Junk Car Parts**, **Junkyard models**, **LB3D** lowpoly) = 159 LFS binaries (png/obj/fbx/jpg), pushed clean (commit `7ebc175`). **Workflow when importing more assets:** Package Manager ▸ My Assets ▸ **Download THEN Import** (Download alone doesn't put files in `Assets/`); then `git add Assets/` (add the whole dir so folder-sibling `.meta`s come along — `git add Assets/Pack` MISSES `Assets/Pack.meta`), verify with `git lfs ls-files`, push. **Perftest junk** (`Assets/Resources/PerformanceTestRun*.json` from the Performance Testing package) is gitignored — not source. - **✅ GIT LFS ACTIVE FOR BINARY ASSETS (2026-07-12).** `git lfs install` done repo-wide; `.gitattributes` routes images/audio/video/models/fonts/native-libs (+ binary `.asset` exceptions: `LightingData.asset`, `*.cubemap`) through LFS, while Unity YAML **text** assets (`.unity/.prefab/.mat/.asset/.anim/.controller`) stay normal mergeable git text (do NOT LFS those). **Forgejo LFS works** — server has LFS enabled + locking support; `git lfs` auths via the **Basic** credential from `~/.git-credentials` (the REST `Authorization: token` scheme does NOT work for the LFS batch endpoint, but plain `git push`/lfs picks up Basic auth from the stored cred automatically — no extra setup). First real art landed: 4 Asset Store packs (**Floreswa** characters, **Junk Car Parts**, **Junkyard models**, **LB3D** lowpoly) = 159 LFS binaries (png/obj/fbx/jpg), pushed clean (commit `7ebc175`). **Workflow when importing more assets:** Package Manager ▸ My Assets ▸ **Download THEN Import** (Download alone doesn't put files in `Assets/`); then `git add Assets/` (add the whole dir so folder-sibling `.meta`s come along — `git add Assets/Pack` MISSES `Assets/Pack.meta`), verify with `git lfs ls-files`, push. **Perftest junk** (`Assets/Resources/PerformanceTestRun*.json` from the Performance Testing package) is gitignored — not source.
- **✅ BUILD + FORGEJO-RELEASE PROCESS ESTABLISHED (2026-07-12).** Distribute playtest builds to friends as **Forgejo release assets** (Forgejo 11.0.15 at `git.rdx4.com`, repo has `has_actions:true` + runners, but we do NOT use CI — the local-MCP-build path is far faster, no Unity-license-in-CI hassle). **Process:** (1) build via MCP `manage_build action=build target=windows64|linux64 output_path=F:/jankenbots/builds/<plat>/JANKENBOTS.exe|.x86_64` → returns a `job_id`; poll `manage_build action=status job_id=…` until `result:succeeded` (a ~empty M1 scene builds in ~65s, ~105MB uncompressed; the exe appears on disk BEFORE status flips to succeeded — wait for the status, don't trust file presence). Build scene = `M1_Arena` (the only enabled build scene); default platform is StandaloneWindows64. (2) `zip` is NOT installed in WSL → archive with `python3 -c "import shutil; shutil.make_archive('NAME','zip',root_dir='.',base_dir='FOLDER')"` (win zip ≈40MB). Builds live in `F:/jankenbots/builds` (= `/mnt/f/jankenbots/builds`, OUTSIDE the repo — don't commit them). (3) **Forgejo release via API** with the token from `~/.git-credentials` (`sed -E 's#https?://[^:]+:([^@]+)@.*#\1#'`, 40-char token, user megaproxy): `POST /api/v1/repos/megaproxy/jankenbots/releases` (JSON `{tag_name,target_commitish=<sha>,name,body,prerelease:true}`) → capture `id`; then upload each asset `POST /releases/{id}/assets?name=FILE.zip -F attachment=@path`. **Release `m1-playtest-2026-07-12` (id 19)** now carries **Windows + macOS** zips — https://git.rdx4.com/megaproxy/jankenbots/releases/tag/m1-playtest-2026-07-12 . Add more assets (e.g. Linux) to the SAME release id rather than making a new one. **Installed build targets (checked 2026-07-12 via `BuildPipeline.IsBuildTargetSupported`):** Win64 ✅, OSX ✅, **Linux64 ❌**. **Linux gotcha:** needs **Linux Build Support (Mono)** installed in Unity Hub (Installs ▸ 6000.5.3f1 ▸ ⚙ Add modules) THEN an Editor restart — MCP can't install engine modules or restart the Editor; if absent, `manage_build target=linux64` errors "Platform 'StandaloneLinux64' is not installed." (User attempted the install 2026-07-12 but it hadn't registered even after a restart → build still ❌; Linux zip still owed to the release.) **macOS gotcha:** unsigned `.app` → recipient must bypass Gatekeeper (right-click ▸ Open, or `xattr -dr com.apple.quarantine JANKENBOTS.app`); zipping from WSL over `/mnt/f` (mounts 777) preserves the Mach-O exec bit. Restarting the Editor DROPS MCP — reconnect via `/mcp` and re-`set_active_instance` (the hash is stable across restarts: `jankenbots@47f7b88ac88d97d3`). - **✅ BUILD + FORGEJO-RELEASE PROCESS ESTABLISHED (2026-07-12).** Distribute playtest builds to friends as **Forgejo release assets** (Forgejo 11.0.15 at `git.rdx4.com`, repo has `has_actions:true` + runners, but we do NOT use CI — the local-MCP-build path is far faster, no Unity-license-in-CI hassle). **Process:** (1) build via MCP `manage_build action=build target=windows64|linux64 output_path=F:/jankenbots/builds/<plat>/JANKENBOTS.exe|.x86_64` → returns a `job_id`; poll `manage_build action=status job_id=…` until `result:succeeded` (a ~empty M1 scene builds in ~65s, ~105MB uncompressed; the exe appears on disk BEFORE status flips to succeeded — wait for the status, don't trust file presence). Build scene = `M1_Arena` (the only enabled build scene); default platform is StandaloneWindows64. (2) `zip` is NOT installed in WSL → archive with `python3 -c "import shutil; shutil.make_archive('NAME','zip',root_dir='.',base_dir='FOLDER')"` (win zip ≈40MB). Builds live in `F:/jankenbots/builds` (= `/mnt/f/jankenbots/builds`, OUTSIDE the repo — don't commit them). (3) **Forgejo release via API** with the token from `~/.git-credentials` (`sed -E 's#https?://[^:]+:([^@]+)@.*#\1#'`, 40-char token, user megaproxy): `POST /api/v1/repos/megaproxy/jankenbots/releases` (JSON `{tag_name,target_commitish=<sha>,name,body,prerelease:true}`) → capture `id`; then upload each asset `POST /releases/{id}/assets?name=FILE.zip -F attachment=@path`. **Release `m1-playtest-2026-07-12` (id 19)** now carries **ALL THREE** zips — Windows + macOS + Linux — https://git.rdx4.com/megaproxy/jankenbots/releases/tag/m1-playtest-2026-07-12 . Add more assets to the SAME release id rather than making a new one. **All standalone targets now installed & build clean (Win64/OSX/Linux64).** **Linux gotcha (RESOLVED):** needed **Linux Build Support (Mono)** installed in Unity Hub (Installs ▸ 6000.5.3f1 ▸ ⚙ Add modules → check the box AND click Install/Continue — checking alone doesn't download it) THEN a **full Editor restart**. Nasty trap: after a mid-session module drop, `BuildPipeline.IsBuildTargetSupported` falsely returns **True** (it re-scanned the modules folder) while the build pipeline still throws `UnityException: Build target 'StandaloneLinux64' not supported` and writes NO output — only a genuine restart loads the module into the running process. Verify a fresh Editor via a NEW `session_id` in `mcpforunity://instances`. **macOS gotcha:** unsigned `.app` → recipient bypasses Gatekeeper (right-click ▸ Open, or `xattr -dr com.apple.quarantine JANKENBOTS.app`); zipping from WSL over `/mnt/f` (mounts 777) preserves the Mach-O/x86_64 exec bit (linux binary also needs `chmod +x` on the recipient side, noted in release body). Restarting the Editor DROPS MCP — reconnect via `/mcp` and re-`set_active_instance` (hash stable across restarts: `jankenbots@47f7b88ac88d97d3`). **Background publish pattern that worked (2026-07-12):** fire `manage_build`, then a detached Bash watcher script that polls the output dir for a stable size, zips via `shutil.make_archive`, and uploads to the release via the Forgejo API — lets the build run without blocking (Unity build blocks the Editor main thread, so MCP scene edits must wait until it finishes anyway; use that window for filesystem-only agent work).
- **✅ CAPSULE REPLICATION VERIFIED (2026-07-11) — M1 netcode thesis proven.** Two clients over Relay (host = original editor via MCP, client = ParrelSync clone), both capsules spawned + drove independently + replicated **bidirectionally, smooth**. The `input→ServerRpc→host-physics→NetworkTransform` loop works over real Relay. **ParrelSync 1.5.2** installed for local 2-client testing (clone at `../jankenbots_clone_0`, symlinks Assets+ProjectSettings; open it via `ClonesManager.OpenProject` or the ParrelSync menu; its MCP loses the port fight so only the original is MCP-controllable). - **✅ CAPSULE REPLICATION VERIFIED (2026-07-11) — M1 netcode thesis proven.** Two clients over Relay (host = original editor via MCP, client = ParrelSync clone), both capsules spawned + drove independently + replicated **bidirectionally, smooth**. The `input→ServerRpc→host-physics→NetworkTransform` loop works over real Relay. **ParrelSync 1.5.2** installed for local 2-client testing (clone at `../jankenbots_clone_0`, symlinks Assets+ProjectSettings; open it via `ClonesManager.OpenProject` or the ParrelSync menu; its MCP loses the port fight so only the original is MCP-controllable).
- **✅✅ TWO-TREAD BOT DRIVES — M1 CORE THESIS VALIDATED (2026-07-11).** Two clients over Relay, each piloting one tread of the shared bot; coordinating both to drive straight **"feels good"** (user's words). First attempt the bot was too hot + top-heavy and launched off on a nudge → tuned to stable: cruiseForce 450, linearDamping 1.5, angularDamping 6, maxAngularVelocity 3, **centerOfMass (0,-0.6,0)** (serialized override, implicitCom=false → persists). These are baked into the scene Bot. **Gotchas found:** (1) the step-2 `PlayerPrefab` capsule still auto-spawns per client and PlayerDriver ALSO reads WASD → despawn capsules (or clear PlayerPrefab) for a clean bot test — in the real game players are seats, not free capsules; (2) each re-host mints a NEW Relay join code (old ones die) — stale codes were the cause of a failed join; (3) recompiling scripts mid-session forces the ParrelSync clone to recompile → don't Join during compile. Remaining M1: shove ball + follow camera (step 5-6), then the REAL verdict = a live 2-human friends playtest (the auto-drive was a solo proxy). - **✅✅ TWO-TREAD BOT DRIVES — M1 CORE THESIS VALIDATED (2026-07-11).** Two clients over Relay, each piloting one tread of the shared bot; coordinating both to drive straight **"feels good"** (user's words). First attempt the bot was too hot + top-heavy and launched off on a nudge → tuned to stable: cruiseForce 450, linearDamping 1.5, angularDamping 6, maxAngularVelocity 3, **centerOfMass (0,-0.6,0)** (serialized override, implicitCom=false → persists). These are baked into the scene Bot. **Gotchas found:** (1) the step-2 `PlayerPrefab` capsule still auto-spawns per client and PlayerDriver ALSO reads WASD → despawn capsules (or clear PlayerPrefab) for a clean bot test — in the real game players are seats, not free capsules; (2) each re-host mints a NEW Relay join code (old ones die) — stale codes were the cause of a failed join; (3) recompiling scripts mid-session forces the ParrelSync clone to recompile → don't Join during compile. Remaining M1: shove ball + follow camera (step 5-6), then the REAL verdict = a live 2-human friends playtest (the auto-drive was a solo proxy).
- **✅ SHARED BOT BUILT (2026-07-11), solo-smoke-verified; awaiting 2-pilot drive test.** In-scene `Bot` NetworkObject in M1_Arena: chassis Cube (Rigidbody 60kg + NetworkObject + NetworkTransform + NetworkRigidbody + SeatManager) with two child treads `Tread_L`/`Tread_R` (TreadPart, side Left/Right) at world ±1.2 X. In-scene bot auto-spawns on host; seat claim + gating verified solo. **DESIGN FIX (important):** the two drafts were mis-wired — TreadPart gated on NetworkObject `IsOwner` (needs per-tread ownership → impossible, NGO forbids nested NetworkObjects in a spawned prefab) while SeatManager's `OwnsSeat/PilotOf` API sat unused. Rewrote TreadPart to **seat-based gating**: both treads share the chassis NetworkObject; a client drives the tread whose seat it holds (`PilotOf(side)==LocalClientId`), host re-validates `OwnsSeat` on the RequireOwnership=false RPC. Also swapped TreadPart to `Keyboard.current` (New-only input). Controls: **W/↑ throttle · Shift pivot · A/D lean**. Moved SeatManager OnGUI below the bootstrap panel (was overlapping). - **✅ SHARED BOT BUILT (2026-07-11), solo-smoke-verified; awaiting 2-pilot drive test.** In-scene `Bot` NetworkObject in M1_Arena: chassis Cube (Rigidbody 60kg + NetworkObject + NetworkTransform + NetworkRigidbody + SeatManager) with two child treads `Tread_L`/`Tread_R` (TreadPart, side Left/Right) at world ±1.2 X. In-scene bot auto-spawns on host; seat claim + gating verified solo. **DESIGN FIX (important):** the two drafts were mis-wired — TreadPart gated on NetworkObject `IsOwner` (needs per-tread ownership → impossible, NGO forbids nested NetworkObjects in a spawned prefab) while SeatManager's `OwnsSeat/PilotOf` API sat unused. Rewrote TreadPart to **seat-based gating**: both treads share the chassis NetworkObject; a client drives the tread whose seat it holds (`PilotOf(side)==LocalClientId`), host re-validates `OwnsSeat` on the RequireOwnership=false RPC. Also swapped TreadPart to `Keyboard.current` (New-only input). Controls: **W/↑ throttle · Shift pivot · A/D lean**. Moved SeatManager OnGUI below the bootstrap panel (was overlapping).