Files
opendance-windows/README.txt
T
zijunchiang a78a536476 feat(integration): 跨端集成验收(包内客户端 × 真实 Web 服务端)+ 修两个真实缺陷(v0.2.3)
单测打的是假 HTTP 夹具,Web 端测试只验自己那一侧 —— 两端各自绿灯不代表契约字段
真的对得上。本版加了跨端集成验收,并在首次运行就抓到两个真实缺陷。

一、跨端集成验收(T-WIN-019)
  * build/integration/probe-integration.gd:只加载**包内**代码
    (`godot --main-pack <pck> --script <探针>` + res://),打真实服务端并逐项断言:
    /health 六个契约版本、catalog/version 指纹、catalog/export 解析出的四类资源与
    defaultModelId;**场景物理参数逐项比对**(重力/基础风/阵风/风坐标系/地面摩擦坡度/
    碰撞体/出生点/相机/环境)+ Wind.sample(0) + 场景 GLB 可下载;
    动作 NDJSON → 客户端解析帧数 == 服务端帧数、33 点、每帧服务端关节角、
    推导帧率 30 fps、时长 96.67 s;缩略图 PNG;模型 VRM 可下载。
  * build/integration/seed-catalog.mjs:用**真实素材**播种目录
    (示例文件/ 下的真 VMD → 内部标准 33 点;真 PMX → VRM;场景 GLB + 物理参数),
    并把期望值(含从 /health 取的契约版本)写成 JSON。
  * build/accept-integration.sh:编排「服务端可达 → 播种 → 包内探针 → 汇报」。
  实测(v0.2.3 包内 pck × 真实 Web v0.7.4 + 真实 MySQL):全部通过。

二、探针抓到的两个真实缺陷
  1. `CatalogTypes.Motion` 缺 `thumbnail_url`:服务端 catalog/export 从
     data-models 0.6.0 起一直在发 thumbnailUrl(Model/Scene 都有),客户端默默丢掉。
     已补字段与解析(缺失为 ""),新增 tests/test_catalog_types.gd(4 项 20 断言)
     用**服务端真实报文形状**锁住三个资源类型的覆盖,并显式记录一处刻意不消费的差异
     (Model.usable/unusableReason —— 目录只下发可用模型,恒为 true)。
  2. `ApiClient._request()` 对**所有**响应无条件 `raw.get_string_from_utf8()`:
     数据本身没错(二进制路径返回原始字节),但每下载一个文件都会打印
     「Unicode parsing error … Invalid UTF-8 leading byte (N)」噪声,并把整个文件
     (模型可达数十 MB)解码成 String 拷贝后丢弃。改为只在需要文本时解码。

三、顺带修掉测试里的写死契约版本
  tests/test_api_integration.gd 断言 rest-api/websocket-keypoints/skeleton-topology
  均为 "0.1.0"。该用例只在服务端可达时才跑,平时一直 skip,所以契约升到 0.5.0 后
  没有任何地方发现它。改为从仓库 contracts/VERSION 读期望值并与 /health 比对
  (顺带成为「客户端契约快照 vs 服务端」的跨端校验)。

证据:verify.sh 全绿(820 通过 / 0 失败,比上版 +46,因为服务端在线时 API 集成组
真的跑了);包内 pck 跨端集成探针全部通过;check-version/PE 版本资源 = 0.2.3。
2026-09-17 05:18:03 +08:00

370 lines
18 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
================================================================
OpenDance Studio -- Windows 客户端 v0.2.3 使用说明(zip 为 LZMA 压缩,需用 7-Zip/WinRAR 解压)
================================================================
摄像头跳舞评分系统的核心体验端:接收 Android 辅助 App 通过 WebSocket
推送的 33 点骨骼关键点,驱动 VRM 角色,并与 Web 服务端下发的参考动作
做实时 DTW 评分。
本客户端只接收关键点坐标,不接收、不存储任何视频画面。
v0.2.3 修订
1. 补 Motion.thumbnail_url: 服务端 catalog/export 从 data-models 0.6.0 起一直
在发 thumbnailUrl,客户端类型里却缺这个字段(Model/Scene 都有),等于默默丢掉。
2. ApiClient 不再对所有响应无条件做 UTF-8 解码: 原来下载每个文件(模型 / GLB /
缩略图)都会打印一条 "Unicode parsing error ... Invalid UTF-8 leading byte"
噪声,并白做一份 String 拷贝;二进制路径现在直接返回原始字节。
3. 新增跨端集成验收 build/accept-integration.sh: 用发布包里的客户端打真实 Web
服务端,逐项比对契约与场景物理参数,并跑通真实 VMD → 内部标准 33 点 → 客户端
解析 2901 帧的闭环。
4. 测试规模 820 项断言(含真实服务端在线的 API 集成组)。
v0.2.2 修订
1. 修复发布包 exe 的版本元数据: v0.2.1 的包在 Windows「右键 -> 属性 ->
详细信息」里显示的是 文件版本 0.1.0 / 产品版本 0.1.0, 而产物名与 Git tag
都是 v0.2.1。根因是 export_presets.cfg 的 application/file_version 与
application/product_version 被硬编码成 0.1.0, 而它们不会跟随 project.godot
的 application/config/version 自动更新。
2. 新增 build/sync-export-version.sh(导出前同步版本号到预设)与
build/check-pe-version.sh(直接读 exe 的 PE 版本资源复核: VS_FIXEDFILEINFO
的 FileVersion 与 StringFileInfo 的 FileVersion/ProductVersion 两处都查)。
check-version.sh 增加预设校验, package-release.sh 在打包前与解包后各校验一次,
verify.sh 增加步骤 1/5, tests/test_about.gd 增加对应单测。
3. 现在本包显示: 文件版本 0.2.2 / 产品版本 0.2.2。
v0.2.1 修订
1. 场景包围盒不再用 global_transform: SceneRuntime._compute_aabb() 原先在
_assemble() 里调用, 而那时节点还没入树 -> Godot 会打一行 ERROR
(Condition "!is_inside_tree()" is true), 且算出来只是各网格的局部 AABB,
导致重力/风区域与兜底地面算偏位(风力可能作用不到物体). 现改为沿树累乘
Node3D.transform。
2. 新增 21 项无头"真机等价"测试: 手写合法 GLB 真实加载(三种 colliders 取值、
重力/风数值、带位移网格的包围盒) + 17 秒保活对照(只发 ping 的客户端不被
断开、完全不发的客户端被 1001 断开)。
3. 新增 build/check-version.sh: 强制 project.godot / project.yaml / 发布版本
三者一致(已接入打包与验证脚本, 并做过反向验证)。
4. 无头测试规模 751 项断言全部通过。
--
一、快速开始
--
1. 解压后得到:
OpenDance.exe 主程序
OpenDance.pck 资源包(必须与 exe 同目录)
README.txt 本文件
README.md 同内容 Markdown 版
THIRD_PARTY.md 开源声明
contracts/ 契约快照(只读;客户端不直连数据库)
2. 先启动 Web 管理服务端(opendance-web),确保能访问其 API。
3. 双击 OpenDance.exe 启动。
4. 在主界面确认「服务端地址」(默认 http://127.0.0.1:3000),
点击「保存并测试」;顶部应显示「服务端正常 | DB...」。
5. 点击「刷新目录」,左侧会出现已发布的曲目、动作、模型与场景。
6. 在 Android 手机上启动「OpenDance 辅助 App」,填入本机显示在
主界面上的接收地址(形如 ws://<本机局域网地址>:8765/pose)并连接。
连接成功后主界面会提示「已连接 N 台设备」。
7. 依次选择曲目 -> 动作 -> 模型 -> 场景,点击「开始跟练」。
场景可以留空不选:服务端没有可用场景时会自动回退到内置空场景,
界面会提示但不影响开始跟练。
--
二、初次部署(从零跑起来)
--
本节面向第一次拿到源码、要从零把客户端跑起来的人;只运行已打包好的
OpenDance.exe 的用户看第一节即可。
1. 前置条件
- 操作系统:Windows 10/11(x86_64)。开发与导出可在 Windows 或 Linux。
- Godot 4.7.x 标准版(GDScript)。不需要 .NET / C# 版。
- 渲染后端为 GL Compatibility,运行需 OpenGL 3.3 级别的显卡与驱动。
- 导出模板:仅打包导出时需要,必须与编辑器完全同版本。
- 磁盘:编辑器 + 导出模板数百 MB;导出产物 exe 约 104 MB + pck 约 107 KB。
- 网络:能访问 Web 服务端 API(默认 TCP 3000);与手机同一局域网,
并放行本端 TCP 8765 入站。
2. 获取代码
git clone https://gitea.kasugano.cn/zijunchiang/opendance-windows
cd opendance-windows
3. 编辑器内首次运行
- Godot 项目管理器 -> 导入 -> 选择 project.godot。
- 首次导入会执行资源导入(生成 .godot/),可能等待数十秒到数分钟;
不要在导入中途关闭编辑器。
- 主场景已配置为 res://scenes/main.tscn,按 F5 运行。
- 首次运行会生成:
%APPDATA%\Godot\app_userdata\OpenDance Studio\opendance.cfg
%APPDATA%\Godot\app_userdata\OpenDance Studio\logs\godot.log
4. 配置服务端地址
主界面顶部「服务端地址」填好后点「保存并测试」,配置字段名为 api_base_url:
http://<服务器地址>:3000 <- 你们自己部署的 Web 服务端地址(不是被脱敏的域名)
http://192.0.2.10:3000
注意:客户端只访问 API 端口(默认 3000);管理端页面在管理端口
(默认 3001),客户端不使用。保存后点「刷新目录」。
5. 与另两端的联通
- Android 辅助 App -> 本端 WebSocket:本端是服务端,默认监听
0.0.0.0:8765,路径 /pose,子协议 opendance.pose.v1。Android 端填写
本端主界面显示的「接收地址」,形如 ws://<本机局域网地址>:8765/pose。
本端可在 opendance.cfg 改 listen_host / listen_port / ws_subprotocol,
改后点「重新连接接收服务」生效。
- Web 服务端:需先在服务端发布曲目 / 动作 / 模型,动作还须完成关键点
预处理,客户端才有可选内容。
6. 打包导出
# Linux(推荐)
GODOT_BIN=/path/to/godot bash build/export-windows.sh release
# Windows(兜底)
powershell -ExecutionPolicy Bypass -File build\export-windows.ps1
产物在 build/windows/: OpenDance.exe(约 104 MB, PE 头 MZ 校验通过)
+ OpenDance.pck(约 107 KB,必须与 exe 同目录);可能附带
OpenDance.console.exe。
打成发布包:
bash build/package-release.sh 0.2.3
得到 dist-release/ 下两个资产:
opendance-windows-v0.2.3.zip <- 单个完整包,禁止 .partN 分卷
SHA256SUMS <- 裸文件名,可直接 sha256sum -c
包里含 OpenDance.exe、OpenDance.pck、THIRD_PARTY.md、README.txt、
README.md、contracts/。打包脚本会解压自检(exe + pck 必须同目录、
exe 的 PE 头为 MZ、pck 魔数为 GDPC、exe 的 PE 版本资源 = 发布版本)
并打印包体与上传上限的对比。
注意:本项目 Gitea 前置 IIS 的默认上传上限是 30,000,000 字节
(约 28.6 MiB),超过会返回 HTTP 413。这时不要改成分卷,
请让运维放宽上限(做法见 opendance-web/deploy/proxy/README.md 第九节)。
v0.2.0/v0.2.1/v0.2.2 该上限都尚未放开,整包(deflate 约 38 MB)改用
LZMA 压缩到约 26 MB 才传上去(打包时用 ZIP_METHOD=lzma)。
**因此这几个版本的 zip 需要 7-Zip / WinRAR / PeaZip 解压 ——
Windows 10 资源管理器不支持 LZMA 的 zip**。
如果你能把包放到别处分发(例如 Web 服务端的 /static,不受 IIS 那个
上限约束),建议改用资源管理器可直接双击解压的 deflate 包:
ZIP_METHOD=deflate bash build/package-release.sh 0.2.3 # 约 38 MB
上限一旦放开,Release 资产也应换回 deflate 包。
7. 自检清单
0) 发布包级验收(对**发布包**而不是源码;文档 §7.5)
GODOT_BIN=/path/to/godot bash build/accept-release.sh dist-release/opendance-windows-v0.2.3.zip
预期末行「发布包级验收全部通过」。它会校验 sha256、解压、核对文件清单、
读 exe 的 PE 版本资源、用 Godot 直接加载包内 pck 跑 180 帧、并跑包内运行时
探针(包内版本号 / 「关于」清单显示值 / 主场景 / autoload / 关键资源 /
包内契约表与 contracts/VERSION 快照是否一致)。
1) 源码一键验证:GODOT_BIN=/path/to/godot bash build/verify.sh
预期末行「验证全部通过」。
2) 无头测试:$GODOT --headless --path . --script res://tests/run_tests.gd
预期「== 结果:751 通过 / 0 失败 ==」与「全部通过」(缺 VRM 素材或
服务端不可达时相关用例跳过,数字会减少)。
3) 启动冒烟:$GODOT --headless --path . --quit-after 180
预期无 SCRIPT ERROR / ERROR:,退出码 0。
4) 日志:%APPDATA%\Godot\app_userdata\OpenDance Studio\logs\godot.log
预期无 SCRIPT ERROR。
5) 服务端联通:主界面点「保存并测试」后顶部显示「服务端正常 | DB ...」。
8. 部署相关常见问题
Q: 模型加载失败:缺少骨骼
A: 模型无 Skeleton3D,或骨骼命名不在映射表内;换用标准 VRM(VRoid
Studio 导出)。人形覆盖率低于 60% 会提示「骨骼覆盖偏低」。
Q: 连不上服务端 / 显示「服务端不可达」
A: 确认 Web 服务端已启动,api_base_url 指向 http://<服务器地址>:3000
(不要填管理端口 3001);访问 http://<服务器地址>:3000/health 应返回 200。
Q: 导出失败:未找到导出模板
A: 安装与编辑器同版本的 export templates。Linux 放
~/.local/share/godot/export_templates/<版本>.stable/;
Windows 放 %APPDATA%\Godot\export_templates\<版本>.stable\。
Q: 接收服务启动失败(端口 8765 可能被占用)
A: 改 opendance.cfg 的 listen_port 后点「重新连接接收服务」,
并同步修改 Android 端填写的接收地址。
--
三、界面说明
--
主界面
- 四个选择器(2x2 栅格):曲目 / 动作(仅列出已完成预处理的动作)/
模型(VRM 优先)/ 场景(GLB + 物理参数)
- 服务端地址:可修改并即时测试
- 接收地址:Android 端要连接的 WebSocket 地址
- 「刷新目录」重新拉取已发布资源(含场景)
- 底部左侧展示所选动作的来源信息(来源类型、名称、作者、许可、链接)
- 底部中间展示所选场景的缩略图与物理参数摘要
(重力 / 摩擦 / 弹性 / 碰撞体来源 / 风);未选场景时说明将使用内置空场景
游戏界面
- 左侧 3D 视图:VRM 角色落在场景出生点并跟随关键点实时动作,
相机按场景配置取景(follow 跟随 / fixed 固定 / orbit 环绕)
- 场景物理在本端落地:重力(Area3D 覆盖,不改全局 ProjectSettings)、
风(每物理帧施加风力,阵风按正弦叠加)、地面摩擦与弹性、
GLB 碰撞体(mesh 三角网 / auto 命名约定 *_col、*_colonly)、环境光与雾
- 右侧 HUD:实时得分、等级、连击、动作进度、连接状态、来源信息、场景参数
结算界面
- 等级(S/A/B/C/D)、平均分与最终分、动作完成度、最高单帧、最长连击
- 「再来一次」/「返回主菜单」/「开源声明」
关于页面
- 「清单」逐行显示:客户端版本(vX.Y.Z)/ 运行引擎 / 契约版本 /
接收协议 / 接收地址 / 服务端地址 / 隐私说明
- 版本号唯一来源是 project.godot 的 application/config/version,与
project.yaml、导出产物名、Git tag、界面展示完全一致
- 完整可滚动的第三方开源项目声明
--
四、配置文件
--
首次运行会在以下位置生成配置文件,可直接用文本编辑器修改:
Windows: %APPDATA%\Godot\app_userdata\OpenDance Studio\opendance.cfg
可配置项:
api_base_url Web 服务端地址(默认 http://127.0.0.1:3000)
listen_host WebSocket 监听地址(默认 0.0.0.0)
listen_port WebSocket 监听端口(默认 8765)
ws_subprotocol 子协议(默认 opendance.pose.v1)
dtw_band DTW 带宽,单位帧(默认 15 ~ 0.5 秒抖动容忍)
score_tolerance 评分容差:平均每步代价达到该值即 0 分(默认 0.25)
mirror_preview 镜像预览
--
五、防火墙与网络
--
本客户端需要接受 Android 设备的入站连接:
- Windows 防火墙需放行 OpenDance.exe 的 **TCP 8765** 入站
- 手机与电脑需在同一局域网
- 手机端填写主界面显示的「接收地址」
若端口被占用,可修改 opendance.cfg 中的 listen_port 后重启。
--
六、评分说明
--
- 每帧特征 = 33 点 x (x,y,z) + 9 个关节角余弦 = 108 维
(与 Web 服务端预处理产物、Android 端推流结构完全一致)
- 置信度低于 0.5 的关键点不参与代价计算,避免遮挡导致误判
- 采用带约束 DTW(|i-j| <= band)对齐,能吸收一定的节奏快慢差异
- 得分 = clamp(1 - 平均代价 / 容差, 0, 1) x 100,并做指数平滑
- 等级:S >= 95, A >= 85, B >= 70, C >= 55, D 其余
--
七、从源码构建
--
前置:Godot 4.7.x + 对应版本的 export templates
# Linux 上直接导出 Windows 可执行文件(推荐)
GODOT_BIN=/path/to/godot bash build/export-windows.sh release
# Windows 上兜底导出
powershell -ExecutionPolicy Bypass -File build\export-windows.ps1
产物位于 build/windows/: OpenDance.exe + OpenDance.pck
(脚本会校验产物存在且为合法 PE 文件)
运行测试:
GODOT=/path/to/godot
$GODOT --headless --path. --import
$GODOT --headless --path. --script res://tests/run_tests.gd
$GODOT --headless --path. --quit-after 180
--
八、目录结构
--
project.godot 工程配置(autoload / 渲染 / 窗口)
export_presets.cfg 导出预设(必须在项目根目录)
project.yaml 任务清单与状态
DSH.md 本项目 DSH 执行约束
tasks/ 单任务说明书(T-WIN-001 ~ 015)
contracts/ 只读契约快照(来源 opendance-web;六项契约各自独立版本)
scenes/main.tscn 根节点
src/autoload/ AppConfig / CatalogStore / PoseReceiver / ScoreService
src/net/ 239 字节编解码、WebSocket 服务、REST 客户端
src/model/ VRM 加载、humanoid 骨骼映射
src/anim/ 关键点 -> 骨骼重定向
src/scene/ 场景实例化与物理落地(重力/风/碰撞/环境/机位)
src/score/ 特征提取、DTW、评分规则
src/ui/ 主界面 / 游戏界面 / 结算 / 关于 / 来源展示 / 场景参数
tests/ 无头测试框架与用例
build/ 导出脚本
--
九、常见问题
--
Q: 主界面显示「服务端不可达」
A: 检查 Web 服务端是否已启动,以及「服务端地址」是否正确(含端口)。
Q: 目录里没有可用的动作
A: 动作必须先在管理端完成「预处理」(BVH -> 关键点序列)并「审核通过」,
才会出现在客户端。
Q: 模型加载失败 / 动作不完整
A: 游戏界面会显示「人形覆盖率」。低于 60% 说明模型的骨骼命名不被识别,
请换用标准 VRM(VRoid Studio 导出)模型。
Q: 连上了手机但分数一直是 0
A: 确认手机端已开始推流;主界面的「查看接收状态」会显示累计帧数与丢帧数。
Q: 手机端反复重连 / 「已连接」数忽大忽小
A: 本端有 15 秒静默超时(契约 v0.2.0 1.1),并在 welcome/config 里声明
silentTimeoutMs=15000 与 heartbeatIntervalMs=5000;Android 端会按后者发
应用层 ping。若仍频繁断开,用「查看接收状态」看每台设备的状态与断开原因
(close code):1001 表示本端主动踢人或服务重启,0/1006 表示网络抖动。
同一设备的重复连接会按 deviceId 去重(旧连接以 1000 关闭,对端不重连),
所以「已连接 N 台」显示的是去重后的设备数,不是连接数。
Q: 选场景时提示「场景缺少 GLB 文件」或「已回退内置空场景」
A: 服务端该场景没有可下载的 GLB(转换失败或未发布)。这不影响开始跟练,
会使用内置空场景与默认物理参数;请让管理员在管理端「场景管理」重新上传。
Q: 场景里的角色一直往下掉
A: 场景没有可用碰撞体且配置为 colliders=auto/mesh。本端会自动生成兜底地面
(日志会记录「已生成兜底地面」);若场景显式配置 colliders=none,则按场景
作者意图不生成任何碰撞体,此时角色不会被地面接住。
Q: 关于页面的开源声明
A: 与 THIRD_PARTY.md、contracts/open-source-credits.md 三处保持一致。
--
十、许可
--
本客户端代码以 MIT 许可发布。第三方开源项目声明见 THIRD_PARTY.md。
未使用 SMPL 模型及其衍生拓扑,统一采用 MediaPipe 33 点拓扑。