435 lines
13 KiB
Markdown
435 lines
13 KiB
Markdown
|
||
# 📡 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 无法读取
|