From 65a8e243d33c86d9f796ffb116e88919ceeb0a32 Mon Sep 17 00:00:00 2001 From: server Date: Tue, 14 Apr 2026 19:22:49 +0200 Subject: [PATCH] Clean actor runtime baseline after motion fix --- docs/migration.md | 20 +++++++++----------- docs/testing.md | 5 +++-- known_issues/runtime_known_issues.json | 8 +------- scripts/validate_actor_scenarios.py | 20 +++++++++++++++++++- 4 files changed, 32 insertions(+), 21 deletions(-) diff --git a/docs/migration.md b/docs/migration.md index 6ec256f..870de9d 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -165,17 +165,15 @@ It also now includes an actor/content validator: - `scripts/validate_actor_scenarios.py` -On the current real client runtime, the full actor validator reports five data -issues that look like pre-existing content inconsistencies rather than `.m2p` -loader regressions: +The current actor validator now resolves real `MotionFileName` targets from +`.msa` files instead of assuming every motion uses a same-name local `.gr2`. +That removed the old false positives from redirected actor motions, and the +remaining `orc_lord` mismatch was fixed in the runtime assets by correcting +`30_1.msa` to point to `30_1.GR2`. -- `Monster/misterious_diseased_host` missing `25.gr2` -- `Monster/skeleton_king` missing `24.gr2` -- `Monster/thief2` missing `03_1.gr2` -- `NPC/christmas_tree` missing `wait.gr2` -- `NPC/guild_war_flag` missing `wait.gr2` +On the current real client runtime, the actor validator now passes cleanly. -The expanded validator did not find additional breakage in: +It still verifies: - `.msm` base model references - `.msm` effect script references @@ -205,11 +203,11 @@ previous cross-pack and wrong-path sound references were cleaned up in the runtime assets, and the last remaining `combo7.wav` issue was resolved by aligning `combo_07.mss` with the byte-identical `combo_08` motion variant. -Those current actor and effect findings are also recorded in: +The current effect findings are recorded in: - `known_issues/runtime_known_issues.json` -The current audio findings are recorded there as well. +Actor and audio are currently clean in that baseline. That file is now the shared runtime baseline used by the validators and the aggregated release gate. diff --git a/docs/testing.md b/docs/testing.md index 0289696..3c0b085 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -208,7 +208,8 @@ python3 scripts/validate_actor_scenarios.py \ This validator checks local actor integrity for `Monster`, `NPC`, and `PC`: - `motlist.txt` motion files exist -- each motion has a paired `.gr2` in the same actor directory +- each motion resolves to a valid `.gr2`, including redirected `MotionFileName` + targets in `.msa` - `.msm` base model targets resolve against the runtime asset set - `.msm` effect script targets resolve against the runtime asset set - `.msm` default hit effect targets resolve against the runtime asset set @@ -275,7 +276,7 @@ Strict behavior: Current baseline on the real runtime: - `world`: `0` -- `actor`: `5` +- `actor`: `0` - `effect`: `12` - `audio`: `0` diff --git a/known_issues/runtime_known_issues.json b/known_issues/runtime_known_issues.json index 9403c9a..c4ab61e 100644 --- a/known_issues/runtime_known_issues.json +++ b/known_issues/runtime_known_issues.json @@ -1,12 +1,6 @@ { "world": [], - "actor": [ - "actor:paired_model:ymir work/monster/misterious_diseased_host:25.gr2", - "actor:paired_model:ymir work/monster/skeleton_king:24.gr2", - "actor:paired_model:ymir work/monster/thief2:03_1.gr2", - "actor:paired_model:ymir work/npc/christmas_tree:wait.gr2", - "actor:paired_model:ymir work/npc/guild_war_flag:wait.gr2" - ], + "actor": [], "effect": [ "effect:reference:ymir work/effect/background/moonlight_eff_bat.mse:ymir work/effect/pet/halloween_2022_coffin_bat_01.dds", "effect:reference:ymir work/effect/background/moonlight_eff_bat.mse:ymir work/effect/pet/halloween_2022_coffin_bat_02.dds", diff --git a/scripts/validate_actor_scenarios.py b/scripts/validate_actor_scenarios.py index e5fc966..acdd566 100755 --- a/scripts/validate_actor_scenarios.py +++ b/scripts/validate_actor_scenarios.py @@ -15,6 +15,7 @@ BASE_MODEL_RE = re.compile(r'^BaseModelFileName\s+"([^"]+)"', re.IGNORECASE) EFFECT_SCRIPT_RE = re.compile(r'^EffectScriptName\s+"([^"]+)"', re.IGNORECASE) DEFAULT_HIT_EFFECT_RE = re.compile(r'^DefaultHitEffectFileName\s+"([^"]*)"', re.IGNORECASE) DEFAULT_HIT_SOUND_RE = re.compile(r'^DefaultHitSoundFileName\s+"([^"]*)"', re.IGNORECASE) +MOTION_FILE_RE = re.compile(r'^MotionFileName\s+"([^"]+)"', re.IGNORECASE) @dataclass @@ -110,6 +111,15 @@ def parse_motlist(path: Path) -> list[str]: return motions +def parse_msa_motion_file(path: Path) -> str | None: + for raw_line in path.read_text(encoding="utf-8", errors="ignore").splitlines(): + line = raw_line.strip() + match = MOTION_FILE_RE.match(line) + if match: + return match.group(1) + return None + + def parse_msm_references(path: Path) -> tuple[str | None, list[str], list[str], list[str]]: base_model: str | None = None effect_scripts: list[str] = [] @@ -146,6 +156,13 @@ def validate_actor_dir(pack: str, pack_dir: Path, actor_dir: Path, asset_index: if not msa_path.is_file(): missing_msa.append(motion) continue + motion_file = parse_msa_motion_file(msa_path) + if motion_file: + resolved_motion = normalize_virtual_path(motion_file) + if resolved_motion not in asset_index: + missing_gr2_for_motions.append(resolved_motion) + continue + gr2_name = Path(motion).with_suffix(".gr2").name if not (actor_dir / gr2_name).is_file(): missing_gr2_for_motions.append(gr2_name) @@ -234,7 +251,8 @@ def main() -> int: failures.append(message) issue_map[issue_id] = message for gr2_name in check.missing_gr2_for_motions: - issue_id = f"actor:paired_model:{check.actor_dir}:{gr2_name.lower()}" + normalized_gr2 = normalize_virtual_path(gr2_name) + issue_id = f"actor:paired_model:{check.actor_dir}:{normalized_gr2}" message = f"{check.actor_dir}: missing paired model {gr2_name}" failures.append(message) issue_map[issue_id] = message