renaudallard/assettocorsa_competizione_server
GitHub: renaudallard/assettocorsa_competizione_server
一款在纯 C99 中干净室实现的 Assetto Corsa Competizione 专用服务器,解决在非 Windows 平台运行官方比赛服务器的问题。
Stars: 0 | Forks: 0
accd
ACC 专用服务器,干净室重新实现
一个未修改的 Assetto Corsa Competizione 客户端可以连接并比赛,
在 Linux、OpenBSD 或 FreeBSD 上运行 —— 无需 Wine,无需 Kunos 二进制文件。
## 目录
- [状态](#status)
- [功能](#what-works)
- [已知限制](#known-limitations)
- [构建](#building)
- [运行](#running)
- [配置文件](#configuration-files)
- [启动服务器](#starting-the-server)
- [防火墙 / 端口](#firewall--ports)
- [从 ACC 客户端连接](#connecting-from-the-acc-client)
- [管理控制台](#admin-console)
- [后台服务](#background-service)
- [快速冒烟测试](#quick-smoke-test)
- [范围与法律立场](#scope--legal-posture)
- [仓库布局](#repository-layout)
- [贡献](#contributing)
- [许可证](#license)
## 状态
协议正确性已通过字节级对比验证,对照完整的 20 分钟 Kunos `accServer.exe` 捕获(101,897 个数据包,2 名玩家,P+Q+R 在 Misano)。所有 20 种服务器到客户端消息类型均与库存服务器的传输(TCP 与 UDP)、节奏和线格式匹配。
干净室协议规范位于
[`notebook-b/NOTEBOOK_B.md`](notebook-b/NOTEBOOK_B.md),并记录了每条线消息、字符串编码和状态转换。
## 功能
### 连接与握手
- **TCP 帧**:使用可变宽度的长度前缀;可变长度的欢迎尾部(`0x0b`)按段构建以逐字节匹配 Kunos 布局。拒绝(`0x0c`)代码 4–12 按线正确编码。
- **接受后欢迎序列** — `0x28` 大状态、`0x36` 排行榜、`0x37` 天气、`0x4e` 评分摘要按正确顺序和节奏发送。`0x28` 计划时间戳使用每连接时间基投影(`ts − server_now + client_ts + RTT/2`)。
- **快速重连**:通过 Steam ID 丢弃旧连接并重用车辆槽位,因此比赛状态、网格位置和罚时队列可在中途断开连接后保留。无论旧套接字是否仍存活,或在非活跃对等体清除后,均可工作。
- **比赛中重连控制** — `unsafeRejoin: 0` 拒绝新的握手;`/lockprep` 冻结准备阶段。返回的驾驶员始终绕过两者。
### 会话管理
- **P / Q / R 调度**:自动阶段转换、倒计时、超时保持以及决赛周重置。
- **基于位置的赛事开始** — 绿灯在领先者标准化赛道位置穿过配置范围内的随机触发点时触发,匹配 `FUN_14012f4a0` 且无时间回退。广播“比赛开始已初始化”。
- **从资格赛生成比赛网格** — 比赛网格来自最近一次资格赛的完成顺序。若无先前的 Q/P,则使用 `defaultGridPosition`。
- **排名排行榜/结果** — 实时计分,`0x36` 按排名广播,`0x3f` 为比赛开始时的网格,`0x3e` 为会话结束时的结果。
- **结果文件写入器** — `results/YYMMDD_HHMMSS_
.json` 匹配库存服务器模式。
### 遥测与中继
- **事件驱动的车辆中继** — 传入的 `0x1e` 车辆更新立即以 ~18 Hz 中继为 `0x39` 给所有其他对等体,并为每个对等体进行时间戳调整以实现死推算。
- **天气与游戏时钟** — 使用种子循环的确定性 sin/cos 天气;每 5 秒广播 `0x37`,携带由 `hourOfDay` × `timeMultiplier` 驱动的 `weekend_time_s`。
- **驾驶员切换** — 多驾驶员条目完整切换状态机(`&swap`、`0x47`/`0x48`/`0x4a`/`0x58`)。
- **实时赛道切换** — `/track ` 在会话中途切换赛道,并向每个客户端重新发送 `0x4b` 欢迎信息。
- **服务器监控协议** — 用于会话状态、车辆、连接、排行榜和实时更新的 protobuf 构建器。
### 管理员与调解
- **聊天/控制台命令**:`/admin`、`/next`、`/restart`、`/kick`、`/ban`、`/dq`、`/tp5`、`/tp15`、`/dt`、`/sg10..30`(全部包含碰撞变体)、`/clear`、`/clear_all`、`/ballast`、`/restrictor`、`/track`、`/connections`、`/hellban`、`/lockprep`、`/unlockprep`、`/manual entrylist`、`/manual start`、`/wt`、`/go`、`/report`。
- **处罚系统** — 每辆车队列、强制进站跟踪,DT/SG 的 3 圈截止倒计时(未完成自动 DQ,可通过 `allowAutoDQ: 0` 降级),遥测自动 DQ 检测。
- **持久化封禁** — `/ban` 写入 `cfg/banlist.txt` 并持久化;封禁的 Steam ID 在重连时被拒绝。
### 集成
- **局域网发现** — UDP 8999 广播响应,使同网络客户端可自动发现服务器。
- **公共大厅注册** — `registerToLobby: 1` 将服务器连接到 Kunos 后台大厅,以便在 ACC 浏览器中列出;设为 `0` 仅允许直接 IP 访问的私有服务器。
- **车辆列表** — `entrylist.json` 填充槽位;若启用 `forceEntryList: 1`,仅接受列表中的 Steam ID。
- **BoP** — 车辆重量/限制器更改时广播 `0x53`。
- **调试跟踪** — `-d` 标志或 `debug` 控制台命令启用每条消息的完整十六进制转储。
- **OpenBSD 支持** — 在 OpenBSD 7.8 arm64 上构建,运行 `pledge("stdio rpath wpath cpath inet")` 后绑定端口。
### 已知限制
- 单线程事件循环。库存的 Kunos 可执行文件是多线程的(CONCRT 工作队列 + 每客户端线程)。accd 使用一个非阻塞 `poll()` 循环,最多可处理 256 个数据包的 UDP 突发 —— 在 30 辆车 × 18 Hz 下表现良好,但有意与可执行文件的并发模型不同。
- 少数 Kunos `settings.json` 键被解析和存储,但不驱动任何 accd 行为,因为底层功能未实现:`writeLatencyFileDumps`(无延迟转储接收器)、`configVersion`(无模式迁移),以及整个 CP 服务器堆栈(`isCPServer`、`isCPInvServer`、`competitionRatingMin/Max`、`region` 等)——CP 服务器需要无法从第三方服务器访问的 Kunos 排名后端。
## 构建
纯 C99,可使用 BSD 或 GNU `make` 构建,不依赖 libc、iconv 和 libm 之外的任何库。
### Linux
```
cd accd
make
```
在 `gcc 15.2.0` 上测试通过 Debian sid aarch64。
### OpenBSD
```
cd accd
make
```
Makefile 会自动检测 `/usr/local/include/iconv.h` 并在 iconv 不在 libc 时链接 `-liconv`。在 OpenBSD 7.8 arm64 上使用 `clang 19.1.7` 测试通过。
### 安装
详见 `accd(1)` 完整参考。
## 运行
### 配置文件
`accd` 需要一个包含 JSON 文件的 `cfg/` 目录。每个文件可以是带 BOM 的 UTF-16 LE(`accServer.exe` 写入的格式)或纯 UTF-8 —— 自动检测。
```
./accd # uses ./cfg/
./accd /path/to/other/cfg # explicit path
./accd -c /path/to/cfg # alternative syntax
./accd -d # enable debug tracing
```
configuration.json — 网络
```
{
"udpPort": 9231,
"tcpPort": 9232,
"maxConnections": 30,
"statsUdpPort": 0,
"configVersion": 1
}
```
`statsUdpPort` 为可选项:非零时服务器会每秒推送一次 `0xbe` 状态快照到 `127.0.0.1:` 用于本地监控工具;设为 `0` 禁用。
settings.json — 身份与策略
```
{
"serverName": "My accd server",
"password": "",
"adminPassword": "my-admin-pass",
"spectatorPassword": "",
"maxCarSlots": 30,
"allowAutoDQ": 1,
"registerToLobby": 1,
"useAsyncLeaderboard": 1,
"unsafeRejoin": 1,
"ignorePrematureDisconnects": 0,
"dumpLeaderboards": 0,
"configVersion": 1
}
```
| 键 | 默认值 | 含义 |
|---|---|---|
| `password` | `""` | 加入车辆所需的密码;为空表示公开。 |
| `adminPassword` | `""` | 在游戏中通过 `/admin ` 提升为管理员。 |
| `spectatorPassword` | `""` | 允许客户端以观众身份加入。 |
| `allowAutoDQ` | `1` | `0` 将失败的 DT/SG 降级为 30 秒停走。 |
| `registerToLobby` | `0` | `1` 在 ACC 浏览器中公开列出服务器。 |
| `useAsyncLeaderboard` | `1` | `0` 在每次排名变化时广播。 |
| `unsafeRejoin` | `1` | `0` 拒绝新的中途重连。 |
| `formationLapType` | `3` | 赛事启动变体。`1` 手动(仅私有,广播“比赛开始已初始化”聊天),`3` 默认滚动(静默),`5` 短阵型。 |
| `isPrepPhaseLocked` | `0` | `1` 冻结准备阶段;返回车辆仍可通过(与 `/lockprep` 管理命令相同)。 |
| `shortFormationLap` | `0` | `1` 缩短阵型圈(解析并透传;可执行文件强制公共服务器设为 `1`)。 |
| `writeLatencyFileDumps` | `0` | `1` 启用延迟诊断文件输出(已解析;诊断接收器未挂钩)。 |
| `latencyStrategy` | `0` | 运行时切换 `/latencymode` 的初始值。 |
| `doDriverSwapBroadcast` | `1` | `0` 禁止 0x47 驾驶员切换状态广播;切换进度保留在切换车辆上。 |
| `ignorePrematureDisconnects` | `0` | `1` 容忍客户端提前断开。 |
| `dumpLeaderboards` | `0` | `1` 在每次更新时将快照写入 `results/`。 |
event.json — 赛道、天气与日程
赛事类型:`P`(练习)、`Q`(资格赛)、`R`(比赛)。
三个 `formationTrigger*` / `greenFlag*` 键会覆盖内置的默认值(基于位置的赛事开始门限,归一化赛道位置 0..1)。如上所示为可执行文件编译时的备用值;留空以使用它们。
entrylist.json — 可选的预填充槽位
预分配车辆条目及驾驶员信息、球重、限制器和网格位置。若缺失,服务器会接受任何客户端进入第一个可用槽位。若启用 `forceEntryList: 1`,仅接受列表中的 Steam ID。
通过 steamcmd 获取库存 Kunos 配置文件
```
steamcmd +@sSteamCmdForcePlatformType windows \
+force_install_dir /path/to/acc-server \
+login \
+app_update 1430110 validate +quit
cp -r /path/to/acc-server/server/cfg ./cfg
```
库存文件为 UTF-16 LE;`accd` 按原样读取。转换为 UTF-8 以便手动编辑:
```
iconv -f UTF-16LE -t UTF-8 cfg/settings.json | tr -d '\r' > tmp \
&& mv tmp cfg/settings.json
```
### 启动服务器
```
cd accd
./accd
```
```
2026-04-18 08:19:24 INFO accd phase 1 starting (pid 78045)
2026-04-18 08:19:24 INFO config: tcp=9232 udp=9231 max=30 lan=1 track="monza"
2026-04-18 08:19:24 INFO lan discovery listening on udp/8999
2026-04-18 08:19:24 INFO admin console enabled (type 'help' for commands)
2026-04-18 08:19:24 INFO listening: tcp/9232 udp/9231 (Ctrl-C to stop)
```
通过 `Ctrl-C`、控制台 `quit` 或 `kill -TERM ` 停止。
### 防火墙 / 端口
| 端口 | 协议 | 用途 |
|-----:|:-----:|---------|
| 9232 | TCP | 游戏连接(握手、聊天、会话数据) |
| 9231 | UDP | 车辆遥测(位置、输入、计时) |
| 8999 | UDP | 客户端发现(用于游戏内查找) |
端口 9232 和 9231 在 `configuration.json` 中可配置;UDP 8999 由 ACC 协议固定。三个端口都必须开放。
### 从 ACC 客户端连接
在客户端机器上:
```
%userprofile%\Documents\Assetto Corsa Competizione\Config\serverList.json
```
(macOS / CrossOver:`~/Documents/Games/Assetto Corsa Competizione/Config/serverList.json`)
```
{ "leagueServerIP": "192.168.1.100" }
```
服务器会显示在 ACC 游戏内的多人服务器列表中。
### 管理控制台
当 stdin 为 TTY 时,会并行运行一个交互式管理控制台:
```
$ ./accd
help
commands (leading / optional):
help show this list
status session phase, connections, tick
show cars list car slots in use
show conns list active connections
next advance to next session
restart restart current session
kick kick car by race number
ban kick + persistent ban
dq disqualify
tp5 5s time penalty (tp5c = collision)
tp15 15s time penalty (tp15c)
dt drive-through (dtc)
sg10 10s stop-and-go (sg10c..sg30c)
clear clear penalties for car
clear_all clear all penalties
ballast assign ballast
restrictor % assign restrictor
track [name] show or change track
tracks list available tracks
connections list connections (also broadcasts)
debug toggle debug tracing
quit shut down the server
```
开头的 `/` 可选(`next` 和 `/next` 均有效)。控制台回复输出到标准输出,服务器日志输出到标准错误 —— 通过 `./accd 2>accd.log` 分离。
当 stdin 不是 TTY(例如 `./accd < /dev/null` 或 systemd)时,控制台会自动禁用,服务器以后台模式运行。所有管理命令仍可通过游戏内聊天(输入 `/admin <密码>`)使用。
### 后台服务
无头快速运行:
```
./accd 2>accd.log &
```
生产环境:安装 `.deb` 或 `.rpm` 包,并使用随附的 systemd 服务单元(以非特权动态用户运行,沙箱化):
```
sudo systemctl enable --now accd
# 配置文件位于 /var/lib/accd/cfg/
```
### 快速冒烟测试
拒绝路径(协议版本错误,期望 14 字节的 `0x0c`):
```
printf '\x03\x00\x09\x99\x00' | nc -q 1 127.0.0.1 9232 | xxd
# 期望值:0e 00 0c 07 00 00 00 00 99 00 00 00 00 01 00 00
```
接受路径(正确版本 `0x100`,空密码):
```
printf '\x04\x00\x09\x00\x01\x00' | nc -q 1 127.0.0.1 9232 | xxd
# 期望值:以 0b 0f 24 开头的大型响应
```
## 范围与法律立场
- **在范围内**:在未安装 Wine 的情况下,针对 ACC 客户端(库存版本)在 Linux、OpenBSD 或 FreeBSD 上的私有多人游戏。
- **不在范围内**:社区积分/竞赛积分系统,以及任何需要运行 Kunos 代码的内容。
法律立场(欧盟指令 2009/24/EC,第 6 条)
这是一个基于独立程序的重新实现,仅用于 **互操作性** 目的,依据欧盟指令 2009/24/EC 第 6 条中关于允许反向工程计算机程序的规定:
- 您必须拥有 Steam 上的 Assetto Corsa Competizione 合法副本才能使用本项目。
- 本仓库不包含任何 Kunos 代码或 Kunos 资产 —— 仅包含独立干净的协议规范和独立实现。
- `notebook-b/` 中的规范完全源自 Kunos 随 ACC 专用服务器 Steam 工具(应用 1430110)分发的官方文档,并以作者自己的语言重写。
- 存在一个用于开发过程的本地脏笔记集(gitignored 且 **永不发布**);仅发布干净的协议规范。
## 仓库布局
树状结构
```
.
├── README.md This file.
├── LICENSE BSD-2-Clause license.
├── VERSION Version number (triggers releases).
├── notebook-b/
│ └── NOTEBOOK_B.md The public clean-room protocol spec.
├── accd/ The C implementation (27 modules).
│ ├── main.c Poll loop + signal handling + lifecycle.
│ ├── bans.{c,h} Persistent kick / ban list.
│ ├── bcast.{c,h} Broadcast helpers (TCP + UDP relay).
│ ├── chat.{c,h} Admin chat commands + penalty dispatch.
│ ├── config.{c,h} JSON config reader (UTF-16 LE or UTF-8).
│ ├── console.{c,h} stdin admin console (poll-driven).
│ ├── dispatch.{c,h} TCP / UDP message dispatchers.
│ ├── entrylist.{c,h} entrylist.json reader.
│ ├── handlers.{c,h} Per-msg-id handlers (22 TCP + 4 UDP).
│ ├── handshake.{c,h} ACP_REQUEST_CONNECTION + 0x0b + welcome.
│ ├── io.{c,h} Byte buffer + TCP framing layer.
│ ├── json.{c,h} Recursive-descent JSON parser.
│ ├── lan.{c,h} UDP 8999 LAN discovery handler.
│ ├── lobby.{c,h} Kunos public-lobby client.
│ ├── log.{c,h} Timestamped logger + hexdump.
│ ├── monitor.{c,h} ServerMonitor protobuf message builders.
│ ├── msg.h All message id constants + enums.
│ ├── net.{c,h} tcp_listen / udp_bind helpers.
│ ├── pb.{c,h} Minimal write-only protobuf encoder.
│ ├── penalty.{c,h} Per-car penalty queue.
│ ├── prim.{c,h} Primitive readers / writers + strings.
│ ├── probe.c Standalone protocol probe tool.
│ ├── ratings.{c,h} Persistent SA/TR rating ledger.
│ ├── results.{c,h} Session results JSON writer.
│ ├── session.{c,h} Session phase machine + standings sort.
│ ├── state.{c,h} Per-conn / global server state structs.
│ ├── tick.{c,h} Event-driven relay + periodic broadcasts.
│ ├── weather.{c,h} Deterministic sin/cos weather simulator.
│ └── Makefile
├── debian/ Debian/Ubuntu packaging.
├── redhat/ Fedora/Rocky RPM spec.
└── .github/workflows/ CI: autorelease + multi-distro packaging.
```
27 个模块,约 17,000 行可移植 C99。除 libc、iconv 和 libm 外无依赖。
## 贡献
本项目遵循严格的干净室规范。在贡献前,请阅读
[`notebook-b/NOTEBOOK_B.md`](notebook-b/NOTEBOOK_B.md)。简而言之:
- 公共规范中的每一条事实都必须追溯到公开来源:手册、SDK、变更日志、默认配置、分发日志。
- 对 `accServer.exe` 的静态或动态分析应保留在您自己的私有脏笔记目录(`notebook-a/` 为惯例,gitignored)中,**不得提交**。
- 提升为公共的事实必须以自己的语言重写为协议级关于线上字节的描述 —— 而不是关于任何特定实现的陈述。
## 许可证
[BSD-2-Clause](LICENSE)。`notebook-b/` 中的干净室规范以相同条款发布。
本仓库中没有任何内容由 Kunos Simulazioni 或 505 Games 创作、认可或授权。
## 支持本项目
如果您发现 accd 有用,可以通过以下方式支持开发:
[](https://www.paypal.me/RenaudAllard)标签:ACC, Assetto Corsa Competizione, C99, FreeBSD, OpenBSD, 专用服务器, 内核驱动, 后台服务, 实时多人, 客户端加密, 干净室实现, 开源, 无Kunos二进制, 无Wine, 服务器, 游戏服务器, 竞速模拟, 管理员控制台, 网络协议, 赛车游戏, 防火墙端口