Files
cocos/tools/renumber-level-prefabs.py
刘宇飞 d393302388 Complete Cocos Creator port with level bundles, themes, and tooling.
Adds level prefabs, theme assets, audio, extensions, and deployment scripts for the Unity WebGL migration.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-16 15:30:58 +08:00

143 lines
4.9 KiB
Python
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.
#!/usr/bin/env python3
"""
将 level-prefabs/Level{N}.prefab 重命名为 Level{91600+N}.prefab并同步 JSON 数据库。
与 Unity / 主站 config.js BEGINNING_REAL_LVID=91601 对齐:游戏 ID 从 91601 起(首关 Level91601
用法:
python3 tools/renumber-level-prefabs.py --dry-run
python3 tools/renumber-level-prefabs.py
"""
from __future__ import annotations
import argparse
import json
import re
from datetime import datetime, timezone
from pathlib import Path
from level_id import (
LEVEL_ID_BASE,
internal_level_index as to_internal_id,
prefab_resource_path,
touch_database,
)
def to_external_id(internal: int) -> int:
return LEVEL_ID_BASE + internal
PREFAB_DIR_NAME = "level-prefabs"
LEVEL_RE = re.compile(r"^Level(\d+)\.prefab$", re.I)
def rename_prefabs(prefab_dir: Path, dry_run: bool) -> dict[int, int]:
"""internal -> external mapping for renamed files."""
mapping: dict[int, int] = {}
files = sorted(
[p for p in prefab_dir.glob("Level*.prefab") if LEVEL_RE.match(p.name)],
key=lambda p: int(LEVEL_RE.match(p.name).group(1)), # type: ignore
reverse=True,
)
for src in files:
m = LEVEL_RE.match(src.name)
if not m:
continue
internal = int(m.group(1))
if internal > LEVEL_ID_BASE:
continue
external = to_external_id(internal)
dst = prefab_dir / f"Level{external}.prefab"
meta_src = Path(str(src) + ".meta")
meta_dst = Path(str(dst) + ".meta")
mapping[internal] = external
if dry_run:
if internal <= 3 or internal in (60, 80, 4188):
print(f" {src.name} -> {dst.name}")
continue
if dst.exists():
raise SystemExit(f"目标已存在,中止: {dst}")
src.rename(dst)
if meta_src.is_file():
meta_src.rename(meta_dst)
return mapping
def migrate_levels_database(db_path: Path, mapping: dict[int, int], dry_run: bool) -> None:
if not db_path.is_file():
print(f"skip database (not found): {db_path}")
return
data = json.loads(db_path.read_text(encoding="utf-8"))
old_levels = data.get("levels") or {}
new_levels: dict[str, dict] = {}
for key, cfg in old_levels.items():
try:
old_id = int(key)
except ValueError:
old_id = int(cfg.get("levelID", key))
internal = to_internal_id(old_id) if old_id > LEVEL_ID_BASE else old_id
external = mapping.get(internal, to_external_id(internal))
entry = dict(cfg)
entry["levelID"] = external
unity_prefab = entry.get("unityPrefab", "")
if isinstance(unity_prefab, str) and unity_prefab:
entry["unityPrefab"] = re.sub(
r"Level\d+\.prefab$",
f"Level{external}.prefab",
unity_prefab.replace("\\", "/"),
flags=re.I,
)
entry["cocosPrefab"] = prefab_resource_path(external)
new_levels[str(external)] = entry
data["levels"] = new_levels
if dry_run:
print(f"database: {len(old_levels)} -> {len(new_levels)} keys:", sorted(new_levels.keys()))
return
db_path.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")
touch_database(db_path)
print(f"updated {db_path}")
def rebuild_prefab_index(prefab_dir: Path, index_path: Path, dry_run: bool) -> None:
index: dict[str, str] = {}
for p in sorted(
prefab_dir.glob("Level*.prefab"),
key=lambda x: int(LEVEL_RE.match(x.name).group(1)), # type: ignore
):
m = LEVEL_RE.match(p.name)
if not m:
continue
lid = int(m.group(1))
index[str(lid)] = f"{PREFAB_DIR_NAME}/Level{lid}"
if dry_run:
print(f"index: {len(index)} entries")
return
index_path.write_text(json.dumps(index, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
print(f"updated {index_path} ({len(index)} entries)")
def main() -> None:
ap = argparse.ArgumentParser()
ap.add_argument("--project", type=Path, default=Path(__file__).resolve().parents[1])
ap.add_argument("--dry-run", action="store_true")
args = ap.parse_args()
project = args.project
prefab_dir = project / "assets" / "resources" / PREFAB_DIR_NAME
db_path = project / "assets" / "level-data" / "levels-database.json"
index_path = project / "tools" / "level-prefab-index.json"
if not prefab_dir.is_dir():
raise SystemExit(f"prefab dir not found: {prefab_dir}")
print(f"{'[dry-run] ' if args.dry_run else ''}Renaming prefabs in {prefab_dir}")
mapping = rename_prefabs(prefab_dir, args.dry_run)
print(f" mapped {len(mapping)} prefabs (1 -> {to_external_id(1)})")
migrate_levels_database(db_path, mapping, args.dry_run)
rebuild_prefab_index(prefab_dir, index_path, args.dry_run)
print("done.")
if __name__ == "__main__":
main()