Skip to content

feat: flag mods that ship outdated .vdata_c copies - #47

Merged
Slush97 merged 2 commits into
mainfrom
feat/vdata-check
Oct 4, 2026
Merged

Slush97 merged 2 commits into
mainfrom
feat/vdata-check

Conversation

@Slush97

@Slush97 Slush97 commented Oct 4, 2026 •

Copy link
Copy Markdown
Owner

A mod that ships a game .vdata_c (scripts/heroes.vdata_c, scripts/abilities.vdata_c) replaces the whole file in game, so anything Valve added after the mod was built is gone. This adds a check for that.

vpkmerge_core::check_vdata(mod, pak01) decodes each .vdata_c the mod ships and the game's copy as KV3 and diffs the trees. One VdataReport per entry:

  • missing: paths the game has and the mod lacks (what the mod deletes), shallowest first so a whole missing hero comes before its fields
  • extra: paths only the mod has (the game dropped them)
  • changed: leaf values that differ

Arrays count as structure. Equal-length arrays diff by index. When lengths differ, arrays of plain values are matched by value and reported as path[=value], because Valve inserts mid-array (the 2026-09-29 batch added EHeroSpiritLifestealEffectiveness inside every hero's m_vecDisplayStats). Arrays of objects report the tail as path[i].

Status is outdated (any missing or extra), modified (values only), current, not-in-game (compiler leftovers like panorama/image_compiler.vdata_c) or undecodable.

modified is not proof a mod is current: it can't separate the mod's intended edits from values Valve rebalanced since. That needs the build the mod was made against.

VdataChecker checks many mods against one pak: it opens the pak only once a mod actually ships a .vdata_c, and reads and decodes each game file once. check_vdata wraps it for a single mod.

CLI:

vpkmerge vdata-check --base <pak01_dir.vpk> <MOD_dir.vpk>... [--limit N] [--json]

--json prints the full untruncated reports. A mod that fails to open gets an error field and the exit code stays 0, so one bad file never hides the rest of a batch. Text mode exits non-zero if any mod failed to open.

Testing

  • 11 unit tests for the tree diff (incl. mid-array insertion, shrunk arrays, object-array tails), plus tests/vdata_check_live.rs against the real pak (gated on DEADLOCK_PAK). Both pass, along with clippy (lib/bins/tests) and fmt.
  • Ran it over the 68 mod VPKs in a real install plus a junk VPK: 0.1 s (was 2.5 s before the shared checker), exit 0, the junk file reported per mod. Two mods flagged: an emotes mod whose heroes.vdata_c predates the 2026-09-29 hero batch (104 missing, including Rat King's ability slots and the new lifesteal stat in every hero) and a Plague Doctor Graves skin missing 9 whole heroes.
  • Linux CI is red on pre-existing morphic pedantic lints (kv3/patch.rs, reader.rs, rewrap.rs, model/glb.rs), same as main since fix: open VPKs that omit the optional v2 MD5 section #45. Nothing in this PR trips clippy.

Notes

  • Grimoire uses this to flag outdated mods on the Installed page (feat(installed): flag mods that ship outdated vdata grimoire#419). It needs a vpkmerge release with this subcommand, then a pin bump in grimoire's scripts/fetch-vpkmerge.mjs.
  • This is the core + CLI part of d312f40 on the local mcp-server branch. That commit also wires the check into the MCP inspect_mod tool. That part stays with the MCP server work, since vpkmerge-mcp isn't on main.

A mod that ships a game .vdata_c overrides the whole file, so anything
Valve added since the mod was built is deleted in game. check_vdata diffs
each shipped copy against pak01 as KV3 and reports missing, extra and
changed paths. Exposed as `vpkmerge vdata-check`.
- Arrays that grew or shrank now count as structure: plain-value arrays are
  matched by value (`path[=value]`) since Valve inserts mid-array, object
  arrays report the tail by index. The 2026 hero batch added a stat inside
  every hero's m_vecDisplayStats, which used to read as `modified`.
- `--json` exits 0 when a mod fails to open; the error stays in that mod's
  entry, so one bad file no longer discards the whole batch.
- VdataChecker opens the base pak once, only when a mod ships a .vdata_c,
  and reads and decodes each game file once. 68 mods: 2.5s to 0.1s.
- A failed entry read is reported per entry instead of aborting the mod.
@Slush97
Slush97 marked this pull request as ready for review October 4, 2026 10:48
@Slush97
Slush97 merged commit dea597f into main Oct 4, 2026
3 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant