# 📡 ACC Broadcasting API 完整规范 ## 一、概述 ACC 有两种遥测数据通道: | 通道 | 协议 | 用途 | |---|---|---| | **Broadcasting API** | UDP(双向) | 广播/观战、实时计时、多车数据 | | **Shared Memory** | 共享内存(Windows) | 仪表盘、运动平台、FFB(仅玩家车辆) | SDK 路径:`steamapps/common/Assetto Corsa Competizione/sdk/broadcasting/` --- ## 二、启用 Broadcasting ### 配置文件 编辑 `文档/Assetto Corsa Competizione/Config/broadcasting.json`(**必须 UTF-16LE BOM 编码**): ```json { "updListenerPort": 9000, "connectionPassword": "asd", "commandPassword": "" } ``` | 字段 | 说明 | |---|---| | `updListenerPort` | ACC 监听的 UDP 端口(默认 9000) | | `connectionPassword` | 客户端连接密码 | | `commandPassword` | 控制命令密码(留空则所有已连接客户端都可发送命令) | ### 测试客户端 运行 `sdk/broadcasting/Testclient/ksBroadcastingTestClient.exe`,输入端口和密码后点击 Connect 即可验证。 --- ## 三、协议规范 ### 基本规则 - **字节序**:Little Endian - **协议版本**:`BROADCASTING_PROTOCOL_VERSION = 2` - **字符串编码**:UTF-8,前缀 `uint16` 长度字段 - **通信流程**:客户端 → ACC 发起注册 → ACC 回传数据流 ### 数据序列化规则 | 类型 | 字节数 | |---|---| | `byte` / `int8` | 1 | | `uint16` / `int16` | 2 | | `int32` / `uint32` | 4 | | `float32` | 4 | | `string` | 2(长度)+ N(UTF-8 内容) | ### Lap 结构体(通用圈速格式) ``` int32 LaptimeMS // 圈速(ms),Int32.MaxValue = 无圈速 uint16 CarIndex // 车辆索引 uint16 DriverIndex // 车手索引 uint8 SplitCount // 分段数(通常 3) int32[] Splits // 各分段成绩(ms),Int32.MaxValue = 无效 uint8 IsInvalid // 0=有效, 1=无效 uint8 IsValidForBest // 0=不可作最佳圈, 1=可作最佳圈 uint8 IsOutLap // 0=非出场圈, 1=出场圈 uint8 IsInLap // 0=非回场圈, 1=回场圈 ``` --- ## 四、出站消息(客户端 → ACC) 所有消息首字节为消息类型。 | 类型 ID | 消息名 | 说明 | |---|---|---| | `1` | **REGISTER_COMMAND_APPLICATION** | 注册连接(必须最先发送) | | `9` | UNREGISTER_COMMAND_APPLICATION | 取消注册 | | `10` | **REQUEST_ENTRY_LIST** | 请求参赛者列表 | | `11` | **REQUEST_TRACK_DATA** | 请求赛道数据 | | `49` | CHANGE_HUD_PAGE | 切换 HUD 页面 | | `50` | **CHANGE_FOCUS** | 切换焦点车辆/摄像机 | | `51` | **INSTANT_REPLAY_REQUEST** | 请求即时回放 | | `52` | PLAY_MANUAL_REPLAY_HIGHLIGHT | (预留) | | `60` | SAVE_MANUAL_REPLAY_HIGHLIGHT | (预留) | ### 1. REGISTER_COMMAND_APPLICATION(ID=1) ``` byte 1 // 消息类型 byte BROADCASTING_PROTOCOL_VERSION // = 2 string DisplayName // 显示名称 string ConnectionPassword // 连接密码(匹配 broadcasting.json) int32 RealtimeUpdateInterval // 实时更新间隔(ms),建议 250 string CommandPassword // 命令密码 ``` ### 2. CHANGE_FOCUS(ID=50) ``` byte 50 // 消息类型 int32 ConnectionId // 连接 ID uint8 HasCarChange // 0=不变, 1=切换车辆 uint16 CarIndex // 目标车辆索引(仅 HasCarChange=1) uint8 HasCameraChange // 0=不变, 1=切换摄像机 string CameraSet // 摄像机集合名 string Camera // 摄像机名 ``` ### 3. INSTANT_REPLAY_REQUEST(ID=51) ``` byte 51 // 消息类型 int32 ConnectionId float32 StartSessionTime // 回放起点(会话时间,ms) float32 DurationMS // 回放时长(ms) int32 InitialFocusedCar // 初始焦点车辆(-1 = 保持当前) string InitialCameraSet string InitialCamera ``` ### 4. REQUEST_ENTRY_LIST(ID=10)/ REQUEST_TRACK_DATA(ID=11) ``` byte 10 或 11 int32 ConnectionId ``` --- ## 五、入站消息(ACC → 客户端) | 类型 ID | 消息名 | 说明 | |---|---|---| | `1` | **REGISTRATION_RESULT** | 注册结果 | | `2` | **REALTIME_UPDATE** | 会话实时信息(全局) | | `3` | **REALTIME_CAR_UPDATE** | 车辆实时信息(每车) | | `4` | **ENTRY_LIST** | 参赛者索引列表 | | `5` | **TRACK_DATA** | 赛道和摄像机数据 | | `6` | **ENTRY_LIST_CAR** | 单辆车详细信息 | | `7` | **BROADCASTING_EVENT** | 广播事件 | ### 1. REGISTRATION_RESULT(ID=1) ``` byte 1 int32 ConnectionId // 分配的连接 ID(后续请求必须携带) uint8 ConnectionSuccess // 0=失败, 1=成功 uint8 IsReadOnly // 0=可读写(即拥有 commandPassword) string ErrorMessage // 错误信息 ``` 成功后应立即发送 `REQUEST_ENTRY_LIST` 和 `REQUEST_TRACK_DATA`。 --- ### 2. REALTIME_UPDATE(ID=2) ``` byte 2 uint16 EventIndex // 赛事索引 uint16 SessionIndex // 会话索引 uint8 SessionType // 见下表 uint8 Phase // 会话阶段(见下表) float32 SessionTime // 会话已过时间 (ms) float32 SessionEndTime // 会话剩余时间 (ms) int32 FocusedCarIndex // 当前焦点车辆 string ActiveCameraSet // 当前摄像机集合 string ActiveCamera // 当前摄像机 string CurrentHUDPage // 当前 HUD 页面 uint8 IsReplayPlaying // 是否回放中 // 以下仅 IsReplayPlaying=1 时存在: float32 ReplaySessionTime // 回放中的会话时间 float32 ReplayRemainingTime // 回放剩余时间 int32 ReplayFocusedCar // 始终存在: uint16 TimeOfDay // 时间 hh*100+mm uint8 AmbientTemp // 环境温度 (°C) uint8 TrackTemp // 赛道温度 (°C) uint8 Clouds // 云量(÷10,即 0.0~1.0) uint8 RainLevel // 降雨量(÷10) uint8 Wetness // 路面湿度(÷10) Lap BestSessionLap // 当前会话最佳圈速 ``` #### RaceSessionType 枚举 | 值 | 类型 | |---|---| | 0 | Practice(练习) | | 4 | Qualifying(排位赛) | | 9 | Superpole(超级杆位) | | 10 | Race(正赛) | | 11 | Hotlap(单圈冲刺) | | 12 | Hotstint(阶段冲刺) | | 13 | HotlapSuperpole | | 14 | Replay(回放) | #### SessionPhase 枚举 | 值 | 阶段 | |---|---| | 0 | NONE | | 1 | Starting | | 2 | PreFormation | | 3 | FormationLap(编队圈) | | 4 | PreSession | | 5 | **Session**(绿灯起跑后) | | 6 | SessionOver | | 7 | PostSession | | 8 | ResultUI | --- ### 3. REALTIME_CAR_UPDATE(ID=3) **最重要的数据包**—每辆车独立发送。 ``` byte 3 uint16 CarIndex // 车辆索引(对应 EntryList) uint16 DriverIndex // 当前车手索引 uint8 DriverCount // 该车车手总数 int8 Gear // -1=R, 0=N, 1~6... float32 WorldPosX // 世界坐标 X(始终为 0) float32 WorldPosY // 世界坐标 Y(始终为 0) float32 Yaw // 偏航角(始终为 0) uint8 CarLocation // 位置枚举(见下) uint16 Kmh // 速度 (km/h) uint16 Position // 排名(1-based) uint16 CupPosition // 组别排名(1-based) uint16 TrackPosition // 赛道位置序号(始终为 0) float32 SplinePosition // 赛道位置 0.0~1.0 uint16 Laps // 已完成圈数 int32 Delta // 与最佳圈速的实时差距 (ms) Lap BestSessionLap // 该车最佳圈速 Lap LastLap // 上一圈 Lap CurrentLap // 当前圈(时间持续更新,分段不填充) ``` #### CarLocation 枚举 | 值 | 位置 | |---|---| | 0 | NONE | | 1 | **Track**(赛道上) | | 2 | Pitlane(维修区通道) | | 3 | PitEntry(进站入口) | | 4 | PitExit(出站出口) | --- ### 4. ENTRY_LIST(ID=4)+ ENTRY_LIST_CAR(ID=6) **两步接收**:先收索引列表,再逐车收详情。 #### ENTRY_LIST 结构: ``` byte 4 int32 ConnectionId uint16 CarEntryCount uint16[] CarIndexes // 车辆索引数组 uint16 DriverEntryCount uint16[] DriverIndexes // 保留字段 ``` #### ENTRY_LIST_CAR 结构: ``` byte 6 uint16 CarIndex // 对应 ENTRY_LIST 中的索引 uint8 CarModelType // 车型(见下) string TeamName // 车队名 int32 RaceNumber // 车号 string TeamCarName // 车型名称 string DisplayName // 显示名 uint8 CupCategory // 组别:0=Overall/Pro, 1=ProAm, 2=Am, 3=Silver, 4=National uint8 CurrentDriverId // 当前车手 ID uint16 Nationality // 国籍(见附录) uint8 DriversOnCarCount // 该车手数量 // 每个车手: uint16 DriverIndex uint8 HasDriverInfo // 0/1 string FirstName string LastName string Nickname string ShortName // 缩写(如 "VER") uint8 Category // 3=Platinum, 2=Gold, 1=Silver, 0=Bronze uint16 Nationality ``` #### CarModelType(部分) | 值 | 车型 | |---|---| | 1 | Mercedes-AMG GT3 | | 2 | Ferrari 488 GT3 | | 15 | Lexus RC F GT3 | | 16 | Lamborghini Huracán GT3 | | 19 | Audi R8 LMS GT3 | | 20 | Aston Martin Vantage GT3 | | 23 | Porsche 911 GT3 R | --- ### 5. TRACK_DATA(ID=5) ``` byte 5 int32 ConnectionId string TrackName // 赛道名 int32 TrackId // 赛道 ID(见下) int32 TrackMeters // 赛道长度 (m) uint8 CameraSetCount // 每个摄像机集合: string CameraSetName uint8 CameraCount string[] CameraNames // 逐个摄像机名 uint8 HUDPageCount string[] HUDPageNames ``` #### TrackId(部分) | ID | 赛道 | |---|---| | 1 | Brands Hatch | | 2 | Spa-Francorchamps | | 3 | Monza | | 4 | Misano | | 5 | Paul Ricard | | 6 | Silverstone | | 7 | Hungaroring | | 8 | Nürburgring | | 9 | Barcelona | | 10 | Zolder | | 11 | Zandvoort | | 13 | Mount Panorama (Bathurst) | | 14 | Laguna Seca | | 15 | Suzuka | --- ### 6. BROADCASTING_EVENT(ID=7) ``` byte 7 uint8 Type // 事件类型 string Msg // 事件消息(常为圈速字符串) int32 TimeMs // 自连接建立以来的时间 (ms) int32 CarId // 相关车辆 ID ``` | 事件 Type | 说明 | |---|---| | 5 | LapCompleted(完成一圈) | | 6 | BestSessionLap(全场最佳圈) | | 7 | BestPersonalLap(个人最佳圈) | --- ## 六、Shared Memory API(补充) 用于本地仪表盘/运动平台,仅含玩家车辆数据。 ### 三个内存页 | 页 | 大小 | 更新频率 | 内容 | |---|---|---|---| | **SPageFilePhysics** | ~800 B | 物理步进 | 速度、油门、刹车、G力、胎温等 | | **SPageFileGraphic** | ~852 B | 图形帧率 | 圈速、排名、旗语、天气、油耗等 | | **SPageFileStatic** | ~688 B | 固定 | 车辆名、赛道、最大RPM等 | ### Physics 关键字段 | 字段 | 类型 | 说明 | |---|---|---| | `gas` | float | 油门 0.0~1.0 | | `brake` | float | 刹车 0.0~1.0 | | `gear` | int | 档位 | | `rpm` | int | 引擎转速 | | `speedKmh` | float | 速度 | | `steerAngle` | float | 转向角 | | `wheelPressure[4]` | float | 胎压 | | `tyreCoreTemp[4]` | float | 轮胎内核温度 | | `brakeTemp[4]` | float | 刹车温度 | | `carDamage` | struct | 车辆损伤 | | `fuel` | float | 油量 (L) | | `abs` | float | ABS 作用量 | | `tc` | float | TC 作用量 | | `localVelocity` | Vector3f | 局部速度 | | `localAngularVel` | Vector3f | 局部角速度 | | `gForce` | Vector3f | G力 | ### Graphics 关键字段 | 字段 | 类型 | 说明 | |---|---|---| | `status` | enum | AC_OFF=0, AC_LIVE=1 等 | | `sessionType` | enum | 同 Broadcasting | | `currentTime` | int | 当前圈速 (ms) | | `lastTime` | int | 上一圈 (ms) | | `bestTime` | int | 最佳圈 (ms) | | `completedLaps` | int | 已完成圈数 | | `position` | int | 排名 | | `normalizedCarPosition` | float | 赛道位置 0.0~1.0 | | `isInPit` | bool | 是否在维修区 | | `flag` | enum | 旗语 | | `fuelPerLap` | float | 每圈油耗 | | `gapAhead` | int | 与前车差距 (ms) | | `gapBehind` | int | 与后车差距 (ms) | | `trackStatus` | string | Green/Fast/Optimum/Greasy/Damp/Wet/Flooded | | `rainIntensity` | enum | 降雨强度 | | `globalYellow` | bool | 黄旗 | --- ## 七、关键注意事项 1. **WorldPosX/Y 和 Yaw**:Broadcasting API 中始终为 0(出于反作弊考虑),需要全车坐标请用 Shared Memory 2. **EntryList**:在首次连接时发送一次,新会话开始时不自动重发 3. **TrackData**:同样只在连接时发送一次,新会话不重发 4. **SplinePosition**:0.0~1.0 表示车辆在赛道上的归一化位置(维修区也有效) 5. **Lap.Splits**:Int32.MaxValue 表示无效分段,超出分段数返回 0 6. **CarUpdate 中 Gear**:传输值 - 2 = 实际档位(传输 1 = -1/R,传输 2 = 0/N,传输 3 = 1 档...) 7. **broadcasting.json 编码**:必须是 UTF-16LE with BOM,否则 ACC 无法读取