id: dc49fbd31ae94590870462f7e1819d0a
parent_id: 5026d9d7f8c648f697a159efdd96d3e1
item_type: 1
item_id: 6c0dcb2a567348fd9796f50c790082e4
item_updated_time: 1780514103798
title_diff: "[]"
body_diff: "[{\"diffs\":[[0,\"on**: **\"],[-1,\"Lua\"],[1,\"KSUDP (built-in UDP)** — 3-phase handshake protocol on port 9996, works under Proton/Linux\\\n- **Secondary option**: **Python\"],[0,\" plugin \"]],\"start1\":191,\"start2\":191,\"length1\":19,\"length2\":139},{\"diffs\":[[0,\"Linux\\\n- The \"],[-1,\"Lua\"],[1,\"Python\"],[0,\" API (`ac.ge\"]],\"start1\":387,\"start2\":387,\"length1\":27,\"length2\":30},{\"diffs\":[[0,\"s\\\n- \"],[-1,\"Missing: some high-frequency raw physics vectors (1000Hz), but Lua provides filtered values suitable for analysis\\\n- Native UDP output also exists but may need format parsing\"],[1,\"**Lua plugin approach: ABANDONED** — CSP lacks compiled LuaSocket C core (`socket.core.dll`), see Section 4\"],[0,\"\\\n\\\n##\"]],\"start1\":534,\"start2\":534,\"length1\":181,\"length2\":115},{\"diffs\":[[0,\"ock-\"],[-1,\"lock-\"],[0,\"free\"]],\"start1\":2608,\"start2\":2608,\"length1\":13,\"length2\":8},{\"diffs\":[[0,\" 3. \"],[-1,\"Why the\"],[1,\"Architecture — Dual-Source Telemetry for AC\\\n\\\n### Two Independent Data Sources\\\n\\\nAC provides telemetry through two independent channels. The CSP\"],[0,\" Lua \"],[-1,\"P\"],[1,\"p\"],[0,\"lugin \"],[-1,\"A\"],[1,\"a\"],[0,\"ppro\"]],\"start1\":2893,\"start2\":2893,\"length1\":28,\"length2\":163},{\"diffs\":[[0,\"ch (\"],[-1,\"Not Shared Memory Executable)\\\n\\\n- Shared memory bridge executables are **Windows-only** — won't work natively on Linux\\\n- A **Lua plugin runs inside the game process** under Proton, so it works cross-platform\\\n- The Lua API provides 95%+ of the data shared memory offers\\\n- This is the cle\"],[1,\"port 5005) has been abandoned (see Section 4).\\\n\\\n**Feed 1: KSUDP (port 9996) — AC Engine Built-in**\\\n- Provider: AC engine itself (NOT CSP, NOT plugin)\\\n- Protocol: 3-phase handshake registration (see Section 5)\\\n- Works identically on Windows and Linux/Proton\\\n- No separate installation required\\\n\\\n**Feed 2: Telemetry Tool Plugin (port 10101) — AC Python Plugin System**\\\n- Provider: AC's Python plugin system + third-party plugin by IkoRein v1.3\\\n- Download: https://www.racedepartment.com/downloads/telemetry-tool-for-ac.37231/\\\n- Config: `<AC>/apps/python/Telemetry_Tool_plugin/config.ini` — set `to_ip` and `to_port`\\\n- Enable: `python.ini` → `[TELEMETRY_TOOL_PLUGIN] ACTIVE=1`\\\n- **OPTIONAL** — requires separate installation of the Telemetry Tool plugin\\\n- Works on both Windows \"],[0,\"an\"],[1,\"d\"],[0,\" Linux\"],[-1,\"-native path\\\n\\\n---\\\n\\\n## 4. Architecture — Triple-Source Telemetry for AC\\\n\\\n### Three Independent Data Sources\\\n\\\nAC provides telemetry through three independent channels:\"],[1,\"/Proton (AC's built-in Python interpreter)\\\n\\\n**Both feeds work on pure Windows without Proton — neither depends on CSP.**\\\n\\\nIf more data is ever needed, AC's Python plugin system can be used (same mechanism as the Telemetry Tool plugin). The abandoned CSP Lua plugin approach is documented in Section 4 for reference only.\\\n\\\n### Architecture Diagram\"],[0,\"\\\n\\\n``\"]],\"start1\":3057,\"start2\":3057,\"length1\":466,\"length2\":1138},{\"diffs\":[[0,\"────────────\"],[-1,\"────────\"],[0,\"┐\\\n│         \"]],\"start1\":4252,\"start2\":4252,\"length1\":32,\"length2\":24},{\"diffs\":[[0,\"                \"],[-1,\"       \"],[0,\"│\\\n│             \"]],\"start1\":4318,\"start2\":4318,\"length1\":39,\"length2\":32},{\"diffs\":[[0,\"                \"],[-1,\"       \"],[0,\"│\\\n│  ┌──────────\"]],\"start1\":4388,\"start2\":4388,\"length1\":39,\"length2\":32},{\"diffs\":[[0,\"────────────────\"],[1,\"──\"],[0,\"┐\"],[1,\" \"],[0,\" ┌──────────────\"]],\"start1\":4414,\"start2\":4414,\"length1\":33,\"length2\":36},{\"diffs\":[[0,\"────\"],[-1,\"┐ ┌──────────────────┐\"],[1,\"───┐        \"],[0,\" │\\\n│\"]],\"start1\":4455,\"start2\":4455,\"length1\":30,\"length2\":20},{\"diffs\":[[0,\"        \"],[-1,\"│\"],[1,\"  │ \"],[0,\" │ Telem\"]],\"start1\":4491,\"start2\":4491,\"length1\":17,\"length2\":20},{\"diffs\":[[0,\"in  \"],[-1,\"│ │ ac-telemetry-\"],[1,\"   │    \"],[0,\"    \"],[-1,\"│\"],[0,\" │\\\n│\"]],\"start1\":4525,\"start2\":4525,\"length1\":30,\"length2\":20},{\"diffs\":[[0,\"n)          \"],[-1,\"│\"],[1,\"  │ \"],[0,\" │ (Python, \"]],\"start1\":4557,\"start2\":4557,\"length1\":25,\"length2\":28},{\"diffs\":[[0,\"on, \"],[-1,\"3rd party)    │ │ plugin (CSP Lua) │ │\\\n│  │                     │ │ IkoRein v1.3            │ │ (ours) \"],[1,\"IkoRein v1.3)    │\"],[0,\"        \"],[-1,\"  │\"],[0,\" │\\\n│  │ \"],[-1,\"Binary\"],[1,\"KSUDP\"],[0,\", ~2\"]],\"start1\":4581,\"start2\":4581,\"length1\":136,\"length2\":47},{\"diffs\":[[0,\"z       \"],[-1,\"│\"],[1,\"   │ \"],[0,\" │ Binar\"]],\"start1\":4630,\"start2\":4630,\"length1\":17,\"length2\":21},{\"diffs\":[[0,\"cars\"],[-1,\"       │ │ JSON, 60Hz\"],[1,\", per-fps │  \"],[0,\"       │\"],[-1,\" │\"],[0,\"\\\n│  │ B\"],[-1,\"asic fields        │ │ Full fields incl.      │ │ Configurable     │ │\\\n│  │ Registration-based\"],[1,\"INDS :9996           │  │ SENDS to :10101        \"],[0,\"  \"],[-1,\"│\"],[0,\" │ \"],[-1,\"wheel slip, tyre temps │ │ CSP extensions   │\"],[1,\"       \"],[0,\" │\\\n│\"]],\"start1\":4658,\"start2\":4658,\"length1\":191,\"length2\":97},{\"diffs\":[[0,\"  │\\\n│  └────────\"],[1,\"──\"],[0,\"┬───────────┘ └─\"]],\"start1\":4750,\"start2\":4750,\"length1\":32,\"length2\":34},{\"diffs\":[[0,\"──┬───────────┘ \"],[-1,\"└\"],[1,\" └─\"],[0,\"─────────┬──────\"]],\"start1\":4766,\"start2\":4766,\"length1\":33,\"length2\":35},{\"diffs\":[[0,\"────\"],[-1,\"──────┘ └────────┬─\"],[0,\"────\"]],\"start1\":4799,\"start2\":4799,\"length1\":27,\"length2\":8},{\"diffs\":[[0,\"───────────────┘\"],[-1,\" │\\\n│  \"],[0,\"         │ BINDS\"]],\"start1\":4796,\"start2\":4796,\"length1\":38,\"length2\":32},{\"diffs\":[[0,\"   │\"],[-1,\" BINDS :9996           │ SENDS to :10101          │ SENDS to  │ │\\\n│           │ (listener)            │ (unbound socket)         │ :5005     │ │\\\n│\"],[1,\"\\\n│             │                  \"],[0,\"        \"],[-1,\"   \"],[0,\"│   \"]],\"start1\":4818,\"start2\":4818,\"length1\":165,\"length2\":50},{\"diffs\":[[0,\"            \"],[-1,\"         │\"],[0,\"            \"]],\"start1\":4867,\"start2\":4867,\"length1\":34,\"length2\":24},{\"diffs\":[[0,\"    \"],[-1,\"             │ (unbound) │ │\\\n└───────────┼──────────\"],[1,\"│\\\n└\"],[0,\"────\"]],\"start1\":4888,\"start2\":4888,\"length1\":60,\"length2\":11},{\"diffs\":[[0,\"────\"],[-1,\"┘\\\n            │ \"],[1,\"────────────────┘\\\n\"],[0,\"    \"]],\"start1\":4942,\"start2\":4942,\"length1\":24,\"length2\":26},{\"diffs\":[[0,\"            \"],[-1,\"        \"],[0,\"│           \"]],\"start1\":4966,\"start2\":4966,\"length1\":32,\"length2\":24},{\"diffs\":[[0,\"      │\\\n\"],[-1,\"            ▼    \"],[0,\"        \"]],\"start1\":4999,\"start2\":4999,\"length1\":33,\"length2\":16},{\"diffs\":[[0,\"        \"],[-1,\"     \"],[0,\"▼       \"]],\"start1\":5013,\"start2\":5013,\"length1\":21,\"length2\":16},{\"diffs\":[[0,\"────────────\"],[-1,\"────────\"],[0,\"┐\\\n│         \"]],\"start1\":5105,\"start2\":5105,\"length1\":32,\"length2\":24},{\"diffs\":[[0,\"        \"],[-1,\"   \"],[0,\"ANALYSIS\"]],\"start1\":5131,\"start2\":5131,\"length1\":19,\"length2\":16},{\"diffs\":[[0,\"            \"],[-1,\"    │\\\n│      \"],[1,\"│\\\n│\"],[0,\"            \"]],\"start1\":5176,\"start2\":5176,\"length1\":37,\"length2\":27},{\"diffs\":[[0,\"                \"],[-1,\" \"],[0,\"│\\\n│  ┌──────────\"]],\"start1\":5242,\"start2\":5242,\"length1\":33,\"length2\":32},{\"diffs\":[[0,\"────────\"],[1,\"─\"],[0,\" Multi-P\"]],\"start1\":5280,\"start2\":5280,\"length1\":16,\"length2\":17},{\"diffs\":[[0,\"UDP \"],[-1,\"Receiver ──\"],[0,\"────\"]],\"start1\":5301,\"start2\":5301,\"length1\":19,\"length2\":8},{\"diffs\":[[0,\"───────┐\"],[1,\"  \"],[0,\" │\\\n│  │ \"]],\"start1\":5316,\"start2\":5316,\"length1\":16,\"length2\":18},{\"diffs\":[[0,\"  :9996 \"],[1,\" (handshake)\"],[0,\" ──→ par\"]],\"start1\":5333,\"start2\":5333,\"length1\":16,\"length2\":28},{\"diffs\":[[0,\"─┐  \"],[-1,\"(registration protocol)\"],[1,\" \"],[0,\"    \"]],\"start1\":5376,\"start2\":5376,\"length1\":31,\"length2\":9},{\"diffs\":[[0,\"┐              │\"],[1,\"  \"],[0,\" │\\\n│  │  :10101 \"]],\"start1\":5377,\"start2\":5377,\"length1\":32,\"length2\":34},{\"diffs\":[[0,\" :10101 \"],[1,\"(optional)  \"],[0,\"──→ pars\"]],\"start1\":5403,\"start2\":5403,\"length1\":16,\"length2\":28},{\"diffs\":[[0,\"ry  \"],[-1,\" \"],[0,\"──┼──→\"],[-1,\" Core Telemetry\"],[0,\" Mod\"]],\"start1\":5441,\"start2\":5441,\"length1\":30,\"length2\":14},{\"diffs\":[[0,\"    \"],[-1,\"      │ │\\\n│  │  :5005  ──→ parser-ac         ──┤   + Per-Feed Status                │ │\\\n│  │  :5015  ──→ parser-dr2        ──┤   + Data Dump per Feed             │ │\\\n│  │  :5006  ──→ parser-pcars      ──┘                                     │ │\\\n│  └─────────────────────────────────────────────────────────────────────┘ │\\\n│                                                                       \"],[1,\"│   │\\\n│  └────────────────────────────────────────────┘              │\"],[0,\"   │\"]],\"start1\":5458,\"start2\":5458,\"length1\":402,\"length2\":78},{\"diffs\":[[0,\"─────┐      \"],[-1,\"         \"],[1,\"│\"],[0,\"   │\\\n│  │ Pl\"]],\"start1\":5587,\"start2\":5587,\"length1\":33,\"length2\":25},{\"diffs\":[[0,\"    \"],[-1,\"            │\\\n│  │ Checker      │  │ Engine    │  │ (GLM-5.1)        │                  │\\\n│  └──────────────┘  └───────────┘  └──────────────────┘                  │\\\n│                                                                          │\\\n│  ┌──────────────────────────────────────────────────────────────────────┐│\\\n│  │ Data Dump: ./data/<session>/<feed>_<timestamp>.{json,bin,csv}       ││\\\n│  └──────────────────────────────────────────────────────────────────────┘│\\\n└──────────────────────────────────────────────────────────────────────────┘\\\n```\\\n\\\n### ⚠️ Port 9996 (KSUDP) — Registration Protocol (NOT Send-Only)\\\n\\\n**This is NOT a simple fire-and-forget UDP feed.** AC's built-in `RemoteTelemetryUDP` uses a registration/handshake protocol:\\\n\\\n```\\\n┌─────────────────────┐                  ┌──────────────────────┐\\\n│   Assetto Corsa      │                  │   External Tool      │\\\n│                      │                  │   (rusty-telemetry)  │\\\n│   BINDS to :9996     │                  │   BINDS to :12000    │\\\n│   (listener socket)  │                  │   (receiver socket)  │\\\n│                      │                  │                      │\\\n│                      │  ◄── register ── │  sends handshake     │\\\n│                      │     packet       │  to :9996            │\\\n│                      │                  │                      │\\\n│                      │  ── telemetry ──►│  receives on :12000  │\\\n│                      │     data         │  from AC             │\\\n└─────────────────────┘                  └──────────────────────┘\\\n```\\\n\\\n**Evidence:**\\\n1. AC log: `RemoteTelemetryUDP :: creating socket on port 9996` → `ERROR BINDING LISTENER SOCKET` — AC explicitly calls it a **LISTENER** socket and tries to **bind** to 9996\\\n2. **No destination config exists anywhere** — no INI file sets where AC should send KSUDP data. The destination is discovered dynamically from registration packet source addresses\\\n3. This follows Kunos's standard pattern (same as the ACSP server plugin protocol in `sdk/dev/acRemoteServerUDP_Example/`)\\\n\\\n**⚠️ Port conflict**: If `rusty-telemetry` binds to `:9996` before AC starts, AC cannot bind its listener socket → `RemoteTelemetryUDP` never initializes → zero KSUDP packets. This is exactly what happened in the 2026-06-03 session.\\\n\\\n**Implementation for rusty-telemetry:**\\\n1. Do **NOT** bind to port 9996 — let AC own it\\\n2. Bind a receiver socket to a different port (e.g., `:12000` or OS-assigned ephemeral)\\\n3. Send a registration/handshake packet to `127.0.0.1:9996`\\\n4. AC reads the registration, extracts the source address, starts streaming telemetry back\\\n5. The handshake protocol details need to be reverse-engineered (check community docs, AC SDK examples, or capture traffic with AC running alone on 9996)\\\n\\\n**Alternative**: Since ports 10101 (Telemetry Tool) and 5005 (our Lua plugin) provide richer data without any registration dance, consider **dropping KSUDP** from the initial implementation and focusing on the two send-only feeds.\\\n\\\n### Source Comparison\\\n\\\n| Aspect | Port 9996 (built-in) | Port 10101 (Telemetry Tool) | Port 5005 (our Lua plugin) |\\\n|---\"],[1,\"│   │\\\n│  └──────────────┘  └───────────┘  └──────────────────┘      │   │\\\n└──────────────────────────────────────────────────────────────────┘\\\n```\\\n\\\n### Source Comparison\\\n\\\n| Aspect | Port 9996 (KSUDP) | Port 10101 (Telemetry Tool) |\\\n\"],[0,\"|---\"]],\"start1\":5663,\"start2\":5663,\"length1\":3129,\"length2\":240},{\"diffs\":[[0,\"C engine\"],[1,\" (built-in)\"],[0,\" | Pytho\"]],\"start1\":5929,\"start2\":5929,\"length1\":16,\"length2\":27},{\"diffs\":[[0,\"n) |\"],[-1,\" CSP Lua app (ours) |\\\n| **Protocol** | ⚠️ Registration-based\"],[1,\"\\\n| **Protocol** | 3-phase handshake\"],[0,\" (AC\"]],\"start1\":5972,\"start2\":5972,\"length1\":68,\"length2\":43},{\"diffs\":[[0,\"AC binds\"],[1,\" :9996\"],[0,\", tool r\"]],\"start1\":6013,\"start2\":6013,\"length1\":16,\"length2\":22},{\"diffs\":[[0,\"s) |\"],[-1,\" ✅ Send-only (unbound socket) | ✅\"],[0,\" Sen\"]],\"start1\":6042,\"start2\":6042,\"length1\":41,\"length2\":8},{\"diffs\":[[0,\"* | \"],[-1,\"❌ Port conflict with rusty-telemetry\"],[1,\"⚠️ Needs protocol fix — handshake bugs found\"],[0,\" | ✅\"]],\"start1\":6087,\"start2\":6087,\"length1\":44,\"length2\":52},{\"diffs\":[[0,\"WORKING** — \"],[-1,\"4.55\"],[1,\"6.3\"],[0,\" MB captured\"]],\"start1\":6142,\"start2\":6142,\"length1\":28,\"length2\":27},{\"diffs\":[[0,\"ed |\"],[-1,\" ❌ Script init fails (diagnosing) |\"],[0,\"\\\n| *\"]],\"start1\":6167,\"start2\":6167,\"length1\":43,\"length2\":8},{\"diffs\":[[0,\"ry (\"],[-1,\"community RE\"],[1,\"official Google Doc\"],[0,\") | \"]],\"start1\":6191,\"start2\":6191,\"length1\":20,\"length2\":27},{\"diffs\":[[0,\" in \"],[-1,\"source) | JSON (our schema\"],[1,\"Python source\"],[0,\") |\\\n\"]],\"start1\":6232,\"start2\":6232,\"length1\":34,\"length2\":21},{\"diffs\":[[0,\"me |\"],[-1,\" 60 Hz configurable |\"],[0,\"\\\n| *\"]],\"start1\":6288,\"start2\":6288,\"length1\":29,\"length2\":8},{\"diffs\":[[0,\"S) |\"],[-1,\" Configurable, CSP extensions |\"],[0,\"\\\n| *\"]],\"start1\":6351,\"start2\":6351,\"length1\":39,\"length2\":8},{\"diffs\":[[0,\"s) |\"],[-1,\" No (focused car, can extend) |\"],[0,\"\\\n| *\"]],\"start1\":6420,\"start2\":6420,\"length1\":39,\"length2\":8},{\"diffs\":[[0,\"red via \"],[-1,\"registration\"],[1,\"handshake\"],[0,\") | conf\"]],\"start1\":6463,\"start2\":6463,\"length1\":28,\"length2\":25},{\"diffs\":[[0,\"t) |\"],[-1,\" config.lua (IP/port) |\"],[0,\"\\\n| *\"]],\"start1\":6502,\"start2\":6502,\"length1\":31,\"length2\":8},{\"diffs\":[[0,\"Yes \"],[-1,\"| ✅ Yes | ❌ Not yet (trivial to add) |\\\n| **Wheel slip** | ❌ No | ✅ Yes | ✅ Yes\"],[1,\"(carCoordinates[3]) | ✅ Yes (x, y, z) |\\\n| **Wheel slip** | ✅ Yes (slipAngle, slipRatio, tyreSlip, ndSlip) | ✅ Yes (slipRatio, slipAngle, ndSlip, wheel_slip)\"],[0,\" |\\\n|\"]],\"start1\":6532,\"start2\":6532,\"length1\":86,\"length2\":164},{\"diffs\":[[0,\"mps \"],[-1,\"| Available via CSP API\"],[1,\"per wheel\"],[0,\" |\\\n|\"]],\"start1\":6730,\"start2\":6730,\"length1\":31,\"length2\":17},{\"diffs\":[[0,\"| ✅ \"],[-1,\"200 Hz | ✅ Per-frame | ✅ 60 Hz |\\\n| **Crash resilience** | Never crashes | Python process | Lua script can crash\"],[1,\"suspensionHeight[4] | ✅ rideHeight (front/rear) |\\\n| **Crash resilience** | Never crashes (engine built-in) | Python process |\\\n| **Installation** | None required | Optional — requires plugin install\"],[0,\" |\\\n\\\n\"]],\"start1\":6763,\"start2\":6763,\"length1\":119,\"length2\":205},{\"diffs\":[[0,\"| 9996 (\"],[-1,\"built-in\"],[1,\"KSUDP\"],[0,\") | 1010\"]],\"start1\":7014,\"start2\":7014,\"length1\":24,\"length2\":21},{\"diffs\":[[0,\"l) |\"],[-1,\" 5005 (our plugin) |\\\n|---\"],[1,\"\\\n\"],[0,\"|---\"]],\"start1\":7051,\"start2\":7051,\"length1\":33,\"length2\":9},{\"diffs\":[[0,\" ✅ | ✅ |\"],[-1,\" ✅ |\"],[0,\"\\\n| Braki\"]],\"start1\":7091,\"start2\":7091,\"length1\":20,\"length2\":16},{\"diffs\":[[0,\" ✅ | ✅ |\"],[-1,\" ✅ |\"],[0,\"\\\n| Racin\"]],\"start1\":7126,\"start2\":7126,\"length1\":20,\"length2\":16},{\"diffs\":[[0,\"s | \"],[-1,\"⚠️ (has world pos) | ✅ (has world pos) | ⚠️ (needs \"],[1,\"✅ (carCoordinates) | ✅ (\"],[0,\"worl\"]],\"start1\":7156,\"start2\":7156,\"length1\":59,\"length2\":32},{\"diffs\":[[0,\"orld pos\"],[1,\"ition\"],[0,\") |\\\n| Th\"]],\"start1\":7185,\"start2\":7185,\"length1\":16,\"length2\":21},{\"diffs\":[[0,\"g | \"],[-1,\"⚠️ (infer from wheel speed) | ✅ (has wheel slip) | ✅ (has\"],[1,\"✅ (slipRatio, tyreSlip) | ✅ (slipRatio,\"],[0,\" wheel\"],[-1,\" \"],[1,\"_\"],[0,\"slip\"]],\"start1\":7229,\"start2\":7229,\"length1\":72,\"length2\":54},{\"diffs\":[[0,\"er |\"],[-1,\" ⚠️ (~70% confidence) |\"],[0,\" ✅ (~\"],[-1,\"9\"],[1,\"8\"],[0,\"5% w\"]],\"start1\":7307,\"start2\":7307,\"length1\":37,\"length2\":14},{\"diffs\":[[0,\"t | \"],[-1,\"❌ | ✅ (core temps) | ✅ (full CSP tyre API\"],[1,\"⚠️ (dirtyLevel only) | ✅ (core temps\"],[0,\") |\\\n\"]],\"start1\":7380,\"start2\":7380,\"length1\":49,\"length2\":44},{\"diffs\":[[0,\" ❌ |\"],[-1,\" ✅ (5-zone damage) |\"],[0,\"\\\n| M\"]],\"start1\":7449,\"start2\":7449,\"length1\":28,\"length2\":8},{\"diffs\":[[0,\"| ❌ \"],[-1,\"| ✅ (all\"],[1,\"(focused\"],[0,\" car\"],[-1,\"s\"],[0,\") | \"],[-1,\"❌ (can extend\"],[1,\"✅ (all cars\"],[0,\") |\\\n\"]],\"start1\":7475,\"start2\":7475,\"length1\":38,\"length2\":35},{\"diffs\":[[0,\" rewrite to \"],[-1,\"registration\"],[1,\"3-phase handshake\"],[0,\" protocol**:\"]],\"start1\":7633,\"start2\":7633,\"length1\":36,\"length2\":41},{\"diffs\":[[0,\"g., \"],[-1,\"`:12000`)\\\n   - Send registration/handshake packet\"],[1,\"OS-assigned ephemeral)\\\n   - **Phase 1**: Send HANDSHAKE `{1, 1, 0}` (12 bytes) to `127.0.0.1:9996`\\\n   - **Phase 2**: Receive 408-byte HandshakerResponse (parse car/driver/track info)\\\n   - **Phase 3**: Send SUBSCRIBE_UPDATE `{1, 1, 1}` (12 bytes)\"],[0,\" to \"]],\"start1\":7767,\"start2\":7767,\"length1\":57,\"length2\":253},{\"diffs\":[[0,\"Receive \"],[-1,\"telemetry data\"],[1,\"continuous RTCarInfo structs\"],[0,\" on the \"]],\"start1\":8042,\"start2\":8042,\"length1\":30,\"length2\":44},{\"diffs\":[[0,\"  - \"],[-1,\"Handshake protocol needs reverse-engineering (see Section 14)\"],[1,\"Reference: https://docs.google.com/document/d/1KfkZiIluXZ6mMhLWfDX1qAGbvhGRC3ZUzjVIt5FQpp4/pub\"],[0,\"\\\n\\\n2.\"]],\"start1\":8103,\"start2\":8103,\"length1\":69,\"length2\":102},{\"diffs\":[[0,\"101 \"],[-1,\"& 5005 \"],[0,\"— ke\"]],\"start1\":8215,\"start2\":8215,\"length1\":15,\"length2\":8},{\"diffs\":[[0,\"** (\"],[-1,\"these are \"],[0,\"send\"]],\"start1\":8257,\"start2\":8257,\"length1\":18,\"length2\":8},{\"diffs\":[[0,\"feed\"],[-1,\"s\"],[0,\", tool\"],[-1,\"s\"],[0,\" bind\"],[1,\"s\"],[0,\" to \"]],\"start1\":8271,\"start2\":8271,\"length1\":21,\"length2\":20},{\"diffs\":[[0,\"101`\"],[-1,\" and `:5005` respectively\"],[0,\"\\\n   \"]],\"start1\":8318,\"start2\":8318,\"length1\":33,\"length2\":8},{\"diffs\":[[0,\"loop\"],[-1,\" per feed\"],[0,\"\\\n\\\n3.\"]],\"start1\":8342,\"start2\":8342,\"length1\":17,\"length2\":8},{\"diffs\":[[0,\"tool\"],[-1,\" / ac_plugin\"],[0,\")\\\n  \"]],\"start1\":8441,\"start2\":8441,\"length1\":20,\"length2\":8},{\"diffs\":[[0,\"ry)\\\n\"],[-1,\"   - `./data/<session_id>/feed_5005_acplugin_<timestamp>.json` (parsed JSON)\\\n   - `./data/<session_id>/feed_5005_acplugin_<timestamp>.csv` (human-readable)\\\n\"],[0,\"   -\"]],\"start1\":8824,\"start2\":8824,\"length1\":164,\"length2\":8},{\"diffs\":[[0,\"      ⏳ \"],[-1,\"Registration\"],[1,\"Handshake\"],[0,\" (sendin\"]],\"start1\":9141,\"start2\":9141,\"length1\":28,\"length2\":25},{\"diffs\":[[0,\"/s)\\\n\"],[-1,\"   Feed :5005  [AC Lua Plugin]    ⏳ Waiting for data...\\\n\"],[0,\"   ─\"]],\"start1\":9230,\"start2\":9230,\"length1\":64,\"length2\":8},{\"diffs\":[[0,\"603_\"],[-1,\"094500/\\\n   ```\\\n\\\n### Telemetry Tool Plugin (Port 10101) — Packet Structure\\\n\\\n**Source**: `/home/jan/.steam/.../apps/python/Telemetry_Tool_plugin/Telemetry_Tool_plugin.py`\\\n**Config**: `config.ini` — `to_ip=127.0.0.1`, `to_port=10101`\\\n\\\n**Packet structure per driver** (binary, little-endian, packed):\\\n\\\n| Offset | Type | Field | Notes |\\\n|--------|------|-------|-------|\\\n| 0 | uint32 | driver_id | Car index |\\\n| 4 | uint32 | lap_time | Current lap time (ms) |\\\n| 8 | uint32 | last_lap | Last completed lap (ms) |\\\n| 12 | uint32 | best_lap | Best lap (ms) |\\\n| 16 | byte | current_splits_num | Number of splits |\\\n| 17 | uint32×3 | cur_split[1-3] | Current sector times (ms) |\\\n| 29 | uint32×3 | last_split[1-3] | Last lap sector times (ms) |\\\n| 41 | float | steer | Steering input |\\\n| 45 | float | throttle | Throttle 0–1 |\\\n| 49 | float | brake | Brake 0–1 |\\\n| 53 | float | clutch | Clutch 0–1 |\\\n| 57 | int16 | gear | Current gear |\\\n| 59 | float | engineRPM | Engine RPM |\\\n| 63 | float | speed | Speed km/h |\\\n| 67 | int16 | track_position | Track position |\\\n| 69 | int16 | leaderboard_position | Leaderboard pos |\\\n| 71 | int16 | lap_count | Lap number |\\\n| 73 | float×3 | x, y, z | World position |\\\n| 85 | double | spline_position | Normalized 0–1 |\\\n| 93 | float | test_float | Validation (666.666) |\\\n| 97 | char[33] | driver_name | Null-padded |\\\n| 130 | char[4] | nationality | Null-padded |\\\n| 134 | char[33] | car_name | Null-padded |\\\n| 167 | char[10] | tyre_name | Tyre compound |\\\n| 177 | uint32 | test_int | Validation (666) |\\\n| 181 | byte | is_connected | Connection status |\\\n| 182 | byte | is_in_pit | Pit stop active |\\\n| 183 | byte | is_car_in_pitlane | In pit lane |\\\n| 184 | byte | valid | Lap validity flag |\\\n| 185 | float | acc_vertical | Vertical G |\\\n| 189 | float | acc_horizontal | Horizontal G |\\\n| 193 | float | acc_frontal | Longitudinal G |\\\n| 196 | byte | is_engine_limiter_on | Rev limiter |\\\n| 197 | byte | is_drs_available | DRS available |\\\n| 198 | byte | is_drs_enabled | DRS active |\\\n| 199 | byte | is_race_finished | Race complete |\\\n| 200 | byte | ptp_status | Push-to-pass status |\\\n| 201 | uint32 | ptp_activations | P2P uses remaining |\\\n| 205 | float | ers_max_j | ERS capacity |\\\n| 209 | float | ers_current_kj | ERS current charge |\\\n| 213 | float×4 | tyre_dirty_level | Tyre marbles (4 wheels) |\\\n| 229 | float | turbo_boost | Turbo boost |\\\n| 233 | float×2 | ride_height | Front/rear ride height |\\\n| 241 | float×4 | current_tyres_core_temp | Core temps (4 wheels) |\\\n| 257 | float×4 | tyres_slip_ratio | Slip ratios (4 wheels) |\\\n| 273 | float×4 | tyres_slip_angle | Slip angles (4 wheels) |\\\n| 289 | float×4 | nd_slip | Normalized slip (4 wheels) |\\\n| 305 | float×4 | wheel_slip | Wheel slip (4 wheels) |\\\n| 321 | float×3 | world_speed | World velocity vector |\\\n| 333 | float×3 | local_speed | Local velocity vector |\\\n| 345 | int32 | valid (trailer) | Validation check |\\\n| 349 | double | test_float (trailer) | Validation (666.666) |\\\n\\\n**Outer wrapper**: `byte(1) packet_type=22` + `[driver_data × cars_count]`\\\n\\\n### AC Built-in UDP (Port 9996) — Reference\\\n\\\n**Status**: Binary, community-reverse-engineered. Mirrors AC shared memory layout.\\\n\\\n| Field | Type | Rate |\\\n|---|---|---|\\\n| packetId, speedKmh, rpm, gear | int/float | ~20 Hz |\\\n| gas, brake, clutch, steerAngle | float | ~20 Hz |\\\n| G_LAT, G_LON | float | ~20 Hz |\\\n| splinePosition, worldPosition | float/vec3 | ~20 Hz |\\\n| suspensionTravel[4] | float×4 | **200 Hz** |\\\n| wheelSpeed[4] | float×4 | ~20 Hz |\\\n| lapTime, lapCount, fuel | float/int | ~20 Hz |\\\n| bestLap, lastLap | float | — |\\\n\\\n---\\\n\\\n## 5. Repositories & Deployment\\\n\\\n### Repo 1: `ac-telemetry-plugin` (AC Lua Plugin)\\\n\\\n| Field | Value |\\\n|---|---|\\\n| **Language** | Lua (LuaJIT) |\\\n| **IDE** | IntelliJ IDEA + EmmyLua |\\\n| **Project path** | `~/IdeaProjects/ac-telemetry-plugin` |\\\n| **Remote** | `ssh://jan@192.168.1.2/home/jan/Development/Repositories/ac-telemetry-plugin.git` |\\\n| **Codeberg** | `ssh://git@codeberg.org/jhunnius/ac-telemetry-plugin` |\\\n| **Runs on** | Game machine (inside AC / Proton) |\\\n| **Status** | **Phase 1.2 — diagnosing LuaSocket availability in CSP sandbox** |\\\n\\\n#### Current Folder State\\\n\\\n| Role | Path | Status |\\\n|---|---|---|\\\n| **Project source** | `~/IdeaProjects/ac-telemetry-plugin` | ✅ App variant + LuaSocket fix + diagnostic logging |\\\n| **App deployment** | `<AC>/apps/lua/ac-telemetry-plugin/` | ✅ Synced with project source |\\\n| **Extension deployment** | `<AC>/extension/lua/ac-telemetry-plugin/` | ❌ **DELETED** |\\\n\\\n#### Changes Made (Uncommitted)\\\n\\\n1. **manifest.ini**: `[SCRIPT]` → `[WINDOW_...]` with `[CORE] LAZY=FULL`, `REQUIRED_VERSION=0`\\\n2. **main.lua**: Added `script.windowMain(dt)` with `ui.text(\\\"AC Telemetry ●\\\")`\\\n3. **src/telemetry.lua**: Fixed `require(\\\"socket.udp\\\")` → `require(\\\"socket\\\")` + `socket.udp()`\\\n4. **src/telemetry.lua**: Added `ac.log()` diagnostic checkpoints before/after `require(\\\"socket\\\")` and `socket.udp()`\\\n5. **icon.png**: Copied from apps/lua deployment (209 bytes)\\\n\\\n#### Next Commit Should Include\\\n\\\n```\\\nfix: correct LuaSocket require pattern, switch to app window variant\\\n\\\n- Fix: require(\\\"socket.udp\\\") → require(\\\"socket\\\") + socket.udp()\\\n  socket.udp is a function, not a module. The old pattern crashed the\\\n  entire script at load time, causing blank windows and no UDP output.\\\n- Change manifest.ini from [SCRIPT] to [WINDOW_...] with CORE LAZY=FULL\\\n- Add script.windowMain() with app title display\\\n- Add icon.png for app bar\\\n- Add ac.log() diagnostic checkpoints around socket initialization\\\n```\\\n\\\n### Repo 2\"],[1,\"193200/\\\n   ```\\\n\\\n---\\\n\\\n## 4. Lua Plugin — ABANDONED\\\n\\\nThe CSP Lua plugin approach (port 5005) is **abandoned**. Root cause:\\\n\\\n- CSP ships only the pure-Lua wrapper of LuaSocket (`socket.lua`), NOT the compiled C core (`socket/core.dll`)\\\n- `socket.lua` line 12: `require(\\\"socket.core\\\")` → FAILS because the C library doesn't exist\\\n- `type(ac)` returns `\\\"userdata\\\"` (not `\\\"table\\\"`) in CSP's LuaJIT, so the `ac_available` check failed, diagnostic `ac.log()` calls were never reached\\\n- CSP has no raw UDP socket API — only `web.get()` (HTTP), `web.socket()` (WebSocket), `ac.connect()` (shared memory IPC)\\\n- File-based or HTTP-based IPC cannot achieve target frequency (60Hz)\\\n- The repo `ac-telemetry-plugin` should be archived\\\n\\\nIf more data is ever needed, use AC's Python plugin system (same mechanism as Telemetry Tool) — it has full access to `ac.getCarState()` and can use Python's `socket` module without restrictions.\\\n\\\n### Abandoned Repo: `ac-telemetry-plugin`\\\n\\\n| Field | Value |\\\n|---|---|\\\n| **Language** | Lua (LuaJIT) |\\\n| **IDE** | IntelliJ IDEA + EmmyLua |\\\n| **Project path** | `~/IdeaProjects/ac-telemetry-plugin` |\\\n| **Remote** | `ssh://jan@192.168.1.2/home/jan/Development/Repositories/ac-telemetry-plugin.git` |\\\n| **Codeberg** | `ssh://git@codeberg.org/jhunnius/ac-telemetry-plugin` |\\\n| **Status** | **ABANDONED** — CSP lacks socket.core, cannot send UDP from Lua |\\\n\\\n---\\\n\\\n## 5. KSUDP (Port 9996) — Complete Protocol Documentation\\\n\\\n**Reference**: https://docs.google.com/document/d/1KfkZiIluXZ6mMhLWfDX1qAGbvhGRC3ZUzjVIt5FQpp4/pub\\\nThis is the official AC Remote Telemetry documentation.\\\n\\\n### 5.1 Handshake Protocol — 3-Phase\\\n\\\n```\\\n┌─────────────────────┐                  ┌──────────────────────┐\\\n│   Assetto Corsa      │                  │   rusty-telemetry    │\\\n│                      │                  │                      │\\\n│   BINDS to :9996     │                  │   Receiver socket    │\\\n│   (listener socket)  │                  │   (any port)         │\\\n│                      │                  │                      │\\\n│                      │  ◄── Phase 1 ──  │  Send HANDSHAKE      │\\\n│                      │     12 bytes      │  {1, 1, 0}          │\\\n│                      │                  │                      │\\\n│                      │  ── Phase 2 ──►  │  Receive 408-byte    │\\\n│                      │     408 bytes     │  HandshakerResponse  │\\\n│                      │                  │                      │\\\n│                      │  ◄── Phase 3 ──  │  Send SUBSCRIBE      │\\\n│                      │     12 bytes      │  {1, 1, 1}          │\\\n│                      │                  │                      │\\\n│                      │  ── Continuous ─► │  RTCarInfo structs   │\\\n│                      │     ~20 Hz        │  (until DISMISS)    │\\\n└─────────────────────┘                  └──────────────────────┘\\\n```\\\n\\\n**Phase 1: Client sends HANDSHAKE to AC (port 9996)**\\\n```\\\nstruct Handshake {\\\n    int32 identifier;    // Platform type: 0=iPhone, 1=iPad, 2=AndroidPhone, 3=AndroidTablet (currently unused, set to 1)\\\n    int32 version;       // Protocol version (currently unused, set to 1)\\\n    int32 operationId;   // 0=HANDSHAKE, 1=SUBSCRIBE_UPDATE, 2=SUBSCRIBE_SPOT, 3=DISMISS\\\n};\\\n// For phase 1: { identifier=1, version=1, operationId=0 }\\\n// Total: 12 bytes, little-endian\\\n```\\\n\\\n**Phase 2: AC responds with HandshakerResponse**\\\n```\\\nstruct HandshakerResponse {\\\n    wchar_t[50] carName;      // 100 bytes (UTF-16LE, NOT ASCII as doc says)\\\n    wchar_t[50] driverName;   // 100 bytes (UTF-16LE)\\\n    int32       identifier;   // Always 4242 (connection valid indicator)\\\n    int32       version;      // Always 1\\\n    wchar_t[50] trackName;    // 100 bytes (UTF-16LE)\\\n    wchar_t[50] trackConfig;  // 100 bytes (UTF-16LE)\\\n};\\\n// Total: 408 bytes (6×100 + 2×4)\\\n```\\\n\\\n**NOTE**: The Google Doc says `char[50]` but the actual implementation uses UTF-16LE (2 bytes per char = 100 bytes per field). Total 408 bytes, confirmed by captured data.\\\n\\\n**Phase 3: Client sends SUBSCRIBE_UPDATE to AC (port 9996)**\\\n```\\\n// Same struct as Phase 1, but operationId = 1 (SUBSCRIBE_UPDATE)\\\n{ identifier=1, version=1, operationId=1 }\\\n```\\\n\\\nAfter Phase 3, AC adds the client as a listener and sends continuous telemetry updates.\\\n\\\n**SUBSCRIBE_SPOT (operationId=2)**: Subscribe to spot events only (e.g., lap completion), for ALL cars in session.\\\n**DISMISS (operationId=3)**: Unsubscribe. AC forgets the client.\\\n\\\n### 5.2 Continuous Telemetry — RTCarInfo (after SUBSCRIBE_UPDATE)\\\n\\\nAC sends this struct for every physics step to subscribed clients:\\\n\\\n```\\\nstruct RTCarInfo {\\\n    char    identifier;               // Always 'a' (0x61)\\\n    int32   size;                      // Size of this struct in bytes\\\n    float   speed_Kmh;\\\n    float   speed_Mph;\\\n    float   speed_Ms;\\\n    bool    isAbsEnabled;\\\n    bool    isAbsInAction;\\\n    bool    isTcInAction;\\\n    bool    isTcEnabled;\\\n    bool    isInPit;\\\n    bool    isEngineLimiterOn;\\\n    float   accG_vertical;\\\n    float   accG_horizontal;\\\n    float   accG_frontal;\\\n    int32   lapTime;                   // Current lap time (ms)\\\n    int32   lastLap;                   // Last completed lap (ms)\\\n    int32   bestLap;                   // Best lap (ms)\\\n    int32   lapCount;\\\n    float   gas;                       // 0–1\\\n    float   brake;                     // 0–1\\\n    float   clutch;                    // 0–1\\\n    float   engineRPM;\\\n    float   steer;\\\n    int32   gear;\\\n    float   cgHeight;\\\n    float   wheelAngularSpeed[4];\\\n    float   slipAngle[4];\\\n    float   slipAngle_ContactPatch[4];\\\n    float   slipRatio[4];\\\n    float   tyreSlip[4];\\\n    float   ndSlip[4];\\\n    float   load[4];\\\n    float   Dy[4];\\\n    float   Mz[4];\\\n    float   tyreDirtyLevel[4];\\\n    float   camberRAD[4];\\\n    float   tyreRadius[4];\\\n    float   tyreLoadedRadius[4];\\\n    float   suspensionHeight[4];\\\n    float   carPositionNormalized;     // Spline position 0–1\\\n    float   carSlope;\\\n    float   carCoordinates[3];         // World position X, Y, Z\\\n};\\\n```\\\n\\\n### 5.3 Spot Event — RTLap (after SUBSCRIBE_SPOT)\\\n\\\n```\\\nstruct RTLap {\\\n    int32   carIdentifierNumber;\\\n    int32   lap;\\\n    char    driverName[50];            // Likely also UTF-16LE in practice\\\n    char    carName[50];\\\n    int32   time;                      // Lap time in ms\\\n};\\\n```\\\n\\\n### 5.4 KSUDP Implementation Bugs Found (2026-06-03)\\\n\\\n**Bug 1: Wrong packet size** — Code sent 4 bytes (`1u32.to_le_bytes()`) instead of 12 bytes (3 × int32). AC reads operationId from uninitialized buffer, gets HANDSHAKE (0).\\\n\\\n**Bug 2: Missing subscribe phase** — Code only sends HANDSHAKE (phase 1), never sends SUBSCRIBE_UPDATE (phase 3). AC responds once per handshake but never starts continuous streaming.\\\n\\\n**Evidence**: 14 packets × 408 bytes over 3 minutes = 0.08 Hz (1 per handshake attempt every 10 seconds). Expected: ~20 Hz continuous.\\\n\\\n**Fix**: Implement proper 3-phase protocol:\\\n1. Send `{1, 1, 0}` (HANDSHAKE) to port 9996\\\n2. Receive 408-byte response (parse car/driver/track info)\\\n3. Send `{1, 1, 1}` (SUBSCRIBE_UPDATE) to port 9996\\\n4. Receive continuous RTCarInfo structs\\\n\\\n---\\\n\\\n## 6. Telemetry Tool Plugin (Port 10101) — Reference\\\n\\\n**Source code**: `<AC>/apps/python/Telemetry_Tool_plugin/Telemetry_Tool_plugin.py`\\\n**Download**: https://www.racedepartment.com/downloads/telemetry-tool-for-ac.37231/\\\n**Author**: IkoRein, version 1.3\\\n**License**: Third-party, check RaceDepartment terms\\\n\\\n### Installation & Configuration\\\n1. Extract to `<AC>/apps/python/Telemetry_Tool_plugin/`\\\n2. Edit `<AC>/apps/python/Telemetry_Tool_plugin/config.ini`:\\\n   ```ini\\\n   [telemetry_tool_plugin]\\\n   to_ip = 127.0.0.1\\\n   to_port = 10101\\\n   ```\\\n3. Enable in `python.ini` (in same directory): `[TELEMETRY_TOOL_PLUGIN] ACTIVE=1`\\\n4. Restart AC\\\n\\\n### Packet Structure (binary, little-endian)\\\n\\\n**Outer wrapper**: `byte packet_type` + `[driver_data × cars_count]`\\\n- `packet_type = 22` → telemetry data\\\n- `packet_type = 23` → version info (sent every ~100 frames)\\\n\\\n**Per-driver structure (357 bytes each)** — from `getBytesForTelemetry(False)`:\\\n\\\n| Offset | Type | Field | Source API | Notes |\\\n|--------|------|-------|-----------|-------|\\\n| 0 | uint32 | driver_id | constructor | Car index |\\\n| 4 | uint32 | lap_time | ac.getCarState(CS.LapTime) | Current lap ms |\\\n| 8 | uint32 | last_lap | ac.getCarState(CS.LastLap) | Last lap ms |\\\n| 12 | uint32 | best_lap | ac.getCarState(CS.BestLap) | Best lap ms |\\\n| 16 | byte | current_splits_num | len(ac.getCurrentSplits()) | |\\\n| 17 | uint32×3 | cur_split[1-3] | ac.getCurrentSplits() | Sector times ms |\\\n| 29 | uint32×3 | last_split[1-3] | ac.getLastSplits() | Last lap sectors ms |\\\n| 41 | float | steer | ac.getCarState(CS.Steer) | -2π…2π |\\\n| 45 | float | throttle | ac.getCarState(CS.Gas) | 0–1 |\\\n| 49 | float | brake | ac.getCarState(CS.Brake) | 0–1 |\\\n| 53 | float | clutch | ac.getCarState(CS.Clutch) | 0–1 |\\\n| 57 | int16 | gear | ac.getCarState(CS.Gear) | |\\\n| 59 | float | engineRPM | ac.getCarState(CS.RPM) | |\\\n| 63 | float | speed | 3.6 × CS.SpeedMS | km/h |\\\n| 67 | int16 | track_position | ac.getCarRealTimeLeaderboardPosition() | |\\\n| 69 | int16 | leaderboard_position | ac.getCarLeaderboardPosition() | |\\\n| 71 | int16 | lap_count | ac.getCarState(CS.LapCount) | |\\\n| 73 | float | x | CS.WorldPosition | World pos X |\\\n| 77 | float | y | CS.WorldPosition | World pos Y |\\\n| 81 | float | z | CS.WorldPosition | World pos Z |\\\n| 85 | double | spline_position | CS.NormalizedSplinePosition | 0–1 |\\\n| 93 | float | test_float | hardcoded 666.666 | Validation |\\\n| 97 | char[33] | driver_name | ac.getDriverName() | Null-padded |\\\n| 130 | char[4] | nationality | ac.getDriverNationCode() | |\\\n| 134 | char[33] | car_name | ac.getCarName() | Null-padded |\\\n| 167 | char[10] | tyre_name | ac.getCarTyreCompound() | |\\\n| 177 | uint32 | test_int | hardcoded 666 | Validation |\\\n| 181 | byte | is_connected | ac.isConnected() | |\\\n| 182 | byte | is_in_pit | ac.isCarInPit() | |\\\n| 183 | byte | is_car_in_pitlane | ac.isCarInPitlane() | |\\\n| 184 | byte | valid | CS.LapInvalidated | |\\\n| 185 | float | acc_vertical | CS.AccG[0] | Vertical G |\\\n| 189 | float | acc_horizontal | CS.AccG[1] | Horizontal G |\\\n| 193 | float | acc_frontal | CS.AccG[2] | Longitudinal G |\\\n| 196 | byte | is_engine_limiter_on | CS.IsEngineLimiterOn | |\\\n| 197 | byte | is_drs_available | CS.DrsAvailable | |\\\n| 198 | byte | is_drs_enabled | CS.DrsEnabled | |\\\n| 199 | byte | is_race_finished | CS.RaceFinished | |\\\n| 200 | byte | ptp_status | CS.P2PStatus | Push-to-pass |\\\n| 201 | uint32 | ptp_activations | CS.P2PActivations | |\\\n| 205 | float | ers_max_j | CS.ERSMaxJ | ERS capacity |\\\n| 209 | float | ers_current_kj | CS.ERSCurrentKJ | ERS charge |\\\n| 213 | float×4 | tyre_dirty_level | CS.TyreDirtyLevel | 0–1 per wheel |\\\n| 229 | float | turbo_boost | CS.TurboBoost | |\\\n| 233 | float×2 | ride_height | CS.RideHeight | Front/rear |\\\n| 241 | float×4 | current_tyres_core_temp | CS.CurrentTyresCoreTemp | Per wheel |\\\n| 257 | float×4 | tyres_slip_ratio | CS.SlipRatio | Per wheel |\\\n| 273 | float×4 | tyres_slip_angle | CS.SlipAngle | Per wheel |\\\n| 289 | float×4 | nd_slip | CS.NdSlip | Normalized slip per wheel |\\\n| 305 | float×4 | wheel_slip | CS.TyreSlip | Per wheel |\\\n| 321 | float×3 | world_speed | CS.Velocity | World velocity |\\\n| 333 | float×3 | local_speed | CS.LocalVelocity | Local velocity |\\\n| 345 | int32 | valid (trailer) | CS.LapInvalidated | Validation check |\\\n| 349 | double | test_float (trailer) | hardcoded 666.666 | Validation (as double) |\\\n\\\nTotal per driver: 357 bytes.\\\n\\\n---\\\n\\\n## 7. Repositories & Deployment\\\n\\\n### Repo 1\"],[0,\": `r\"]],\"start1\":9316,\"start2\":9316,\"length1\":5500,\"length2\":11476},{\"diffs\":[[0,\"e 1.\"],[-1,\"1 — needs KSUDP registration protocol + data dump upgrade** |\\\n\\\n#### Planned Cargo Workspace Layout (Phase 1.2\"],[1,\"0 — KSUDP 3-phase handshake rewrite needed** |\\\n\\\n#### Planned Cargo Workspace Layout (Phase 1.1\"],[0,\"+)\\\n\\\n\"]],\"start1\":21080,\"start2\":21080,\"length1\":117,\"length2\":102},{\"diffs\":[[0,\"er)\\\n\"],[-1,\"│   ├── parser-ac/                (AC JSON :5005 → generic model)\\\n\"],[0,\"│   \"]],\"start1\":21387,\"start2\":21387,\"length1\":74,\"length2\":8},{\"diffs\":[[0,\"    \"],[-1,\"├── feed_10101_*.bin\\\n        └── feed_5005_*.{json,csv}\\\n```\\\n\\\n---\\\n\\\n## 6. Data Format — JSON Schema (Lua Plugin, Port 5005)\\\n\\\n```json\\\n{\\\n  \\\"timestamp\\\": 1716123456,\\\n  \\\"speed_kmh\\\": 245.3,\\\n  \\\"rpm\\\": 8750,\\\n  \\\"gear\\\": 6,\\\n  \\\"throttle\\\": 0.850,\\\n  \\\"brake\\\": 0.000,\\\n  \\\"steering\\\": 0.120,\\\n  \\\"g_forces\\\": { \\\"x\\\": 0.500, \\\"y\\\": -0.200, \\\"z\\\": 1.000 },\\\n  \\\"wheel_slip\\\": [0.100, 0.100, 0.000, 0.000],\\\n  \\\"suspension_travel\\\": [0.150, 0.140, 0.160, 0.150],\\\n  \\\"lap_time\\\": 85.234,\\\n  \\\"lap_count\\\": 5\\\n}\\\n```\\\n\\\n---\\\n\\\n## 7. Implementation Phases\\\n\\\n### Phase 1: Core Infrastructure\\\n\\\n- **1.0 Rust listener upgrade — IMMEDIATE**\\\n  - [x] Multi-port UDP binding (9996, 10101, 5005) — **DONE but 9996 needs rewrite**\\\n  - [x] Per-feed status reporting with health indicators\\\n  - [x] Data dump to per-feed files (`./data/<session>/feed_<port>_*.{bin,json}`)\\\n  - [x] Startup banner showing all feed ports and bind status\\\n  - [ ] **Rewrite port 9996 handler** — change from bind-and-listen to registration protocol\\\n  - [ ] Reverse-engineer KSUDP handshake packet format\\\n  - [ ] Configurable via env vars or config file\\\n\\\n- **1.1 Binary parsers — START NOW**\\\n  - [ ] `parser-ksudp` for port 9996 (AC built-in, community-reverse-engineered structure)\\\n  - [ ] `parser-telemetry-tool` for port 10101 (struct layout documented in Python source)\\\n  - [ ] Map both to generic `TelemetryFrame` model\\\n\\\n- **1.2 AC Lua Plugin — DIAGNOSING**\\\n  - [x] Fix LuaSocket require pattern\\\n  - [x] Switch to app window variant\\\n  - [x] Add icon.png\\\n  - [x] Delete extension/lua duplicate\\\n  - [x] Add `ac.log()` diagnostic checkpoints around `require(\\\"socket\\\")` and `socket.udp()`\\\n  - [ ] **Test in AC** — restart AC, open app, check CSP log for diagnostic messages\\\n  - [ ] **If LuaSocket unavailable**: investigate CSP's native networking API (`ac.connectUDP()`, `web` module, etc.)\\\n  - [ ] **Commit changes to git**\\\n  - [ ] **Add fields**: world position, spline position, fuel (trivial, available from CSP API)\\\n\\\n- **1.3 Python Telemetry Tool — WORKING**\\\n  - [x] Added `[TELEMETRY_TOOL_PLUGIN] ACTIVE=1` to `python.ini`\\\n  - [x] **Verified** — 4.55 MB of valid binary data captured on port 10101\\\n  - [ ] Confirm data includes wheel slip, tyre temps, all cars (parse the binary dump)\\\n\\\n- **1.4 Workspace refactor**\\\n  - [ ] Split into Cargo workspace with `core`, `receiver`, parsers\\\n  - [ ] SQLite database with migrations\\\n  - [ ] Basic REST API endpoints\\\n\\\n- **1.5 DR2.0 + PCARS parsers** (deferred until AC feeds work)\\\n\\\n### Phase 2: Data Analysis & Storage\\\n- Plausibility engine: cross-reference feeds\\\n- Derived metrics: curvature (G_LAT / v²), slip ratios, derivatives\\\n- Track mapping from world position data\\\n- Reference lap system\\\n\\\n### Phase 3: Web Visualization\\\n- Real-time telemetry dashboard\\\n- Track map with racing line (world position)\\\n- Lap comparison overlays\\\n\\\n### Phase 4: AI Coaching with GLM-5.1\\\n- Basic coaching from built-in feeds (braking, lap consistency, racing line)\\\n- Advanced coaching from Lua plugin (traction, tyre strategy, damage)\\\n\\\n---\\\n\\\n## 8. Development Tools & References\\\n\\\n| Component | IDE | Project Path |\\\n|---|---|---|\\\n| `ac-telemetry-plugin` | IntelliJ IDEA + EmmyLua | `~/IdeaProjects/ac-telemetry-plugin` |\\\n| `rusty-telemetry` | RustRover | `~/RustroverProjects/rusty-telemetry` |\\\n\\\n### Key References\\\n\\\n| Resource | URL | Description |\\\n|---|---|---|\\\n| Custom Shaders Patch (CSP) | https://github.com/ac-custom-shaders-patch | CSP Lua source code, examples, and SDK |\\\n\\\n---\\\n\\\n## 9–12. [Unchanged — Rally, References, Metrics, Game Priority]\\\n\\\n*(See previous versions of this note for unchanged sections 9–12\"],[1,\"└── feed_10101_*.bin\\\n```\\\n\\\n### Deployment Paths\\\n\\\n| Path | Purpose |\\\n|---|---|\\\n| `<AC>/apps/python/Telemetry_Tool_plugin/` | **Third-party plugin** — config.ini for IP/port |\\\n| `~/RustroverProjects/rusty-telemetry/` | **Rust listener** — multi-port receiver |\\\n| `~/IdeaProjects/ac-telemetry-plugin/` | **ARCHIVED** — abandoned Lua plugin repo |\\\n\\\n`<AC>` = `/home/jan/.steam/debian-installation/steamapps/common/assettocorsa`\\\n`<Proton Docs>` = `~/.steam/.../compatdata/244210/pfx/drive_c/users/steamuser/Documents/Assetto Corsa`\\\n\\\n---\\\n\\\n## 8. Implementation Phases\\\n\\\n### Phase 1: Core Infrastructure\\\n\\\n- **1.0 Rewrite KSUDP to proper 3-phase handshake protocol** — IMMEDIATE\\\n  - [x] Multi-port UDP binding (9996, 10101) — **DONE but 9996 needs rewrite**\\\n  - [x] Per-feed status reporting with health indicators\\\n  - [x] Data dump to per-feed files (`./data/<session>/feed_<port>_*.bin`)\\\n  - [x] Startup banner showing all feed ports and bind status\\\n  - [ ] **Rewrite port 9996 handler** — implement 3-phase handshake from Google Doc\\\n  - [ ] Phase 1: Send `{1, 1, 0}` HANDSHAKE (12 bytes) to AC :9996\\\n  - [ ] Phase 2: Receive 408-byte HandshakerResponse, parse UTF-16LE fields\\\n  - [ ] Phase 3: Send `{1, 1, 1}` SUBSCRIBE_UPDATE (12 bytes) to AC :9996\\\n  - [ ] Receive continuous RTCarInfo structs (~20 Hz)\\\n  - [ ] Configurable via env vars or config file\\\n\\\n- **1.1 Binary parsers — START NOW**\\\n  - [ ] `parser-ksudp` for port 9996 (RTCarInfo struct from official Google Doc)\\\n  - [ ] `parser-telemetry-tool` for port 10101 (struct layout documented in Python source)\\\n  - [ ] Map both to generic `TelemetryFrame` model\\\n\\\n- **1.2 REMOVED** (was Lua plugin — abandoned, see Section 4)\\\n\\\n- **1.3 Telemetry Tool — WORKING, OPTIONAL**\\\n  - [x] Added `[TELEMETRY_TOOL_PLUGIN] ACTIVE=1` to `python.ini`\\\n  - [x] **Verified** — 6.3 MB of valid binary data captured on port 10101\\\n  - [ ] Confirm data includes wheel slip, tyre temps, all cars (parse the binary dump)\\\n  - [ ] Make Telemetry Tool feed optional in rusty-telemetry (enabled only if port 10101 is reachable)\\\n\\\n- **1.4 Workspace refactor**\\\n  - [ ] Split into Cargo workspace with `core`, `receiver`, parsers\\\n  - [ ] SQLite database with migrations\\\n  - [ ] Basic REST API endpoints\\\n\\\n- **1.5 DR2.0 + PCARS parsers** (deferred until AC feeds work)\\\n\\\n### Phase 2: Data Analysis & Storage\\\n- Plausibility engine: cross-reference feeds\\\n- Derived metrics: curvature (G_LAT / v²), slip ratios, derivatives\\\n- Track mapping from world position data\\\n- Reference lap system\\\n\\\n### Phase 3: Web Visualization\\\n- Real-time telemetry dashboard\\\n- Track map with racing line (world position)\\\n- Lap comparison overlays\\\n\\\n### Phase 4: AI Coaching with GLM-5.1\\\n- Basic coaching from KSUDP feed (braking, lap consistency, racing line)\\\n- Advanced coaching from Telemetry Tool feed (traction, tyre strategy, multi-car)\\\n\\\n---\\\n\\\n## 9. Development Tools & References\\\n\\\n| Component | IDE | Project Path |\\\n|---|---|---|\\\n| `rusty-telemetry` | RustRover | `~/RustroverProjects/rusty-telemetry` |\\\n| `ac-telemetry-plugin` | IntelliJ IDEA + EmmyLua | `~/IdeaProjects/ac-telemetry-plugin` (ARCHIVED) |\\\n\\\n### Key References\\\n\\\n| Resource | URL | Description |\\\n|---|---|---|\\\n| AC Remote Telemetry Protocol | https://docs.google.com/document/d/1KfkZiIluXZ6mMhLWfDX1qAGbvhGRC3ZUzjVIt5FQpp4/pub | Official AC KSUDP handshake and telemetry protocol |\\\n| Telemetry Tool Plugin | https://www.racedepartment.com/downloads/telemetry-tool-for-ac.37231/ | Third-party Python plugin for AC |\\\n| Custom Shaders Patch (CSP) | https://github.com/ac-custom-shaders-patch | CSP Lua source code, examples, and SDK |\\\n\\\n---\\\n\\\n## 10–12. [Unchanged — Rally, References, Metrics, Game Priority]\\\n\\\n*(See previous versions of this note for sections on Rally games, additional references, metrics definitions, and game priority rankings\"],[0,\")*\\\n\\\n\"]],\"start1\":22121,\"start2\":22121,\"length1\":3563,\"length2\":3799},{\"diffs\":[[0,\"DP\\\",\"],[-1,\" \\\"IMGUI_LUA_ac-telemetry\\\",\"],[0,\" Pyt\"]],\"start1\":26182,\"start2\":26182,\"length1\":34,\"length2\":8},{\"diffs\":[[0,\" log\"],[-1,\" (check impl. field)\"],[0,\"\\\n└──\"]],\"start1\":26245,\"start2\":26245,\"length1\":28,\"length2\":8},{\"diffs\":[[0,\"96 (\"],[-1,\"AC Built-in) — Registration\"],[1,\"KSUDP) — 3-Phase Handshake\"],[0,\" Pro\"]],\"start1\":26503,\"start2\":26503,\"length1\":35,\"length2\":34},{\"diffs\":[[0,\"r** \"],[-1,\"by sending a handshake packet to `:9996`\\\n- AC then streams telemetry data back to the registered address\"],[1,\"via 3-phase handshake:\\\n  1. Send HANDSHAKE `{1, 1, 0}` (12 bytes) to `:9996`\\\n  2. Receive 408-byte HandshakerResponse (UTF-16LE car/driver/track + identifier=4242)\\\n  3. Send SUBSCRIBE_UPDATE `{1, 1, 1}` (12 bytes) to `:9996`\\\n  4. Receive continuous RTCarInfo structs (~20 Hz)\"],[0,\"\\\n- N\"]],\"start1\":26681,\"start2\":26681,\"length1\":112,\"length2\":283},{\"diffs\":[[0,\"`\\\n- \"],[-1,\"The handshake packet format needs reverse-engineering\"],[1,\"**Reference**: https://docs.google.com/document/d/1KfkZiIluXZ6mMhLWfDX1qAGbvhGRC3ZUzjVIt5FQpp4/pub\"],[0,\"\\\n\\\n##\"]],\"start1\":27125,\"start2\":27125,\"length1\":61,\"length2\":106},{\"diffs\":[[0,\"ng: \"],[-1,\"4.55 MB captured in 2026-06-03 session\\\n\\\n### Port 5005 (Our Lua Plugin) — DIAGNOSING ❌\\\n- Config: `<AC>/apps/lua/ac-telemetry-plugin/config.lua`\\\n- Bug fix: `require(\\\"socket.udp\\\")` → `require(\\\"socket\\\")` + `socket.udp()`\\\n- Diagnostic logging added: `ac.log()` checkpoints before/after socket init\\\n- CSP log shows `impl.: 0000000000000000` (null implementation) — script may have failed to init\\\n- Possible causes: LuaSocket unavailable in CSP sandbox, or app not activated in-game (LAZY=FULL)\\\n- Deployed to `apps/lua/` only (extension copy deleted)\\\n- Requires AC restart + enabling the app in the in-game app launcher\\\n\\\n### Deployment Paths\\\n\\\n| Path | Purpose |\\\n|---|---|\\\n| `~/IdeaProjects/ac-telemetry-plugin/` | **Source of truth** — git repo, edit here |\\\n| `<AC>/apps/lua/ac-telemetry-plugin/` | **Deployed app** — CSP loads from here |\\\n| `<AC>/apps/python/Telemetry_Tool_plugin/` | **Third-party plugin** — config.ini for IP/port |\\\n| `~/RustroverProjects/rusty-telemetry/` | **Rust listener** — multi-port receiver |\\\n\\\n`<AC>` = `/home/jan/.steam/debian-installation/steamapps/common/assettocorsa`\\\n`<Proton Docs>` = `~/.steam/.../compatdata/244210/pfx/drive_c/users/steamuser/Documents/Assetto Corsa`\\\n\\\n---\\\n\\\n## 14. Session Diagnostics — 2026-06-03\\\n\\\n### Session Data Captured\\\n\\\n| Port | Feed | File | Size | Status |\\\n|------|------|------|------|--------|\\\n| 9996 | KSUDP | `feed_9996_ksudp.bin` | 0 bytes | ❌ AC failed to bind (rusty-telemetry had the port) |\\\n| 10101 | Telemetry Tool | `feed_10101_telemetry_tool.bin` | 4.55 MB | ✅ Valid binary telemetry |\\\n| 5005 | AC Lua Plugin | `feed_5005_ac_plugin.json` | 0 bytes | ❌ Script failed to init |\\\n\\\n### Key Findings\\\n\\\n**Port 9996 — Port conflict:**\\\n- AC log: `RemoteTelemetryUDP :: creating socket on port 9996` → `ERROR BINDING LISTENER SOCKET`\\\n- Cause: `rusty-telemetry` bound to `:9996` before AC started → AC couldn't bind its listener socket\\\n- Resolution: rusty-telemetry must NOT bind to 9996; must use registration protocol instead (see Section 4)\\\n\\\n**Port 5005 — Script initialization failure:**\\\n- CSP log: `App is loaded: '...apps\\\\lua\\\\ac-telemetry-plugin', impl.: 0000000000000000, windows: 1`\\\n- `impl.: null` means the Lua script failed during initialization (module load time)\\\n- The `require(\\\"socket\\\")` call at module level is the likely crash point — if LuaSocket is unavailable in CSP's sandbox, the entire module fails to load\\\n- Diagnostic `ac.log()` checkpoints have been added to `src/telemetry.lua` to confirm on next AC restart\\\n- Also possible: app was never activated in the in-game app launcher (LAZY=FULL defers loading)\\\n\\\n**Port 10101 — Working correctly:**\\\n- `py_log.txt` confirms: \\\"Telemetry Tool plugin 1.3 — Sending data to 127.0.0.1:10101\\\"\\\n- Binary data decoded successfully: packet type 22, driver \\\"Jan-Peter von Hunnius\\\", car \\\"abarth500\\\"\\\n- Telemetry Tool Python plugin creates an unbound socket → no port conflict with rusty-telemetry\\\n\\\n### Startup Order for Next Session\\\n\\\n1. **Start AC first** — let it bind port 9996\\\n2. **Start rusty-telemetry after AC loads** — binds to 10101 and 5005 only\\\n3. For KSUDP: rusty-telemetry sends registration to AC's port 9996 (once handshake protocol is implemented)\\\n4. For port 5005: activate the \\\"AC Telemetry\\\" app in the in-game app launcher\\\n5. Check CSP logs for `ac.log()` diagnostic messages from the Lua plugin\\\n\\\n---\\\n\\\n*Last updated: 2026-06-03 — Session diagnostics, KSUDP registration protocol discovery, Lua plugin diagnosis*\\\n*Port 10101 confirmed working. Port 9996 needs registration protocol in rusty-telemetry. Port 5005 needs LuaSocket availability confirmed.*\\\n*Next: restart AC → check Lua diagnostic logs → implement KSUDP registration in rusty-telemetry\"],[1,\"6.3 MB captured in 2026-06-03 session\\\n- **Optional** — make rusty-telemetry listen only if port is reachable\\\n\\\n### Port 5005 (AC Lua Plugin) — ABANDONED ❌\\\n- CSP lacks compiled LuaSocket C core (`socket/core.dll` / `socket.core.so`)\\\n- `require(\\\"socket\\\")` → `socket.lua` → `require(\\\"socket.core\\\")` → FAILS\\\n- No workaround — CSP has no raw UDP socket API\\\n- Repo `ac-telemetry-plugin` should be archived\\\n- If more data needed: use AC's Python plugin system (same as Telemetry Tool)\\\n\\\n---\\\n\\\n## 14. Session Diagnostics\\\n\\\n### Session 2026-06-03 ~09:45 — Initial Capture\\\n\\\n| Port | Feed | File | Size | Status |\\\n|------|------|------|------|--------|\\\n| 9996 | KSUDP | `feed_9996_ksudp.bin` | 0 bytes | ❌ AC failed to bind (rusty-telemetry had the port) |\\\n| 10101 | Telemetry Tool | `feed_10101_telemetry_tool.bin` | 4.55 MB | ✅ Valid binary telemetry |\\\n| 5005 | AC Lua Plugin | `feed_5005_ac_plugin.json` | 0 bytes | ❌ Script failed to init |\\\n\\\n**Key findings:**\\\n- Port 9996 port conflict: rusty-telemetry bound first → AC couldn't start RemoteTelemetryUDP\\\n- Port 5005: CSP `impl.: null` → Lua script failed at `require(\\\"socket\\\")`\\\n- Port 10101: Confirmed working, Python plugin sends valid data\\\n\\\n### Session 2026-06-03 19:32 — Investigation Results\\\n\\\n| Port | Feed | File | Size | Status |\\\n|------|------|------|------|--------|\\\n| 9996 | KSUDP | `feed_9996_ksudp.bin` | 5,768 bytes | ⚠️ Partial — 14 × 408-byte handshake responses only, no continuous stream |\\\n| 10101 | Telemetry Tool | `feed_10101_telemetry_tool.bin` | 6,335,955 bytes | ✅ Valid binary telemetry |\\\n| 5005 | AC Lua Plugin | `feed_5005_ac_plugin.json` | 0 bytes | ❌ Abandoned — CSP lacks socket.core |\\\n\\\n**KSUDP Analysis**:\\\n- AC successfully bound to port 9996 (no error in log)\\\n- rusty-telemetry sent 4-byte handshake (wrong, should be 12 bytes)\\\n- AC responded with valid 408-byte HandshakerResponse per attempt (identifier=4242 ✓, version=1 ✓)\\\n- 14 responses in 3 minutes = 1 per 10-second handshake interval\\\n- Never sent SUBSCRIBE_UPDATE → AC never started continuous streaming\\\n- Fix: implement proper 3-phase protocol (see Section 5)\\\n\\\n**Lua Plugin Root Cause**:\\\n- `require(\\\"socket\\\")` → loads `socket.lua` → `require(\\\"socket.core\\\")` → FAILS (no C library)\\\n- CSP's `extension/internal/lua-shared/socket.lua` exists but `socket/core.dll` does not\\\n- `type(ac)` is `\\\"userdata\\\"` not `\\\"table\\\"`, so diagnostic `ac.log()` calls were bypassed to `print()` which goes nowhere\\\n- Decision: abandoned, use Python plugins if more data needed\\\n\\\n### Startup Order for Next Session\\\n\\\n1. **Start AC first** — let it bind port 9996\\\n2. **Start rusty-telemetry after AC loads** — binds to 10101 only\\\n3. For KSUDP: rusty-telemetry performs 3-phase handshake to AC's port 9996\\\n4. Make Telemetry Tool feed optional (only listen if port 10101 is reachable)\\\n\\\n---\\\n\\\n*Last updated: 2026-06-03 21:12 — Dual-source architecture, KSUDP 3-phase handshake documented, Lua plugin abandoned*\\\n*Port 10101 confirmed working (6.3 MB). Port 9996 needs 3-phase handshake fix. Port 5005 abandoned (CSP lacks socket.core).*\\\n*Next: implement KSUDP 3-phase handshake in rusty-telemetry, parse Telemetry Tool binary data\"],[0,\"*\"]],\"start1\":27531,\"start2\":27531,\"length1\":3676,\"length2\":3130}]"
metadata_diff: {"new":{},"deleted":[]}
encryption_cipher_text: 
encryption_applied: 0
updated_time: 2026-06-03T19:17:57.277Z
created_time: 2026-06-03T19:17:57.277Z
type_: 13