140 lines
4.0 KiB
Python
140 lines
4.0 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""Promote latest FP_Huoshan1 testsavs into StreamingAssets/TestSaveFiles/Huoshan1."""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import shutil
|
|
from pathlib import Path
|
|
|
|
TESTSAVS = Path.home() / "AppData/LocalLow/Tin Bird/AllOurBrokenParts/AllOurBrokenParts/testsavs"
|
|
DEST = Path("Assets/StreamingAssets/TestSaveFiles/Huoshan1")
|
|
CATALOG = Path("Assets/StreamingAssets/TestSaveFiles/catalog.json")
|
|
|
|
YARN = "FP_Huoshan1"
|
|
SCENE = "Scene/HuoShanFixScene"
|
|
SECTION_ID = "Huoshan1"
|
|
SECTION_TITLE = "Huoshan 1"
|
|
|
|
# DevJump 菜单展示顺序(叙事流程);内容仍取各 node 最新 snapshot。
|
|
NODE_ORDER = [
|
|
"开头对话",
|
|
"Stage2",
|
|
"EmoPlugIn",
|
|
"检查情绪",
|
|
"Stage3",
|
|
"结束Stage2",
|
|
"SalePlugIn",
|
|
"检查销售模块",
|
|
"步进查看模式",
|
|
"步进_情绪输入",
|
|
"销售转动_语义合成",
|
|
"销售转动_语言审查",
|
|
"销售模块转动完成",
|
|
"销售转动_开始",
|
|
"销售转动_异常检测",
|
|
"查看子模块",
|
|
"查询异常进程",
|
|
"处理器单次调节失败",
|
|
"旋钮调节",
|
|
"Stage4",
|
|
"结束Stage3",
|
|
"Stage5",
|
|
"结束Stage4",
|
|
"Stage6",
|
|
"结束Stage5",
|
|
"SpeakPlugIn",
|
|
"检查表达",
|
|
"手动释放log",
|
|
"LOG1梳理完成",
|
|
"LOG2梳理完成",
|
|
"LOG疏通全部完成",
|
|
"Stage8",
|
|
"结束Stage7",
|
|
"结束对话",
|
|
"Center",
|
|
]
|
|
|
|
|
|
def sanitize(name: str) -> str:
|
|
bad = '<>:"/\\|?*'
|
|
for c in bad:
|
|
name = name.replace(c, "_")
|
|
name = name.replace(" ", "_")
|
|
if len(name) > 48:
|
|
name = name[:48]
|
|
return name or "no_node"
|
|
|
|
|
|
def ensure_catalog_section() -> None:
|
|
catalog = {"sections": []}
|
|
if CATALOG.exists():
|
|
try:
|
|
catalog = json.loads(CATALOG.read_text(encoding="utf-8")) or catalog
|
|
except json.JSONDecodeError:
|
|
catalog = {"sections": []}
|
|
|
|
sections = catalog.setdefault("sections", [])
|
|
for section in sections:
|
|
if section.get("id") == SECTION_ID:
|
|
if not section.get("title"):
|
|
section["title"] = SECTION_TITLE
|
|
break
|
|
else:
|
|
sections.append({"id": SECTION_ID, "title": SECTION_TITLE})
|
|
|
|
CATALOG.write_text(json.dumps(catalog, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
|
|
|
|
|
def main() -> None:
|
|
by_node: dict[str, dict] = {}
|
|
for d in TESTSAVS.iterdir():
|
|
if not d.is_dir():
|
|
continue
|
|
meta_path = d / "meta.json"
|
|
snap_path = d / "snapshot.json"
|
|
if not snap_path.exists() or not meta_path.exists():
|
|
continue
|
|
meta = json.loads(meta_path.read_text(encoding="utf-8"))
|
|
if meta.get("yarnProjectId") != YARN:
|
|
continue
|
|
if meta.get("sceneName") != SCENE:
|
|
continue
|
|
node = meta.get("nodeName") or "no_node"
|
|
if node == "no_node":
|
|
continue
|
|
saved_at = meta.get("savedAt") or ""
|
|
prev = by_node.get(node)
|
|
if prev is None or saved_at >= prev["saved_at"]:
|
|
by_node[node] = {
|
|
"dir": d,
|
|
"node": node,
|
|
"saved_at": saved_at,
|
|
"so": meta.get("sceneSoName") or "",
|
|
}
|
|
|
|
if DEST.exists():
|
|
shutil.rmtree(DEST)
|
|
DEST.mkdir(parents=True, exist_ok=True)
|
|
|
|
order_index = {name: i for i, name in enumerate(NODE_ORDER)}
|
|
ordered = sorted(
|
|
by_node.values(),
|
|
key=lambda x: (order_index.get(x["node"], 10_000), x["saved_at"], x["node"]),
|
|
)
|
|
print(f"unique nodes: {len(ordered)}")
|
|
for i, item in enumerate(ordered, start=1):
|
|
folder = f"{i:02d}_{sanitize(item['node'])}"
|
|
out = DEST / folder
|
|
out.mkdir(parents=True, exist_ok=True)
|
|
shutil.copy2(item["dir"] / "snapshot.json", out / "snapshot.json")
|
|
shutil.copy2(item["dir"] / "meta.json", out / "meta.json")
|
|
print(f"OK {folder} | {item['saved_at']} | so={item['so']} | node={item['node']}")
|
|
|
|
ensure_catalog_section()
|
|
print("catalog preserved/updated")
|
|
print("done")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|