From cd08e793e13e5f09163e0e0d2951b9c0491de234 Mon Sep 17 00:00:00 2001 From: AskaEth Date: Sat, 25 Jul 2026 16:40:03 +0800 Subject: [PATCH] refactor: merge FH4+FH5 into 'forza_horizon', add separate FH6 game plugin --- README.md | 4 +- dashboards/forza_composite/manifest.json | 2 +- dashboards/lap_timer/manifest.json | 2 +- docs/DASHBOARD_DEV.md | 2 +- .../manifest.json | 6 +-- .../parser.py | 8 ++-- games/builtin/forza_horizon_4/manifest.json | 10 ----- games/builtin/forza_horizon_6/manifest.json | 10 +++++ .../parser.py | 43 +++++-------------- 9 files changed, 32 insertions(+), 55 deletions(-) rename games/builtin/{forza_horizon_5 => forza_horizon}/manifest.json (50%) rename games/builtin/{forza_horizon_4 => forza_horizon}/parser.py (92%) delete mode 100644 games/builtin/forza_horizon_4/manifest.json create mode 100644 games/builtin/forza_horizon_6/manifest.json rename games/builtin/{forza_horizon_5 => forza_horizon_6}/parser.py (73%) diff --git a/README.md b/README.md index 3018725..cc75a5a 100644 --- a/README.md +++ b/README.md @@ -102,8 +102,8 @@ TurboSu/ │ └── parsers/ # 内置解析器 (base/forza/ac/acc/f1/iracing) ├── games/ │ ├── builtin/ # 7 款内置游戏插件 -│ │ ├── forza_horizon_5/ # manifest.json + parser.py -│ │ ├── forza_horizon_4/ +│ │ ├── forza_horizon/ # Forza Horizon 4/5 (合并) +│ │ ├── forza_horizon_6/ # Forza Horizon 6 │ │ ├── forza_motorsport/ │ │ ├── assetto_corsa/ │ │ ├── assetto_corsa_competizione/ diff --git a/dashboards/forza_composite/manifest.json b/dashboards/forza_composite/manifest.json index 8c9b3f4..eb14b83 100644 --- a/dashboards/forza_composite/manifest.json +++ b/dashboards/forza_composite/manifest.json @@ -5,7 +5,7 @@ "description": "Forza 系列专属复合仪表:转速弧+档位大字+时速+油门刹车行程+油量水温", "author": "Yei.J. (AskaEth)", "version": "1.0.0", - "supported_games": "forza_horizon_5,forza_horizon_4,forza_motorsport", + "supported_games": "forza_horizon,forza_horizon_6,forza_motorsport", "config": { "icon": "🎮", "aspect_ratio": "16:9", diff --git a/dashboards/lap_timer/manifest.json b/dashboards/lap_timer/manifest.json index ccba71b..6f69a19 100644 --- a/dashboards/lap_timer/manifest.json +++ b/dashboards/lap_timer/manifest.json @@ -5,7 +5,7 @@ "description": "当前圈速、最佳圈和上圈时间对比显示。仅支持 Forza 系列和 F1", "author": "Yei.J. (AskaEth)", "version": "1.0.0", - "supported_games": "forza_horizon_5,forza_horizon_4,forza_motorsport,f1_24", + "supported_games": "forza_horizon,forza_horizon_6,forza_motorsport,f1_24", "config": { "icon": "⏱️", "aspect_ratio": "4:3", diff --git a/docs/DASHBOARD_DEV.md b/docs/DASHBOARD_DEV.md index 319b03c..83b54cb 100644 --- a/docs/DASHBOARD_DEV.md +++ b/docs/DASHBOARD_DEV.md @@ -38,7 +38,7 @@ dashboards/my_dashboard/ | `description` | string | 否 | 描述文字 | | `author` | string | 否 | 作者 | | `version` | string | 否 | 版本号 | -| `supported_games` | string | 否 | 支持的游戏: `"all"` 或 ID 逗号分隔 (如 `"forza_horizon_5,f1_24"`) | +| `supported_games` | string | 否 | 支持的游戏: `"all"` 或 ID 逗号分隔 (如 `"forza_horizon,f1_24"`) | | `config.icon` | string | 否 | 预览图标 emoji | | `config.aspect_ratio` | string | 否 | 渲染比例: `auto` / `16:9` / `4:3` / `1:1` / `21:9` | | `config.render_mode` | string | 否 | 渲染模式: `contain` / `cover` / `fill` / `center` | diff --git a/games/builtin/forza_horizon_5/manifest.json b/games/builtin/forza_horizon/manifest.json similarity index 50% rename from games/builtin/forza_horizon_5/manifest.json rename to games/builtin/forza_horizon/manifest.json index 5eadffe..2b3a0a3 100644 --- a/games/builtin/forza_horizon_5/manifest.json +++ b/games/builtin/forza_horizon/manifest.json @@ -1,9 +1,9 @@ { - "id": "forza_horizon_5", - "name": "Forza Horizon 5", + "id": "forza_horizon", + "name": "Forza Horizon 4/5", "parser_type": "forza", "telemetry_format": "fh5", - "description": "极限竞速:地平线5 遥测数据输出", + "description": "极限竞速:地平线4 & 5 UDP遥测数据 (FH6兼容格式)", "author": "Yei.J. (AskaEth)", "version": "1.0.0", "default_port": 20777 diff --git a/games/builtin/forza_horizon_4/parser.py b/games/builtin/forza_horizon/parser.py similarity index 92% rename from games/builtin/forza_horizon_4/parser.py rename to games/builtin/forza_horizon/parser.py index fa1652a..e2cedf9 100644 --- a/games/builtin/forza_horizon_4/parser.py +++ b/games/builtin/forza_horizon/parser.py @@ -4,15 +4,15 @@ from server.telemetry.data import TelemetryData def get_parser(): - return ForzaHorizon4Parser() + return ForzaHorizonParser() -class ForzaHorizon4Parser: +class ForzaHorizonParser: def game_id(self) -> str: - return "forza_horizon_4" + return "forza_horizon" def parse(self, data: bytes, addr: tuple) -> TelemetryData: - td = TelemetryData(game_id="forza_horizon_4", timestamp=time.time()) + td = TelemetryData(game_id="forza_horizon", timestamp=time.time()) td.raw = {"raw_hex": data.hex(), "length": len(data), "addr": f"{addr[0]}:{addr[1]}"} if len(data) < 312: diff --git a/games/builtin/forza_horizon_4/manifest.json b/games/builtin/forza_horizon_4/manifest.json deleted file mode 100644 index 234871c..0000000 --- a/games/builtin/forza_horizon_4/manifest.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "id": "forza_horizon_4", - "name": "Forza Horizon 4", - "parser_type": "forza", - "telemetry_format": "fh4", - "description": "极限竞速:地平线4 遥测数据输出", - "author": "Yei.J. (AskaEth)", - "version": "1.0.0", - "default_port": 20777 -} diff --git a/games/builtin/forza_horizon_6/manifest.json b/games/builtin/forza_horizon_6/manifest.json new file mode 100644 index 0000000..85c99ad --- /dev/null +++ b/games/builtin/forza_horizon_6/manifest.json @@ -0,0 +1,10 @@ +{ + "id": "forza_horizon_6", + "name": "Forza Horizon 6", + "parser_type": "forza", + "telemetry_format": "fh6", + "description": "极限竞速:地平线6 UDP遥测数据 (新增CarGroup/Smashable字段)", + "author": "Yei.J. (AskaEth)", + "version": "1.0.0", + "default_port": 20777 +} diff --git a/games/builtin/forza_horizon_5/parser.py b/games/builtin/forza_horizon_6/parser.py similarity index 73% rename from games/builtin/forza_horizon_5/parser.py rename to games/builtin/forza_horizon_6/parser.py index e85dbe6..6c4c3c8 100644 --- a/games/builtin/forza_horizon_5/parser.py +++ b/games/builtin/forza_horizon_6/parser.py @@ -4,75 +4,52 @@ from server.telemetry.data import TelemetryData def get_parser(): - return ForzaHorizon5Parser() + return ForzaHorizon6Parser() -class ForzaHorizon5Parser: +class ForzaHorizon6Parser: def game_id(self) -> str: - return "forza_horizon_5" + return "forza_horizon_6" def parse(self, data: bytes, addr: tuple) -> TelemetryData: - td = TelemetryData(game_id="forza_horizon_5", timestamp=time.time()) - td.raw = { - "raw_hex": data.hex(), - "length": len(data), - "addr": f"{addr[0]}:{addr[1]}", - } + td = TelemetryData(game_id="forza_horizon_6", timestamp=time.time()) + td.raw = {"raw_hex": data.hex(), "length": len(data), "addr": f"{addr[0]}:{addr[1]}"} if len(data) < 312: return td try: td.raw["is_race_on"] = struct.unpack_from("