Clean actor runtime baseline after motion fix
Some checks failed
ci / headless-e2e (push) Has been cancelled
runtime-self-hosted / runtime-ci (push) Has been cancelled

This commit is contained in:
server
2026-04-14 19:22:49 +02:00
parent 15c10c9a6f
commit 65a8e243d3
4 changed files with 32 additions and 21 deletions

View File

@@ -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.

View File

@@ -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`

View File

@@ -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",

View File

@@ -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