feat(playable): 首个 Mac 联网内测版 STB-01 soak 工具与发布状态文档
- 新增 script/playable_soak.sh:先做 soak 配置/资源校验,无 --allow-gameplay 只校验不启动客户端;N(>=10) 次独立正常退出运行 + 墙钟 soak,失败即停止后续 运行并记 BLOCKED;写本批次 release-manifest.json 后聚合 - run_client_gate.sh:确认的故障代理对所有 suite 启动(共享场景的退出运行也经代理); soak 超时下限只约束 soak 客户端(validate_soak 增加 soak_client 参数) - 新增本地 127.0.0.1 故障代理、RSS 采样/内存判定、窗口/指标/流程模块及其测试 - forest_mob_render_test 输出 PASS/FAIL 标记,供 rendering_batch_test.sh 识别 - 新增 docs/FIRST-MAC-PLAYABLE-STATUS.md:如实记录 PASS/BLOCKED、已知问题与环境需求 - .gitignore 排除本地场景配置、凭据文件与运行输出 离线回归:rendering_batch_test.sh failures=0,playable_gate_test.sh PASS, node 夹具测试 PASS。未联网运行,未重建候选包。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ft3kXpNdLbKEfg5uDLfqVF
This commit is contained in:
@@ -1,14 +1,75 @@
|
||||
extends SceneTree
|
||||
|
||||
## Parent-runner precondition gate. Runs headless before the packaged client
|
||||
## starts; it never opens a network connection or reads credentials.
|
||||
##
|
||||
## env:
|
||||
## MT_PLAYABLE_VALIDATE_CONFIG scenario JSON (required)
|
||||
## MT_PLAYABLE_SUITE playable|full|soak (default playable)
|
||||
## MT_PLAYABLE_ASSETS asset root used for map/waypoint checks
|
||||
## MT_PLAYABLE_SERVERLIST test-mode serverlist override (same value is
|
||||
## handed to the client, so both resolve alike)
|
||||
## MT_PLAYABLE_GATE_OUTPUT directory for required-cases.json / server-address.json
|
||||
## exit: 0 = ready, 2 = configuration/precondition blocked.
|
||||
|
||||
const Config = preload("res://testing/playable_config.gd")
|
||||
const ServerInfoRes = preload("res://net/serverinfo.gd")
|
||||
|
||||
func _init() -> void:
|
||||
var path := OS.get_environment("MT_PLAYABLE_VALIDATE_CONFIG")
|
||||
var suite := OS.get_environment("MT_PLAYABLE_SUITE").strip_edges()
|
||||
if suite.is_empty():
|
||||
suite = "playable"
|
||||
var errors: Array = []
|
||||
if not (suite in Config.SUITES):
|
||||
errors.append("unknown suite: %s" % suite)
|
||||
var result := Config.load_file(path)
|
||||
if not result.ok:
|
||||
for error in result.errors:
|
||||
errors.append_array(result.errors)
|
||||
# A present soak block is validated for every suite: its confirmed fault proxy also
|
||||
# fronts the playable exit runs that share the scenario.
|
||||
if result.ok and (suite == "soak" or result.config.has("soak")):
|
||||
errors.append_array(Config.validate_soak(result.config, suite == "soak").errors)
|
||||
var address := {}
|
||||
var bounds := {}
|
||||
if result.ok:
|
||||
var serverinfo := ServerInfoRes.new()
|
||||
var override := OS.get_environment("MT_PLAYABLE_SERVERLIST").strip_edges()
|
||||
if not override.is_empty():
|
||||
if not serverinfo.load_file(override):
|
||||
errors.append("MT_PLAYABLE_SERVERLIST cannot be loaded")
|
||||
else:
|
||||
serverinfo.load_file("res://serverlist.txt")
|
||||
var resolved := Config.resolve_server(result.config, serverinfo)
|
||||
errors.append_array(resolved.errors)
|
||||
address = resolved.address
|
||||
var assets := OS.get_environment("MT_PLAYABLE_ASSETS").strip_edges()
|
||||
if assets.is_empty():
|
||||
errors.append("MT_PLAYABLE_ASSETS is not set; resource preconditions cannot be checked")
|
||||
else:
|
||||
var resources := Config.check_resources(result.config, assets)
|
||||
errors.append_array(resources.errors)
|
||||
bounds = resources.bounds
|
||||
if not errors.is_empty():
|
||||
for error in errors:
|
||||
printerr("CONFIG: " + String(error))
|
||||
quit(2)
|
||||
return
|
||||
print("PLAYABLE CONFIG: PASS " + path)
|
||||
var out_dir := OS.get_environment("MT_PLAYABLE_GATE_OUTPUT").strip_edges()
|
||||
if not out_dir.is_empty():
|
||||
var cases := Config.required_cases(result.config, suite)
|
||||
if not _write_json(out_dir.path_join("required-cases.json"), {"schema_version": 1, "suite": suite, "cases": cases}) \
|
||||
or not _write_json(out_dir.path_join("server-address.json"), address) \
|
||||
or not _write_json(out_dir.path_join("map-bounds.json"), bounds):
|
||||
printerr("CONFIG: cannot write gate output")
|
||||
quit(2)
|
||||
return
|
||||
print("PLAYABLE CONFIG: PASS")
|
||||
quit(0)
|
||||
|
||||
func _write_json(path: String, value: Variant) -> bool:
|
||||
var file := FileAccess.open(path, FileAccess.WRITE)
|
||||
if file == null:
|
||||
return false
|
||||
file.store_string(JSON.stringify(value, " ") + "\n")
|
||||
file.close()
|
||||
return true
|
||||
|
||||
Reference in New Issue
Block a user