jankenbots/memory.md
megaproxy e8269c75dd memory: Linux build resolved + published; record restart/false-True gotcha
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 18:20:16 +01:00

63 lines
23 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# memory — JANKENBOTS
Durable memory for this project. Read at session start, update before session end. Date format: `YYYY-MM-DD`. Deep design detail lives in `DESIGN.md` (the full record carried over from the idea's `plan.md`); this file is the distilled decisions + live work.
> **Location:** this project lives at **`/mnt/f/jankenbots/jankenbots`** (Windows `F:\jankenbots\jankenbots`) so the Unity Editor (Windows-only) and the git repo/docs share one folder. `~/claude/projects/jankenbots` is a **symlink** to it. Work from either path. **Name:** the game is **JANKENBOTS** (working title was JANKENSTEIN; the archived idea folder keeps the old name at `~/claude/archive/ideas/jankenstein/`).
## Decisions & rationale
- **Concept (locked).** ~25-player friendslop build-brawl: teams build a janky physics robot from themed scrap; **each teammate pilots one functional part** (pillar #6 — "you build your control scheme, not a robot"); death → free-roaming ragdoll **gremlin**. 8 design pillars in `DESIGN.md`.
- **Engine: Unity** (2026-07-10). Decided after a 3-angle netcode/prior-art study. The physics-party genre is overwhelmingly Unity (Fall Guys, Gang Beasts, Human Fall Flat, PEAK, Lethal Company, Party Animals, Stumble Guys, Trailmakers, Besiege, Robocraft); the two UE physics-heavy titles both swapped UE physics for Bullet. An earlier Unreal-MCP-tooling edge was correctly overridden. **The defining risk is networked physics at ~25-player scale — no shipped game occupies that quadrant.**
- **Control feel v0.1 — driving = symmetric** (2026-07-10). Each locomotion part = one pilot applying *literal force* ("you are the left tread") — the only model that generalises to arbitrary scrap contraptions and the one that keeps pillar #6. Tread pilot verbs: **snap-to-cruise throttle** (both slam forward = easy straight; coordination lives in the in-between), **lock-pivot** (called-play turns), **passive lean** (nobody idle). Asymmetric (throttle/steer split) held as the fallback if the prototype groans.
- **Weapons = three-body call-and-response** (2026-07-10). A weapon pilot perturbs the drivers' world → turn simultaneous chaos into rhythm: wind-up **telegraphs a brace**, a **braced bonus** makes drive+strike a combo, weapon doubles as engine/counterweight. Generalises to **5 archetypes** — Punch / Grinder / Grip (later tier) / Guard / Passive. The **weapon mix is a build-time chaos dial** (over-building self-punishes = pillar #6 made mechanical).
- **Team size & lobby math** (2026-07-10). Team **35** (3 = smallest full driver+driver+weapon triangle; 5 = coordination/budget ceiling); lobby **≥2 teams**; sweet spot 5 teams of 5. Unequal sizes balanced by a **flat build budget** (small team = concentrated & nimble; big team = distributed & tippy → equal power, different character), reinforced by physics (bigness = top-heavy, self-limiting) + coordination tax; matchmaking buckets pre-made friend groups & prefers equal sizes, NPC-fills the rest. **No living "roamer" — everyone is in the bot; off-bot = gremlins (dead) only.** Style vote needs ≥3 teams (2-team lobbies use fight-result + spectator vote).
- **Prototype netcode = Unity NGO + Relay, host-authoritative** (2026-07-10). Chosen for the *throwaway* first prototype (free, first-party, friends join by code, no port-forwarding). **This does NOT commit the final architecture** — server-authoritative state sync vs Photon Quantum deterministic lockstep is still deferred until the game is proven fun.
## Open questions / TODOs
- [ ] **▶ 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.
- **✅ 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 **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).
- **✅✅ 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).
- **✅ UGS GATE CLEARED + HOST PATH VERIFIED (2026-07-10).** Cloud project **jankenbots** (`cloudProjectId a0d222ae-c79b-4120-bc0d-947bfd7600cb`, org `megaproxy`) is linked (has been in ProjectSettings since project creation) AND **Relay is now activated in the dashboard** (the missing step — done via cloud.unity.com ▸ Development ▸ Products ▸ Relay; there is NO enable toggle, opening the product page activates it). Verified by an in-editor Play-mode smoke test: `UnityServices.State=Initialized`, anonymous auth signed in, `HostGame()` created a real Relay allocation + join code, host's Player capsule auto-spawned (`IsSpawned`, owner=0). **The host half of M1-step-2 works end-to-end.** Still unverified: a *remote client joining* + peer-to-peer replication (needs a 2nd instance — standalone build via `manage_build` or a ParrelSync clone).
- **Next build steps:** (5) shared bot = chassis Rigidbody + 2 `TreadPart` treads + `SeatManager` (⚠ `TreadPart` still reads legacy `Input.GetAxis` — swap to `Keyboard.current` like PlayerDriver was, project is Input-System-**New-only**); (6) shove ball + follow cam; (7) 2-friends feel test over Relay (needs a standalone build via `manage_build` or a ParrelSync clone for the 2nd client).
- **Verified Relay API gotcha (baked into NetworkBootstrap):** with the unified SDK, `new RelayServerData(allocation, "dtls")` does NOT exist — use `AllocationUtils.ToRelayServerData(allocation, "dtls")` (`AllocationUtils` is in `Unity.Services.Relay.Models`). Corrected via live editor reflection.
- [x] **Project location decided + unified**`F:\jankenbots\jankenbots` (git + Unity + docs together), symlinked `~/claude/projects/jankenbots`. Unity `.gitignore`/`.gitattributes` in place.
- [x] **Unity project created** — fresh URP + Input System template (`Assets/`, `SampleScene`, `InputSystem_Actions`). Still to add: NGO + Relay + Lobby packages (part of M1 above).
- [x] **Unity MCP working** — CoplayDev server on `127.0.0.1:8067`, portproxy `8068→8067`, `.mcp.json``172.27.208.1:8068/mcp`; handshake verified (v3.4.4). **Tools only load when Claude is started from this project folder** with Unity open + server running.
- [ ] **Netcode architecture (deferred, the real risk)** — dedicated server-authoritative state sync (Robocraft 2 blueprint) vs Photon Quantum lockstep (Stumble Guys precedent). Decide *after* the fun is proven + the 25-player physics spike. Netcode target ≈ 2530 primary rigid bodies + gremlins/debris.
- [ ] **The 25-player physics/netcode spike** — two ~30-joint bots colliding at arena scale, ≥100 ms latency, min-spec. The make-or-break gate before scaling up.
- [ ] Remaining **design** questions (detail in `DESIGN.md`): build-coupling tightness, hidden per-player agenda, at-scale voting, part taxonomy, scavenge-contest rules, art style, progression (cosmetic-only), final name (working title JANKENSTEIN; alts BODGEBOTS / SCRAPYARD SCRUM / CLANKERS / RUSTBUCKETS / JALOPY WARS).
## Session log
### 2026-07-12 (session 4 — M1 build finished: shove ball + follow camera)
- **▶ RESUME HERE next session:** the entire M1 build is done (bot drives, shove ball, follow camera — all in `M1_Arena`, all smoke-verified). **The ONE thing left in M1 is the real 2-human friends playtest over Relay** — flip off any `debugAutoDriveFull`, don't rely on scripted proxies, get two actual people each on one tread pushing the ball around. That playtest is the true fun verdict. After it → **M2 = add a hammer** (first weapon). See the "SHOVE BALL + FOLLOW CAMERA" ✅✅✅ block above for every value/gotcha.
- Fanned out two Sonnet subagents (camera-script author + ball-networking designer) while MCP was down, then wired both into the scene via MCP once the editor came back. Ball needs no script; camera is `FollowCamera.cs`. Confirmed the control scheme is **forward + steer, no reverse**. 2 commits pushed (`87f929b` camera script, `b21f2d9` scene+ball+material). Unity-6 physics API renames noted in the block above.
### 2026-07-11 (session 3 — M1 bot drives, thesis validated)
- **▶ RESUME HERE next session:** M1 core is proven (two-tread bot drives over Relay, feels good). **Remaining M1:** (1) build the **shove ball** (big physics sphere) + a **follow camera** framing the bot — makes coordination purposeful; (2) run the **real 2-human friends playtest** (this session used a host-side `debugAutoDriveFull` auto-drive as a solo proxy — flip it off / don't rely on it with real players); then **M2 = add a hammer**. To bot-test cleanly: despawn the player capsules or clear `NetworkConfig.PlayerPrefab` (they double-drive on WASD), and remember each re-host = a NEW Relay code.
- Built the whole M1 bot this session via MCP + ParrelSync 2-client testing; tuned physics live after it launched off; validated it drives straight with two pilots. All committed + pushed (see the M1-progress TODO block above for the full state, gotchas, and verified API/tuning values). ParrelSync clone editor can be closed between sessions; reopen via ClonesManager when needed.
### 2026-07-10 (session 2 — M1 build start)
- **Started Milestone 1 via a research/draft workflow + serial in-editor build.** Ran a 9-agent workflow (verified NGO/Relay API research → adversarial verify → draft 4 scripts → integration plan); the verify agent installed the packages to reflect against the *live* editor, catching two hallucinated Relay APIs before any code hit the project. Then serially, all main-loop MCP work (packages/scene/prefabs are stateful/single-editor → NOT parallelized): dropped scripts (compile clean, 0 errors), built `M1_Arena` scene + NetworkManager, made the replicated Player capsule prefab, fixed PlayerDriver input for New-only Input System. 4 commits pushed. **Stopped at the UGS-link gate** (see TODO) — nothing is runtime-testable until that's linked.
- **Runtime host smoke-test (solo, no 2nd client):** enter Play via `manage_editor play`, then `execute_code` to call `nb.HostGame()` and poll `read_console` — saw QosJob region pings + `Hosting with join code XXXXXX`. Note: the `editor/state` resource's background tick FREEZES (goes stale) during playmode transitions, but direct `execute_code`/`read_console` commands still work — probe with those, don't trust the stale state snapshot.
- **MCP resource URI correction:** editor state is `mcpforunity://editor/state` (not `/editor_state`); project info `mcpforunity://project/info`. `find_gameobjects` wildcard `*` returns nothing — search by name/component/path. `execute_code` uses CodeDom (C# 6) by default → fully-qualify `UnityEngine.Object`, no C#12 syntax.
### 2026-07-10 (session 1 — promotion + setup)
- **Promoted from `ideas/jankenstein` to a real project.** Scaffolded the WSL-side project home (this repo): filled `CLAUDE.md`, seeded this `memory.md` from the idea's `plan.md`, carried the full design record over as `DESIGN.md`, added `docs/prototype-plan.md`. Idea folder archived to `archive/ideas/jankenstein`.
- Decided to build a **basic networked prototype first** (user wants to test the coordination feel with real friends — local split-screen won't capture the social/voice-chat core). Stack: **Unity NGO + Relay**, host-authoritative, throwaway netcode. Milestone 1 = one shared bot, each player one tread.
- **Consolidated the project + seeded Unity MCP config** (`.mcp.json``unity` HTTP server at `172.27.208.1:8080/mcp`, `.claude/settings.local.json`, `docs/mcp-setup.md` runbook), Unity `.gitignore`/`.gitattributes` (no LFS yet).
- **Renamed JANKENSTEIN → JANKENBOTS and unified folders.** User created the Unity project at `F:\jankenbots\jankenbots` (named it jankenbots). Moved the seeded repo (git history + docs + `.mcp.json`) **into** the Unity project root so it's one folder; renamed the Forgejo repo `jankenstein → jankenbots` + updated the remote; re-pointed the symlink to `~/claude/projects/jankenbots`. Unity project is a fresh URP/Input-System template (has `Assets/`, `SampleScene`, `InputSystem_Actions`). **MCP status: ✅ WORKING (verified 2026-07-10, mcp-for-unity-server v3.4.4).** Server binds `127.0.0.1:8067` (HTTP mode), bridged to WSL via `netsh portproxy 0.0.0.0:8068→127.0.0.1:8067`; Claude reaches `http://172.27.208.1:8068/mcp` (`.mcp.json`). **Key gotcha:** 8080 is unusable — it's in a Hyper-V/WinNAT reserved range (WSL2), bind fails with WSAEACCES/Errno 13; use a non-reserved port + a *distinct* proxy port. Full detail in `docs/mcp-setup.md`. **Session wrapped here** — everything committed & pushed; resume by opening a fresh Claude session from this folder (Unity open, MCP server running) and starting Milestone 1.
## External references
- Full design record: `DESIGN.md` (this repo) — control feel v0.1, weapon archetypes, lobby math, tech direction, netcode prior art.
- Prototype build-plan: `docs/prototype-plan.md`.
- Wiki (orientation): http://192.168.1.249:6876/projects/jankenbots
- Idea-phase history: `~/claude/archive/ideas/jankenstein/` (original brainstorm `plan.md` + session logs).
- Netcode prior art (Rocket League GDC talk, Fall Guys/Robocraft/Stumble Guys case studies, Gaffer On Games) — cited in `DESIGN.md` → References.
- **Unity MCP setup runbook:** `docs/mcp-setup.md`. Server = CoplayDev/unity-mcp in **HTTP mode host-side**; Claude (WSL) connects at `http://172.27.208.1:8080/mcp` (`.mcp.json` committed). Bridge pattern mirrors the UE servers (host-side HTTP + host-IP). Not yet stood up — needs the editor + package installed.