T-WIN-013:API 升级与场景选择 - 契约快照同步到 rest-api 0.5.0 / data-models 0.7.0 / database 0.4.0 - catalog_types.gd 新增 SceneConfig(物理/环境/出生点/相机)与 Scene; CatalogBundle 增加 scenes[];from_dict 按契约 7.1 再收敛一次 (缺项默认、越界夹紧、非法枚举回退),与服务端 normalizeSceneConfig 同规则 - 新增 src/scene/scene_runtime.gd:GLB 载入 -> 碰撞体(mesh 三角网 / auto 命名约定 *_col、*_colonly / none 不兜底)-> 场景级重力(Area3D SPACE_OVERRIDE_REPLACE,不改全局 ProjectSettings)-> 风(每物理帧 apply_central_force,阵风正弦叠加,支持 local 坐标系)-> 环境光与雾 -> 出生点与相机(follow/fixed/orbit);无碰撞体时兜底地面 - CatalogStore 增加 scenes/default_scene/find_scene/ensure_scene_cached/ fetch_thumbnail(场景缓存带 sha256 指纹,避免换包后仍用旧 GLB) - 主界面改为 2x2 四个选择器(曲目/动作/模型/场景)+ 场景参数面板 (缩略图 + 重力/摩擦/弹性/碰撞体/风摘要) - 游戏界面载入场景并落地物理,角色落在出生点;场景缺失/下载失败/ 解析失败一律回退内置空场景并提示,不阻塞开始跟练 T-WIN-012:「关于」清单展示当前版本号 - 版本唯一来源为 project.godot 的 application/config/version(v0.2.0) - about_manifest() 输出版本/引擎/契约/协议/地址/隐私清单; 无头测试无 autoload,读取 AppConfig 走 get_node_or_null 容错 - Credits 的单个 CONTRACT_VERSION 常量改为 CONTRACT_VERSIONS 字典 (六项契约各自独立递增),并双向核对 contracts/VERSION T-WIN-014:发布必须是单个完整包 - build/package-release.sh 重写:只产出单个 zip + SHA256SUMS, 清理历史 .partN,超限时告警而非回退分卷;新增源产物与解压后完整性 自检(PE 头 MZ / pck 魔数 GDPC / exe+pck 同目录同名)与体积-上限对比 - deploy/proxy 相关的 Gitea IIS 256MB 上限说明见 opendance-web 测试:tests/test_scene_config.gd(12 例)+ tests/test_about.gd(4 例); 无头测试 687 通过 / 0 失败(10 个套件),verify.sh 全部通过
88 lines
3.4 KiB
GDScript
88 lines
3.4 KiB
GDScript
extends TestCase
|
||
|
||
## T-WIN-010:开源声明一致性与完整性
|
||
|
||
const Credits = preload("res://src/ui/credits.gd")
|
||
|
||
## 文档 §8.2 模板中的最低 10 项
|
||
const REQUIRED_NAMES := [
|
||
"MediaPipe", "Three.js", "@pixiv/three-vrm", "Assimp", "Godot Engine",
|
||
"godot-vrm", "NestJS", "TypeORM", "OkHttp", "bvh",
|
||
]
|
||
|
||
|
||
func test_credits_cover_documented_minimum_set() -> void:
|
||
var names: Array = []
|
||
for entry in Credits.ENTRIES:
|
||
names.append(str(entry["name"]))
|
||
for required in REQUIRED_NAMES:
|
||
ok(names.has(required), "文档要求的开源项目「%s」已声明" % required)
|
||
ok(Credits.entry_count() >= 10, "声明条目不少于文档要求的 10 项", "实际 %d" % Credits.entry_count())
|
||
|
||
|
||
func test_every_entry_has_license_and_source_link() -> void:
|
||
for entry in Credits.ENTRIES:
|
||
var name := str(entry["name"])
|
||
ne(str(entry["license"]), "", "%s 有许可证" % name)
|
||
var url := str(entry["url"])
|
||
ok(url.begins_with("https://"), "%s 的源链接为 https" % name, url)
|
||
ne(str(entry["usage"]), "", "%s 注明了用途" % name)
|
||
|
||
|
||
func test_known_licenses_are_correct() -> void:
|
||
var expected := {
|
||
"MediaPipe": "Apache License 2.0",
|
||
"Godot Engine": "MIT",
|
||
"Assimp": "BSD-3-Clause",
|
||
"OkHttp": "Apache License 2.0",
|
||
"bvh": "MIT",
|
||
}
|
||
for entry in Credits.ENTRIES:
|
||
var name := str(entry["name"])
|
||
if expected.has(name):
|
||
eq(str(entry["license"]), expected[name], "%s 的许可证正确" % name)
|
||
|
||
|
||
func test_bbcode_contains_all_entries_and_no_smpl() -> void:
|
||
var text := Credits.to_bbcode()
|
||
for entry in Credits.ENTRIES:
|
||
contains(text, str(entry["name"]), "BBCode 含 %s" % str(entry["name"]))
|
||
contains(text, str(entry["url"]), "BBCode 含 %s 的链接" % str(entry["name"]))
|
||
contains(text, "SMPL", "BBCode 明确声明未使用 SMPL")
|
||
contains(text, "第三方开源项目声明", "BBCode 含声明标题")
|
||
for key in Credits.CONTRACT_VERSIONS:
|
||
contains(text, "%s v%s" % [key, Credits.CONTRACT_VERSIONS[key]], "BBCode 含 %s 契约版本" % key)
|
||
|
||
|
||
func test_plain_text_export_is_parseable() -> void:
|
||
var text := Credits.to_plain_text()
|
||
var lines := text.split("\n")
|
||
eq(lines.size(), Credits.entry_count(), "纯文本导出行数等于条目数")
|
||
for line in lines:
|
||
var parts := line.split(" | ")
|
||
eq(parts.size(), 3, "每行三段:名称 | 许可证 | 链接")
|
||
|
||
|
||
func test_contract_versions_match_snapshot() -> void:
|
||
# contracts/VERSION 是唯一来源;本端 README/关于页/THIRD_PARTY 都必须与之一致
|
||
var file := FileAccess.open("res://contracts/VERSION", FileAccess.READ)
|
||
if file == null:
|
||
ok(false, "无法读取 contracts/VERSION 快照")
|
||
return
|
||
var content := file.get_as_text()
|
||
file.close()
|
||
# 六项各自独立递增,逐项核对(不能用单个常量)
|
||
for key in Credits.CONTRACT_VERSIONS:
|
||
contains(content, "%s: %s" % [key, Credits.CONTRACT_VERSIONS[key]], "契约快照 %s 版本一致" % key)
|
||
# 反向核对:快照里的每一项都必须在代码里有对应常量,避免新增契约漏同步
|
||
for line in content.split("\n"):
|
||
var trimmed := line.strip_edges()
|
||
if trimmed == "" or trimmed.begins_with("#"):
|
||
continue
|
||
var parts := trimmed.split(":")
|
||
eq(parts.size(), 2, "VERSION 行格式为 name: version(%s)" % trimmed)
|
||
if parts.size() == 2:
|
||
var name := parts[0].strip_edges()
|
||
var version := parts[1].strip_edges()
|
||
eq(Credits.version_of(name), version, "契约快照新增项 %s 已同步到 Credits" % name)
|