tests/test_pose_server.gd 用的是**同引擎内的客户端**(Godot WebSocketPeer),而真实
对端是 Android(Kotlin/OkHttp)——同引擎自测会共享同样的假设(超时判定、分包、
文本/二进制边界、关闭语义、子协议协商),于是「测试全绿」不等于「Android 连得上」。
契约 §7 也写明:静默超时要用保活避免,而不是靠重连掩盖;这句话只有用独立实现的
客户端实测才算验过。
新增三个文件:
* build/integration/probe-ws.gd —— 把**包内** PoseServer 跑起来(--main-pack +
只加载包内代码),把连接/控制帧/帧/断开事件汇总成 JSON 报告;自身不做判定
(避免「用被测实现判定被测实现」)。支持 --stop-file 提前收尾。
* build/integration/ws-client.mjs —— Node 实现的契约一致性客户端(用内置
WebSocket,零依赖),逐条跑 §1 §1.1 §2 §3.1-§3.6 §5 §6 §7 的场景。
* build/accept-ws.sh —— 编排:解包 → 起探针 → 等监听 → 跑客户端 → 收报告 →
核对客户端看不到的那一半(frames_dropped / malformed_frames / 控制帧计数 /
device_replaced / 关闭原因 / 静默超时消息带毫秒数与阈值)。
实测(**已发布**的 v0.2.3 资产重新下载后跑):客户端 30 通过 / 0 失败 + 服务端侧
16 项事实全过。关键两条:
* 保活对照:只发应用层 ping 持续 19 秒(> 静默超时 15 秒)连接**仍保持打开**;
* 静默超时:完全不发数据 → close(1001,"timeout"),实测 15011 ms(阈值 15000 ms);
另有:子协议协商、welcome 声明保活参数且 heartbeat(5000) < silentTimeout(15000)/2、
pong 原样回传 sentAtMs、同 deviceId 替换旧连接(1000 "replaced by new connection")、
bye → 1000、协议不匹配 → bye + 1002、72 帧含故意跳号(服务端 frames_dropped=3)
与故意 100 字节非法帧(malformed_frames=1)。
反向验证:新增**故意不合规的服务端** build/integration/probe-ws-bad.gd
(welcome 不声明保活字段、不带 sessionId、收到 ping 不回 pong、不做 deviceId 去重),
对同一客户端 → 14 项 ✗、退出码 1,证明断言不是空转。
踩坑记录(已写入任务文档):
1. --script 模式下从无类型数组取 peer,peer.get_packet() 返回类型推不出来,
var x := … 直接 Parse Error;
2. 复核静默超时消息时写了否定式断言 !includes("静默超时 1"),把正确行为判成失败 ——
断言应写成正向模式匹配;
3. 探针原本只能按固定时长跑,编排脚本得猜时间;改成客户端跑完 touch stop 文件。
顺带:package-release.sh 结尾把三条发布前验收(产物自证 / WS 契约一致性 / 跨端集成)
一次列全。不影响已发布的 v0.2.3 产物,故不新发版本。
22 KiB
opendance-windows
OpenDance Studio 阶段二: Windows 客户端(Godot 4)。
接收 Android 辅助 App 通过 WebSocket 推送的 33 点骨骼关键点,驱动 VRM 角色, 并与 Web 管理服务端下发的参考动作做实时 DTW 评分。
本客户端只接收关键点坐标,不接收、不存储任何视频画面。
状态
阶段二已完成: T-WIN-001 -> T-WIN-014 全部落地(含场景选择与整包发布)。
| 验收项 | 结果 |
|---|---|
| 脚本编译 | OK 全部通过(无 SCRIPT ERROR) |
| 无头测试 | OK 751 项全部通过(10 个测试套件) |
| 应用启动 | OK 180 帧无运行时错误 |
| Windows 导出 | OK 生成 OpenDance.exe(104 MB, PE 头校验通过)+ OpenDance.pck |
| 真实 VRM 验证 | OK 154 骨骼 VRoid 模型,humanoid 覆盖率 100% |
| 骨骼重定向 | OK 关键点方向与骨骼实际指向点积 1.0000 |
| 跨实现交叉校验 | OK 关节角与 Web 服务端 TypeScript 实现逐帧一致 |
| 场景选择与物理落地 | OK scenes[] 可选、重力/风/摩擦/碰撞体落地并可日志核对 |
| 连接保活与设备去重 | OK welcome/config 声明静默超时与心跳;应用层 ping/pong;按 deviceId 去重 |
| 发布整包 | OK 单个 opendance-windows-v0.2.3.zip + SHA256SUMS(不拆分卷) |
| 版本号展示 | OK 「关于」清单显示 v0.2.2;project.godot / project.yaml / export_presets.cfg / exe 的 PE 版本资源 / 产物名 / tag 全部一致 |
| 场景物理与保活 | OK 新增真实 GLB 端到端用例 + 17 秒保活对照用例(751 项断言全过) |
v0.2.3 修订(跨端集成验收 + 两处真实缺陷)
缺陷 1(契约字段缺失):CatalogTypes.Motion 没有 thumbnail_url 字段,
而服务端 catalog/export 从 data-models 0.6.0 起一直在发 thumbnailUrl
(同一文件的 Model / Scene 都有)。客户端默默丢掉该字段。
修复:补字段与解析(缺失时为 "",兼容旧服务端),并新增
tests/test_catalog_types.gd(4 项)用服务端真实报文形状锁住三个资源类型的
thumbnailUrl 覆盖;同时显式记录一处刻意不消费的差异
(Model.usable/unusableReason —— 目录只下发可用模型,恒为 true)。
缺陷 2(二进制响应白做 UTF-8 解码):ApiClient._request() 对所有响应
无条件执行 raw.get_string_from_utf8()。数据本身没错(二进制路径返回的是原始字节),
但每下载一个文件都会打印一条
Unicode parsing error … Invalid UTF-8 leading byte (N) 噪声,
并把整个文件(模型可达数十 MB)解码成一份 String 拷贝后丢弃。
修复:只在需要文本时解码。跨端集成探针第一次注意到这条噪声。
新增跨端集成验收(build/accept-integration.sh):用发布包里的客户端
(--main-pack 加载包内 pck)打真实 Web 服务端,逐项比对契约与物理参数,
并跑通「真 VMD → 内部标准 33 点 → 客户端解析 2901 帧」的完整闭环。
详见 tasks/019-cross-end-integration.md。
此外修掉 tests/test_api_integration.gd 里一处写死的契约版本(0.1.0):
该用例只在服务端可达时才跑,平时一直 skip,于是契约升到 0.5.0 后
没有任何地方发现它。现在改为从仓库 contracts/VERSION 读期望值并与服务端 /health 比对。
v0.2.2 修订(发布包 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 自动更新;check-version.sh
当时只比 project.godot ↔ project.yaml(两处一致),所以静态校验全绿、
产物却是错的 —— 漂移发生在没人看的第三个位置。
修复:
| 环节 | 做法 |
|---|---|
| 同步 | build/sync-export-version.sh(新增):导出前把版本号写进导出预设(幂等,拒绝非 X.Y.Z) |
| 静态防线 | build/check-version.sh:新增对导出预设两个键的校验 |
| 产物防线 | build/check-pe-version.sh(新增):直接读 exe 的 PE 版本资源 —— VS_FIXEDFILEINFO(签名 0xFEEF04BD 后 16 字节)+ StringFileInfo(strings -el,属性页显示的就是它)。两处都查:本机实测固定信息里的 ProductVersion 恒为 0.0.0.0 |
| 打包 | build/package-release.sh:打包前校验源 exe、解包后再校验包内 exe |
| 一键验证 | build/verify.sh:新增步骤 1/5(未导出则跳过) |
| 单元测试 | tests/test_about.gd:预设两个键必须等于版本号唯一来源 |
实测修复后产物:VS_FIXEDFILEINFO FileVersion = 0.2.2.0、
StringFileInfo FileVersion/ProductVersion = 0.2.2。详见
tasks/017-pe-version.md。
v0.2.1 修订(两处修复 + 一批"真机等价"测试)
- 场景包围盒不再用
global_transform:SceneRuntime._compute_aabb()原本在_assemble()里调用,而那时运行时节点还没入树 —— Godot 会打一行ERROR: Condition "!is_inside_tree()" is true,并且算出来的只是各网格的 局部 AABB。后果:把网格摆在非原点位置的场景,重力/风区域与兜底地面会算得 偏小或偏位(风力可能作用不到物体上)。现改为沿树累乘Node3D.transform, 与是否入树无关,结果与运行时挂在单位变换父节点下完全一致; - 新增 21 项"真机等价"测试(无头即可跑,不需要设备):
tests/test_scene_config.gd:在代码里手写合法 GLB(三角形 + BIN chunk)后 真实加载,核对三种colliders取值的碰撞体、重力/风 Area3D 数值、 以及带位移网格的包围盒是否跟着走(这一条正是上述缺陷的回归测试);tests/test_pose_server.gd:时间维度验证保活 —— 一个只发应用层ping、 不发关键点帧的客户端在 17 秒(> 15 秒静默超时)后仍保持连接, 而完全不发数据的客户端被close(1001)断开。这正是"画面里没人就掉线、 然后疯狂重连"的根因对照;
- 新增版本号一致性校验
build/check-version.sh:强制project.godot的application/config/version(「关于」清单与 .pck 的来源) 与project.yaml、发布版本号三者一致;已接入build/package-release.sh(打包前)与build/verify.sh(0/4 步),并做过反向验证。
快速开始
# 验证(编译 -> 测试 -> 启动)
GODOT_BIN=/path/to/godot bash build/verify.sh
# 导出 Windows 可执行文件
GODOT_BIN=/path/to/godot bash build/export-windows.sh release
# 打包发布产物(单个完整 zip + SHA256SUMS)
bash build/package-release.sh 0.2.3
# 发布包级验收(对**发布包**跑,而不是对源码;文档 §7.5)
GODOT_BIN=/path/to/godot bash build/accept-release.sh dist-release/opendance-windows-v0.2.3.zip
accept-release.sh 校验 sha256 → 解压 → 文件清单(并检查没有 .partN 残留)→
exe 的 PE 版本资源 → 用 Godot 直接加载包内 pck 跑 180 帧 → 包内运行时探针
(build/probe-release.gd:包内版本号、AboutScreen.app_version()、主场景、autoload、
15 个关键资源、以及包内 Credits.CONTRACT_VERSIONS 与 contracts/VERSION 快照是否一致)。
它读的全是包内资源,因此结论就是「用户装到机器上的那一份能不能用」。
已对已发布的 v0.2.2 资产(重新下载)跑通,并用「篡改包内 contracts/VERSION」与
「期望版本写错」两个负例反向验证过。
WebSocket 契约一致性验收(T-WIN-020)
tests/test_pose_server.gd 用的是同引擎内的客户端,而真实对端是
Android(Kotlin/OkHttp) —— 同引擎自测会共享同样的假设(超时判定、分包、
文本/二进制边界、关闭语义、子协议协商)。因此另配一套独立实现的客户端:
GODOT_BIN=/path/to/godot bash build/accept-ws.sh dist-release/opendance-windows-v0.2.3.zip
# 跳过耗时的两个场景(保活 19s / 静默超时 15s):
GODOT_BIN=... bash build/accept-ws.sh <zip> --skip-slow
build/integration/probe-ws.gd:把包内 PoseServer 跑起来并汇总事件成 JSON(自身不判定);build/integration/ws-client.mjs:Node 实现的契约一致性客户端(无依赖,用内置WebSocket), 逐条跑 §1 §1.1 §2 §3.1-§3.6 §5 §6 §7 的场景;build/accept-ws.sh:编排两者,并核对客户端看不到的那一半(服务端侧frames_dropped/malformed_frames/ 控制帧计数 / 去重事件 / 关闭原因)。
实测(已发布的 v0.2.3 资产):客户端 30 通过 / 0 失败 + 服务端侧 16 项事实全过,
其中最关键的是保活对照——只发应用层 ping 持续 19 秒(> 静默超时 15 秒)连接
仍保持,而完全不发数据的连接在 15011 ms(阈值 15000 ms)被 close(1001, "timeout")。
反向验证用一个故意不合规的服务端(build/integration/probe-ws-bad.gd:welcome 不声明
保活字段、不回 pong、不做去重)跑同一客户端 → 14 项 ✗、退出码 1,证明断言不是空转。
详见 tasks/020-ws-contract-conformance.md。
当前版本:v0.2.3(project.godot 的 application/config/version,
与 project.yaml、export_presets.cfg、导出产物名、Git tag、「关于」清单展示、
以及 exe 的 PE 版本资源全部一致)。
详细使用说明见 README.txt,工程约束见 DSH.md。
初次部署(从零跑起来)
本节面向第一次拿到本仓库的人:从安装引擎到跑通「Web 服务端 + 本端 + Android」 三端链路。地址与端口一律使用占位符或产品默认值。
1. 前置条件
| 项 | 要求 |
|---|---|
| 操作系统 | 运行客户端:Windows 10/11(x86_64)。开发与导出:Windows 或 Linux 均可 |
| Godot | 4.7.x(project.godot 声明特性 4.7,本仓库按 4.7.2 验证)。使用标准版(GDScript), 不需要 .NET / C# 版 |
| 显卡与图形界面 | 渲染后端为 GL Compatibility,运行需支持 OpenGL 3.3 的显卡与驱动。仅无头测试与导出可无 GPU、可无图形界面 |
| 导出模板 | 仅打包导出时需要:必须安装与编辑器完全同版本的 export templates(.tpz) |
| 磁盘与网络 | Godot 编辑器 + 导出模板约数百 MB;导出产物 OpenDance.exe 约 104 MB、OpenDance.pck 约 107 KB。本端需能访问 Web 服务端 API(默认 TCP 3000),并与 Android 设备处于同一局域网、放行本端 TCP 8765 入站 |
2. 获取代码
git clone https://gitea.kasugano.cn/zijunchiang/opendance-windows
cd opendance-windows
3. 首次运行(编辑器内)
- 启动 Godot 4.7.x,在项目管理器选择「导入」,指向本仓库的
project.godot。 - 首次导入会执行资源导入(生成
.godot/),耗时从数十秒到数分钟不等;请等待完成,不要在导入中途关闭编辑器。 - 主场景已在
project.godot中配置为res://scenes/main.tscn,直接按 F5(或「运行项目」)即可。 - 界面全部由 GDScript 代码构建,不需要在编辑器里编辑场景,这是刻意设计(见
DSH.md6)。 - 首次运行会在用户数据目录生成配置与日志:
%APPDATA%\Godot\app_userdata\OpenDance Studio\opendance.cfg
%APPDATA%\Godot\app_userdata\OpenDance Studio\logs\godot.log
4. 配置服务端地址
- 位置:主界面顶部「服务端地址」输入框,填好后点「保存并测试」。
- 配置字段名(
opendance.cfg):api_base_url。 - 端口区分:客户端只访问 Web 服务端的 API 端口(默认 3000);管理端页面在管理端口(默认 3001),客户端不使用该端口。
- 示例占位符:
http://<服务器地址>:3000
http://192.0.2.10:3000
<服务器地址>是你们自己部署的 Web 服务端地址(部署方决定,不是被脱敏的域名); 上面第二行是 RFC 5737 文档用示例 IP,照抄时请替换。
保存后点「刷新目录」;顶部应显示「服务端正常 | DB ...」,左侧列出已发布的曲目 / 动作 / 模型。
5. 与另外两端的联通
| 对端 | 方向 | 本端配置 | 说明 |
|---|---|---|---|
| Android 辅助 App | Android -> 本端(WebSocket) | listen_host(默认 0.0.0.0)、listen_port(默认 8765)、ws_subprotocol(默认 opendance.pose.v1) |
本端是 WebSocket 服务端; Android 端填写本端主界面显示的「接收地址」,形如 ws://<本机局域网地址>:8765/pose |
| Web 服务端 | 本端 -> Web(HTTP) | api_base_url(默认 http://127.0.0.1:3000) |
需先在服务端发布曲目 / 动作 / 模型 / 场景,动作还须完成关键点预处理,否则目录为空或该项不可选 |
场景不是必需项:服务端没发布任何可用场景(或缺 GLB)时,本端回退到内置空场景 并在界面提示,不会阻塞开始跟练(文档 §5.6 第 4 条)。
- Android 侧无本端可配置项:接收地址由 Android App 自己填写;本端只监听固定端口 8765(可在
opendance.cfg改listen_port后点「重新连接接收服务」生效)。 - 已知限制:Godot 的
WebSocketPeer不暴露握手请求路径,本端接受任意 path,只校验子协议opendance.pose.v1(见「已知限制」1)。 - 接收状态可用主界面「查看接收状态」查看:连接数、去重后设备数、累计帧、 丢帧、畸形帧、保活参数,以及每台设备的状态(契约 §8)与首帧时间。
6. 打包导出
# Linux 上直接导出 Windows 可执行文件(推荐)
GODOT_BIN=/path/to/godot bash build/export-windows.sh release
# Windows 上兜底导出
powershell -ExecutionPolicy Bypass -File build\export-windows.ps1
# 打成可分发的 zip(单个完整包 + SHA256SUMS)
bash build/package-release.sh 0.2.3
| 产物 | 位置 | 说明 |
|---|---|---|
OpenDance.exe |
build/windows/ |
主程序,约 104 MB;脚本校验前 2 字节为 MZ(合法 PE) |
OpenDance.pck |
build/windows/ |
资源包,约 107 KB; 必须与 exe 同目录 |
OpenDance.console.exe |
build/windows/ |
控制台包装器,可能生成,非必需 |
opendance-windows-v0.2.3.zip |
dist-release/ |
发布包:exe + pck + THIRD_PARTY.md + README.txt + README.md + contracts/ |
SHA256SUMS |
dist-release/ |
裸文件名,与 zip 同目录时可 sha256sum -c SHA256SUMS 直接校验 |
导出前必须安装与编辑器同版本的 export templates,否则脚本会报「未找到导出模板」。
7. 自检清单
| 检查 | 命令 | 预期输出 |
|---|---|---|
| 一键验证(编译 + 测试 + 启动) | GODOT_BIN=/path/to/godot bash build/verify.sh |
末行「验证全部通过」,退出码 0 |
| 无头单元 / 集成测试 | $GODOT --headless --path . --script res://tests/run_tests.gd |
== 结果:687 通过 / 0 失败 == 与 全部通过(仓库基线 687 项 / 10 个套件;缺 VRM 素材或服务端不可达时相关用例显式跳过,数字会减少) |
| 启动冒烟(180 帧) | $GODOT --headless --path . --quit-after 180 |
无 SCRIPT ERROR / ERROR:,进程退出码 0 |
| 日志检查 | 打开 %APPDATA%\Godot\app_userdata\OpenDance Studio\logs\godot.log |
无 SCRIPT ERROR、无 ERROR: |
| 导出产物校验 | bash build/export-windows.sh release |
输出「PE 头校验通过」,且 build/windows/OpenDance.exe 前 2 字节为 4d5a |
| 服务端联通 | 主界面「服务端地址」->「保存并测试」 | 顶部显示「服务端正常 |
8. 常见问题
| 现象 | 原因 | 处理 |
|---|---|---|
| 游戏界面提示「模型加载失败:缺少骨骼」或「骨骼绑定失败,模型可能不受支持」 | 模型没有 Skeleton3D,或骨骼命名不在 humanoid 映射表内;驱动至少需要 hips / spine / neck / head + 四肢共 16 根核心骨骼 |
换用标准 VRM(VRoid Studio 导出);看「人形覆盖率」,低于 60% 会提示「骨骼覆盖偏低,动作可能不完整」;必要时在 src/model/humanoid_map.gd 增加命名映射 |
| 顶部「服务端不可达」或「目录拉取失败」 | Web 服务端未启动、地址或端口写错(误填管理端口 3001)、或网络不通 | 确认 api_base_url 为 http://<服务器地址>:3000;浏览器访问 http://<服务器地址>:3000/health 应返回 200;再点「保存并测试」 |
| 导出失败:「未找到导出模板」 | 未安装 export templates,或版本 / 路径与编辑器不一致 | 编辑器「编辑器 -> 管理导出模板 -> 下载并安装」;Linux 放 ~/.local/share/godot/export_templates/<版本>.stable/, Windows 放 %APPDATA%\Godot\export_templates\<版本>.stable\ |
| 提示「接收服务启动失败(端口 8765 可能被占用)」 | TCP 8765 被占用或未放行 | 改 opendance.cfg 的 listen_port 后点「重新连接接收服务」,并同步修改 Android 端填写的接收地址 |
| 手机已连接但分数一直是 0 | Android 端未开始推流,或帧内关键点置信度都低于 0.5 | 「查看接收状态」确认累计帧在增长、畸形帧为 0;确认 Android 端子协议为 opendance.pose.v1 |
技术栈
| 组件 | 选型 |
|---|---|
| 引擎 | Godot 4.7.x(MIT) |
| VRM 加载 | Godot 内置 GLTFDocument |
| 通信 | WebSocketPeer(服务端)+ HTTPRequest |
| 评分 | DTW + 关节角度差异(纯 GDScript) |
架构
Android 辅助 App ──239 字节关键点帧──▶ 本端 WebSocket 服务端:8765
│
Web 服务端 ──GET /api/catalog/export────────┤
──GET /api/motions/:id/keypoints ┘
▼
VRM 驱动 + DTW 评分
src/
├── autoload/ AppConfig / CatalogStore / PoseReceiver / ScoreService
├── net/ 239 字节编解码、WebSocket 服务、REST 客户端、NDJSON 解析
├── model/ VRM 加载、humanoid 骨骼名映射
├── anim/ 关键点 -> 骨骼重定向
├── scene/ 场景实例化与物理落地(重力/风/碰撞/环境/机位)
├── score/ 特征提取、DTW、评分规则
└── ui/ 主界面 / 游戏界面 / 结算 / 关于 / 来源展示 / 场景参数面板
界面全部用 GDScript 代码构建(不手写 .tscn),因此可被无头导入与单元测试覆盖。
依赖的契约(opendance-web 冻结,逐项独立版本)
| 契约 | 版本 |
|---|---|
rest-api |
0.5.0 |
data-models |
0.7.0 |
websocket-keypoints |
0.2.0 |
skeleton-topology |
0.2.0 |
model-formats |
0.5.0 |
database |
0.4.0 |
contracts/websocket-keypoints.md- 239 字节关键点帧与文本控制消息(本端为接收方)contracts/skeleton-topology.md- MediaPipe 33 点拓扑、父节点表、9 个关节角特征contracts/rest-api.yaml- 目录与关键点序列 APIcontracts/data-models.md- 资源字段与 VRM 优先规则contracts/model-formats.md- 模型与动作格式、关键点序列结构contracts/database.md- 数据库表结构(客户端只读快照,不直连数据库)
contracts/ 是只读快照。任何跨端契约变更必须先回到
opendance-web 修改并提升版本,
再在本仓库创建适配任务。
已知限制
- Godot 的
WebSocketPeer不暴露握手请求路径,因此无法校验 path 是否为/pose, 实现接受任意 path,仅校验子协议opendance.pose.v1。 - 文档原选型
godot-vrmGDExtension 不发布预编译产物,改用 Godot 内置 glTF 导入器- 自实现骨骼名映射;理由见
DSH.md6。
- 自实现骨骼名映射;理由见
build/export-windows.ps1未在真实 Windows 上执行过(本环境无 Windows)。 两条路径共用同名导出预设与相同的 PE 头校验逻辑。- 真实 VRM 样例仅放在
_scratch/(已 gitignore),版权原因不入库; 测试在找不到模型时显式跳过而非伪装通过。 - Gitea 附件上传上限:本项目 Gitea 前置 IIS 的默认
maxAllowedContentLength = 30,000,000字节(≈28.6 MiB),实测 16.4 MB 通过、 30.5 MB / 37 MB / 40 MB 上传返回 HTTP 413。v0.1.0因此被拆成zip.part0/1/2(无法直接解压)。现改为只上传单个完整包,超限时打包脚本 直接告警并要求运维放宽上限,不再回退分卷;做法见opendance-web/deploy/proxy/README.md第九节。 - v0.2.0 / v0.2.1 / v0.2.2 的 zip 用的是 LZMA 压缩(
ZIP_METHOD=lzma打出来的),因为这几次发布时 IIS 上限都尚未放开:整包从约 38 MB 降到约 26 MB 才能传上去。代价:Windows 10 的资源管理器不支持 LZMA(method 14) 的 zip, 必须用 7-Zip / WinRAR / PeaZip 解压(Win11 24H2+ 起资源管理器原生支持 7z 等格式,但本项目未在 Windows 上实测)。 若 IIS 上限已放开、或你能把包放到别处分发(例如 Web 服务端的/static, 不受 IIS 那个限制约束),请改用资源管理器可直接双击解压的 deflate 包:ZIP_METHOD=deflate bash build/package-release.sh 0.2.3(约 38 MB)。 - 发布包 exe 的版本元数据(T-WIN-017):v0.2.1 之前在 Windows
「属性 → 详细信息」里显示的是
0.1.0,因为export_presets.cfg的application/file_version/application/product_version被硬编码且无人校验。 v0.2.2 起由build/sync-export-version.sh在导出前同步、由build/check-pe-version.sh直接读 exe 的 PE 版本资源复核 (VS_FIXEDFILEINFO+StringFileInfo两处都查),check-version.sh/package-release.sh/verify.sh/test_about.gd均已接入。
第三方开源声明
见 THIRD_PARTY.md,或客户端「关于」页面。
未使用 SMPL 模型及其衍生拓扑,统一采用 MediaPipe 33 点拓扑。