Deadlock Mod Manager
Developer Documentation

Mod compatibility repairs

Deterministic checks and reversible repairs for compiled mod resources

The desktop manager compares mod resources with the locally installed Deadlock files and packs verified repairs into a separate overlay. These checks run in the Rust backend in release builds. They require no AI agent, network service, or diagnostic tools on the user's computer. The player-facing behavior is described in Fixing Mods After Game Updates.

Per-mod health check

mod_manager/mod_health.rs defines the per-mod status, and manager/localization/health.rs runs the check. Every mod in the profile VPK manifest whose files exist is checked, enabled or disabled, including local imports. Each mod is analyzed on its own against the installed game with LocalizationOverlayPlan::build_standalone. A mod without resources the rules can check is notCovered. A mod that cannot be analyzed is logged and left without a status.

The frontend ModHealthRenderer requests checks once per profile after start, for mods whose installed files change, and as a forced recheck of every mod when the game build differs from the build of the previous check. Progress is sent as mod-health-progress events.

StatusConditionBadge
worksNo repairs and no findings that need an updateNone
fixableRepairs available, nothing left after them, no choice madeCan be fixed
partlyFixableRepairs available, findings left after them, no choice madeCan be partly fixed
fixedRepairs available and the player fixed the modFixed for this patch, or Partly fixed for this patch when findings remain
originalFilesRepairs available and the player chose original filesOriginal files
needsUpdateFindings that need an update and no repairsNeeds an update for this patch
notCoveredNo checkable resourcesNone; the date-based outdated badge still applies

On installed mods, the date-based outdated badge appears only when the status is notCovered or no result exists yet. Browse pages keep the date badge.

A mod needs an update when it has asset warnings other than missing textures, or any game-data or localization parse warning of its own. Warnings on models that nothing loaded references are left out. Missing textures are counted as warnings and shown in review, but do not make a mod need an update. Uncertain baselines and stale localization snapshots still pause launch for review (see below); they describe how a mod's edits are merged rather than a defect, so they do not change the status.

Each repair and remaining finding maps to one player-visible symptom: camera and aim; animations and movement; colors and textures; model; abilities, stats and text; and model data issues. Structural model faults the engine could reject, and missing references without a known fallback, map to model data issues (crashRisk). They are not confirmed to crash the game. A status carries the symptoms its repairs fix and the symptoms they leave.

Results are cached per profile in mod-health.json in the app's local data directory. An entry is keyed by the size and modification time of each of the mod's VPKs, the game build, REPAIR_REVISION, and HEALTH_RULES_REVISION in mod_health.rs. File locations are not part of the key, because enabling, disabling, and reordering rename a mod's VPKs. A change to any part of the key makes the entry stale, so the mod is checked again. Increment HEALTH_RULES_REVISION when status or symptom rules change. Results from the launch analysis of all fixed mods together take precedence over standalone results for the same files. Results are never uploaded or shown to other users.

Fix choices

Each mod has one of three choices, stored per profile in its profile VPK manifest entry:

  • No choice, the default for existing and new mods. Repairs are not applied.
  • Fixed: compatibilityEnabled is set.
  • Original files: compatibilityOptedOut records that the player chose the mod's original files. Bulk and automatic fixes skip these mods.

fix_mods sets the fixed choice for exactly the listed mods, clearing any earlier choice of original files, and prepares the overlay the way launch does. It returns applied, or reviewRequired when something still needs review; the choices stay saved either way. Fix N mods in the banner and the launch preflight skip mods whose remaining symptoms include model data issues, so the player sees those issues before fixing such a mod; Fix all in the details list, where the issues are shown, includes them. Fix N mods in the banner, Fix and Fix all in the details list, Fix for this patch in the right-click menu and on the mod page, and the launch preflight all call it. Use original files calls set_mod_compatibility_for_mod with enabled: false, which records the original-files choice.

Disabling or reordering a mod keeps its choice. When the set of fixed mods changes, the manager deletes the active profile's overlay and receipt, clears the cached plan, and refreshes game search paths. Deadlock must be closed to change these choices or apply fixes. Review remains available while it runs.

Before a modded launch, the frontend collects enabled mods whose status is fixable or partlyFixable. While Fix mods automatically after game updates (autoFixMods, on by default) is on, they are fixed without a prompt. With it off, a prompt offers Fix and launch or Launch anyway; Launch anyway starts the game without applying the pending fixes. A failed fix is reported and launch continues with the original files.

Analysis indexes every enabled mod for load order and dependency context, but only fixed mods contribute repair candidates or baseline history. Models, meshes, skeletons, and other binary assets follow the effective game load-order provider. A fixed winning skin can be repaired even when a shadowed skin is not fixed. Shadowed models never contribute repair candidates or replace the winning skin. This also applies to multiple variant VPKs within one mod: the first effective archive supplies the model; duplicate paths are not merged. Overlapping models produce a review warning naming the active archive and the other providers, because their non-overlapping animation or accessory files can still mix. Disable unused variations or competing skins when those files cannot work together. If the winning provider is not fixed, its resource stays protected and launch uses the original files.

Localization and mergeable game-data tables combine providers, so those entire paths remain protected when any contributing mod is not fixed. A material recipe requiring a protected output declines as a whole. Export checks all output paths before staging files.

Review before launch

A mod's own asset and data findings describe that mod whether or not it is repaired, so mod health reports them and they never pause launch. requires_review pauses launch only for:

  • Conflicts between enabled mods: localization and compiled game-data conflicts, competing definitions, and overlapping models, including variants of the same mod
  • Baselines that are not an exact historical match
  • Mods whose files are all missing, localization files that could not be parsed, and stale localization snapshots

Unmanaged VPKs, protected and shared resources, and every other finding are listed in review when it opens, without pausing launch on their own. When review is needed, launch caches the plan it built so the dialog does not rebuild it.

Approving the review writes the reviewed plan and retries launch once; closing the dialog cancels launch. Warnings are preserved and do not imply that every mod has been repaired. The backend rechecks the receipt and current inputs on the retry. Further changes require another launch attempt rather than repeatedly prompting or bypassing review.

If no enabled mod is fixed, preparation is bypassed and existing repair packages are ignored. Saving mod order alone does not trigger repairs. The IDs of fixed mods form part of the receipt/cache key and review fingerprint, so changing those choices rejects old review choices.

Automatic repairs

Authored animation clips after skeleton changes

Authored NM clips are checked against their effective skeleton, including mods that already have animation bindings. A track-count mismatch is reported instead of silently passing the model. A mod-provided skeleton retains its own contract.

For a vanilla skeleton with inserted bones, the manager reconstructs the old sampling layout from an effective model in the same archive. The remaining named bones must account for every authored track, and their parent chains must agree after collapsing inserted identity helpers. A non-additive clip must identify at least eight bones and at least half its tracks through unique static bind poses, including translation, rotation, and scale. Those named anchors constrain hierarchy traversal within the two sampling LODs. Unconstrained sibling branches retain the authored model's order. This supports reordered limbs and clothing, rather than assuming that removing new names recovers the old track order. The contract is scoped to that archive, skeleton path, and old track count; sibling additive clips use the same contract. This reconstruction depends on unchanged LOD membership and the authored hierarchy; it is not a general recovery of any historical skeleton.

The repair reorders tracks and their compressed frame segments together, updates read offsets, and inserts static reference-pose tracks (zero deltas for additive clips). It retains original motion values, frame offsets, root motion, events, graphs, skeletons, and model bind frames. Nonidentity inserted ancestors, insufficient pose evidence, truncated streams, secondary animations, and bone-indexed sampling channels prevent repair. Per-mod repair summaries group files by repair type, with expandable paths for detailed inspection.

Baseline evidence and local history

Historical matching has two distinct jobs: finding plausible game builds and proving that a field was unchanged. Approximate float fingerprints can help find a build, but they cannot authorize discarding a numeric difference. New history stores exact row and field fingerprints separately from discovery fingerprints. The bundled older history remains readable; its floating-point values cannot prove an unchanged field or authorize repairs that need that proof.

The review reports matched, approximate, ambiguous, and unavailable baselines. Equally good candidate builds are accepted only when they imply the same authored changes. Conflicting candidates leave historical rebasing disabled. Missing or uncertain history preserves differences from today's game and requires review; a close historical match is an inference, not a declaration from the mod author.

When relevant mods are analyzed and steam.inf identifies the game build, the manager records fingerprints of the installed vanilla VData tables in an atomic cache scoped to that installation. It retains the latest 64 distinct snapshots per table and compacts older deltas without losing the retained snapshots. This learns future patches locally without shipping another embedded history update. It cannot recover a build the installation never observed. Corrupt history is ignored and rebuilt from the currently installed tables.

Hero and ability data

VData tables are merged first, then checked together using the selected conflict resolutions. Changing a choice refreshes the preview before apply. The dependency pass follows bound hero abilities, ability triggers and weapon/slide references, plus _base and _multibase inheritance within a table. Native _class names, model paths, and CSS classes are not treated as table dependencies.

An unchanged historical definition absent from today's game is usually discarded. If a retained mod row still needs it, the manager can keep that definition after migrating known enum aliases. Its native class must still occur in the installed table, its enum strings must be observed at matching property paths in current data, and all enabled copies must agree on both runtime values and encoding. Every known dependency in the retained chain must resolve. A failed chain produces a finding without adding part of the chain. Inheritance loops prevent repair; mutual ability triggers can be valid and are allowed. Current game definitions and authored mod definitions take precedence over historical copies.

New and retained definitions can also inherit current settings from a declared _base or _multibase template. The template must be the only eligible parent with the same native class, must exist in today's game, and must match an unchanged historical source row. Differences between the old template and its child are replayed onto the current template with their original encoding. This carries new defaults forward and drops removed inherited fields while preserving distinct child settings. It does not delete arbitrary unfamiliar fields.

If another selected mod changes the template, the new child's update is skipped and its original settings are used. Retired dependency repair is rejected in that case. Selecting vanilla for the template can make the repair eligible again. Ambiguous parents and inherited native classes not observed in current data are reported. A class missing from current VData may still exist in the engine; the manager does not guess a replacement class or claim the game code was restored.

New custom heroes missing m_eHeroDevelopmentState can inherit only that field when current heroes with the same model, class, and legacy visibility flags agree on its value and wire encoding. Explicit visibility settings, disabled heroes, and development heroes are preserved. Existing hero-clone rebasing continues to require an unambiguous unchanged historical donor. Missing fields without enough evidence remain findings rather than guessed defaults.

The report distinguishes restored visibility fields, retained required definitions, missing definitions, and repairs rejected because of competing data, unsupported classes/enums, or inheritance cycles. Unresolved references already present in vanilla are excluded from these mod findings. Unreferenced historical definitions stay discarded. Original archives are never changed.

Hero level-up modifier maps are checked against the union of keys in the installed hero table. Keys absent from that observed domain are omitted from the overlay, with matching encoding entries removed. Supported values and cosmetic fields remain authored. Every omission appears in the repair report; nonzero omissions also require author review because no replacement or unit conversion is inferred. This rule applies only to CitadelHeroData_t.m_mapStandardLevelUpUpgrades, whose native keys are modifier enums. It does not prune open string or ability maps. The observed domain is not a complete engine enum registry: a valid key unused by all current heroes may be omitted, so this check does not establish full schema compatibility. Runtime testing of legacy Sliver exposed this gap when a retired modifier key caused 55 hero definitions to fail parsing despite a valid binary.

These checks cover the reference fields listed above; they are not a complete game schema or proof of in-game behavior. Adding a field to vanilla does not prove that every mod needs it. New reference fields and schema migrations need evidence and regression coverage before becoming automatic rules.

Models and animation skeletons

Animation skeletons can inherit today's vanilla resource when the surviving bone names, named parents, reference poses, masks, and existing metadata agree. Compiler rounding, quaternion sign changes, index reordering, and removal of terminal _end, _end_L, or _end_R leaf bones are supported. A current attachWorld leaf on an unchanged root and retired *_TWIST leaf helpers whose parents match their name stems are also supported. The retired scapulaAimUp_JNT helper is recognized only as a leaf under spine_3. Retired knee_helper_L and knee_helper_R leaves are recognized only beneath the corresponding leg_upper_L or leg_upper_R. Removed helpers cannot have children, and their parents must survive. Arbitrary added or removed custom bones prevent replacement.

When only mask settings differ, the manager can rebase onto the current sampled layout while preserving the mod's named primary weights and extra masks. It drops retired bone references, inherits weights for newly added bones, and adds masks required by the current game. Changed weight-list metadata and secondary masks prevent this repair. If several mods provide a skeleton, their repairs must produce identical bytes; competing custom settings remain unresolved.

A small pose-drift rebase can also update the sampled layout while keeping the mod's exact named reference poses and their encoding. Local translation and scale differences are limited to 0.001; normalized quaternion rotation differences are limited to 0.0002 radians (about 0.011 degrees). Model-space translations must fit the displacement bound from the named parent's translation, rotation, scale, and the child offset. Larger pose changes, invalid quaternions, unrelated model-space movement, and changed hierarchy prevent automatic repair. The narrow bounds select a repair; they do not authorize overwriting the mod's poses.

When those narrow checks reject a sampling change, the manager can use the mod's full model rig as additional evidence. It requires one unambiguous model at a matching game path, owned by the same source archive, with the same active NM animation graph references. Custom animation data in the source mod prevents this broader migration. Effective overrides from other mods are checked against the model's graph closure and their clips' skeleton references. Unrelated NM clips and nested graphs are allowed only when those checks prove they do not affect the candidate skeleton. Event graphs without child resources require an explicit, verified skeleton binding. A game graph with that binding may reference an absent optional NM clip only when no enabled mod provides that clip. Missing graphs, missing override resources, graph cycles, unsupported animation formats, and exhausted traversal limits prevent migration. Unrelated legacy animation graphs cannot replace these active NM graphs. Structural layout repairs also require this dependency proof when active animation overrides are present. After this proof, structurally equivalent rigs still inherit the exact current resource bytes rather than undergoing an unnecessary model-backed rewrite.

This model-backed rebase supports three structural patterns:

  • Retired sampling branches with no surviving descendants. Their deformation bones must remain unchanged in both full model rigs, or be virtual samples absent from both. The manager never removes bones from the model itself.
  • Added leaf controls absent from the old model, with zero bone sphere radius and no use in the current embedded meshes, attachments, hitboxes, or constraints. Their retained parent bind and sampled world poses must agree. External meshes, missing mesh evidence, or nonempty constraints prevent this control migration.
  • Larger authored reference-pose differences that match each resource's own model bind poses. Each sampled world pose must agree with its local and parent transforms, both before and after repair. The mod's exact named poses and mask weights survive; the current game supplies the sampling layout and new controls.

Model-backed migrations still reject changed surviving parents, unsupported mask metadata, and competing model or skeleton sources. An available model is evidence for these checks, not permission to replace a custom deformation rig.

Models missing the complete camera interface can inherit today's camera metadata and attachment points when every current bone retains its parent and reference pose. After a verified NM binding migration, the same bounded uniform-size proof may be applied to every current model bone before adding camera attachments. Additional authored branches and reordered indices are allowed; all source bones and mesh mappings remain intact. Models with authored camera attachments but missing CitadelCameraSettings_t can inherit numeric camera offsets without changing their rig or attachments. Schemas containing resource, bone, or attachment references require further evidence and are not copied. Existing camera fields, attachments, mesh buffers, animation references, and authored poses are preserved. Unrepaired missing camera fields are reported. Conflicting model sources keep addon precedence and are not automatically repaired.

Models with the known pre-update root_motion attachment frame can also migrate existing camera and aim points. Both standing_pivot and crouching_pivot must match the old rotation, and the installed game must supply both pivots in the new frame. Both models must have an unparented root with matching scale and rotation within 0.001 radians (about 0.057 degrees), allowing small exporter differences. Root translation may differ; the authored bind pose remains unchanged. This tolerance applies only to camera migration, not skeleton or animation rebasing. The inverse basis rotation is applied to the mod's own offsets and rotations, preserving custom positioning instead of replacing it with vanilla values. Every embedded mesh block is checked; inactive influence slots, other bone attachments, model metadata, geometry, and animation bindings remain unchanged. Reordered and added custom bones do not prevent repair when the root dependency still matches.

Mixed pivot frames, blended influences, incompatible root axes, unsupported encodings, and failed preservation checks produce review warnings. Already current or unrecognized authored rotations are preserved. This rule does not migrate onto a pre-update game installation. Existing compatibility receipts are invalidated when these repair rules change.

Rebuilt v4 meshes expand compact unsigned zero/one encodings into UINT32 lanes while keeping their values and flags. Copying those compact tags from current game attachments made the engine reject Skeleton Talon's owl mesh with Unsupported primitive value type 27, displaying an ERROR model and breaking its camera. Repair revision 28 rebuilds affected overlays. The installed Shiv Josuke and Skeleton Talon regression verifies their combined repair export and unchanged geometry; in-game acceptance remains a separate check.

The shared resource rebuild function validates replacement v4 KV3 payloads for every block kind, including mesh MDAT and control CTRL blocks. Its target format check rejects newer-only wire types even when the decoder understands them, including subtypes on empty typed arrays. Untouched legacy blocks remain opaque and are copied exactly. The opt-in corpus runner independently decodes every versioned KV3 block in exported resources, records block counts and failures per mod, and can retain overlays with DMM_COMPAT_EXPORT_ROOT for external inspection. Successful rebuilding does not establish in-game acceptance.

Hero model relocation follows the current heroes.vdata_c model bindings. It uses previously observed bindings for the same hero definition to identify an old primary model, rather than matching display names, mod titles, or filenames. The bundled archive includes all 43 model bindings from a pinned November 2024 game definition; its source commit, date, URL, and SHA-256 are recorded in src-tauri/assets/hero-model-bindings.json. Model compatibility analysis also records the installed game's hero table, even when no mod edits that table. Observed string bindings survive the 64-build history compaction and are stored in the v3 history format; older history formats remain readable.

A unique current target is required. Competing models, opted-out target paths, and enabled hero definitions that change the target model block relocation. Unrelated hero-definition edits with an unchanged model binding do not block it. Legacy models pass through the same sampled-rig, animation, camera, mesh mapping, and resource checks used for repairs at existing paths. Already valid AG2 models can move only after proving that their current graph bindings and sampled rig match. The output retains the mod's material bindings and authored mesh data; it does not restore the vanilla appearance. Failed preservation checks produce a model-mapping warning and no relocated output.

The Gold Pocket relocation regression and the older Ivy fixture in the fifteen-mod mixed tests both migrate through this same code; neither requires hero-specific dispatch.

This is a generic migration for historical primary hero-model bindings. Unknown historical bindings, material-only migrations, HUD style updates, and arbitrary asset schema changes still need separate evidence and adapters. A valid export is not an in-game acceptance test.

Partial interfaces missing camera attachments can inherit those attachments when the rigs are equivalent. Required attachment names are read from the current model's AttachmentCameraData, including additional poses and alternate forms. If the current attachments need a missing control bone, the manager can append an isolated root leaf with zero deformation bounds to a single embedded mesh. Its current bind pose, flags, and inverse bind matrix come from the game model. All original bone indices and remapping entries remain unchanged. Graph and sampling skeleton bindings must match. The sampling skeleton must resolve from the game; overrides of that skeleton or the model's graph closure prevent extension. Overrides elsewhere in the library can coexist when the shared dependency proof verifies that they sample another skeleton. Unknown animation formats and animations supplied by the model's own mod still prevent extension. Constraints, external meshes, dependent deformation branches, and unsupported bone schemas prevent this extension. This rule uses asset structure and matching paths for every hero and alternate form, without hero-specific dispatch.

Model checks include m_animGraph2Refs.m_hGraph and the legacy anim_graph_resource in embedded metadata, including mods with no separate skeleton file. When both NM binding arrays are absent or empty, the manager can inherit the same model's installed-game bindings after verifying the current NM sampling skeleton. Every sampled bone and its complete ancestor chain must retain its named parent. Reference poses must match, or all sampled local translations must fit one uniform size change within 2%, with at most 0.002 units of residual per coordinate. Rotations still match within 0.00001 per quaternion component, allowing sign reversal; local scales retain the existing 0.001 tolerance. This proof does not modify authored poses. Bone names must match unambiguously, including case, and all referenced graphs and the single sampling skeleton must resolve from the game. Animation overrides that enter this model's graph closure or sample its skeleton, animations supplied by the model's own mod, partial authored bindings, ambiguous models, and nonuniform pose changes prevent this repair. Overrides for other heroes coexist once the shared dependency proof shows they stay outside this model's graphs and skeleton (repair revision 36). An absent sample may be appended only as an identity leaf directly under an existing root, with zero deformation sphere in the current model. Both rigs must use the known seven-array bone schema, and the source cannot use external meshes. Existing indices, mesh data, and remapping entries remain unchanged. Other missing sampled bones prevent repair. Skipped migrations report the failing bone or check.

Binding restoration preserves the complete source deformation rig, material groups, mesh mappings, stream buffers, and embedded legacy animation blocks. The new resource references use verified Source 2 IDs, and the rebuilt DATA is checked for values and wire encoding. Camera repair consumes that result, so the two edits compose on one model. Binding restoration also adds missing current pawn-control metadata and removes unavailable legacy graph selectors at the root and in CCitadelHeroModelGameData_t. Null selectors are treated as absent. Existing AG2 pawn/UI selectors can move to canonical bindings only when their resource path matches the current binding for the same identifier. An available legacy graph or a differing available modern graph prevents this migration. Authored control settings and particle fields survive. Include-model dependencies and embedded legacy clips remain intact and can still produce missing-resource warnings; restoring NM bindings does not establish that every legacy animation remains usable. File-contract tests cannot prove live animation or facing behavior. When adding camera attachment points, valid authored points already present are preserved, missing points are appended, and duplicate or invalid camera points prevent the repair. This lets partial camera interfaces migrate without replacing authored attachment transforms.

Missing graph references remain warnings outside this proven migration. Reviews with unresolved warnings do not show a green ready status merely because no mods conflict.

The rules operate on resource structure rather than mod IDs or hero names. The installed game provides the current reference data. Unknown differences do not become speculative repairs.

Generalization requirements

A repair is selected by resource structure and a matching installed-game resource path. Mod IDs, display names, authors, hero names, and archive filenames do not select a repair. The IDs and filenames in the report identify the affected input. A custom resource with no matching game counterpart has no baseline for these repairs.

Bone names such as attachWorld, scapulaAimUp_JNT, and knee_helper_L are narrow game conventions, not evidence that any added or removed leaf is safe. The twist rule supports any bone stem when the retired leaf is named <parent>_TWIST with an optional numeric suffix and its actual parent matches that stem. Broader helper recognition needs additional evidence about deformation and animation dependencies, as in the model-backed sampling checks above.

To extend a rule:

  1. Capture a reproducible incompatibility and compare the mod with the installed game or a known historical game resource.
  2. Identify which structure changed and which authored data must survive.
  3. Add regression cases with unrelated mod IDs, resource paths, and bone names, plus cases that preserve custom poses, hierarchy, metadata, and conflicting mod settings.
  4. Verify the compiled overlay and the original in-game symptom before enabling the rule automatically.

The skeleton pipeline tests exercise VPK scanning, repair selection, diagnostics, overlay packing, and decoding with unrelated robot and creature fixtures. Their source VPKs must remain byte-identical. Separate tests protect custom joints, poses, masks, and unsupported metadata. Structural findings without such a verified transformation remain diagnostics rather than automatic repairs.

Compatibility report

The compatibility review in Mods Library shows the affected mod and resource for each planned repair or finding. The backend also checks skeleton parent ranges and cycles, duplicate bone names, reference-pose dimensions, model bone-remapping indices, attachment influences, hitbox bone references, and direct model resource dependencies. Embedded mesh checks use the full model skeleton because each mesh may contain only a subset of its bones.

Required resource names are resolved against the game, core archives, and every enabled mod. Missing references may have engine fallbacks; a finding is not proof of a crash. Unreadable resources and skeleton layouts without a verified repair are reported and left unchanged. Users can review the findings and continue, or disable an affected mod and contact its author.

These checks do not validate every vertex weight, shader, animation clip, or game behavior. New repair rules still need a reproducible game test and regression coverage before they can be applied automatically.

The resolver indexes the supplied enabled archives once, preserving their ordered first-provider precedence. Installed citadel/core loose files and top-level VPK archives provide game baselines. This scope does not emulate arbitrary workshop, map, server, or language search-path overrides. Entry extraction includes VPK preload prefixes, including entries stored entirely in the directory tree, and reads payloads lazily. Repeated reads share bytes within the plan. Successful KV3 parses use an exact-content LRU cache limited to 16 entries and a conservative 32 MiB allocation charge; oversized trees are parsed without being retained. Loose file presence is stamped, with content hashes for supported compiled baseline formats and the language files supplied by enabled mods. Other languages do not need a content hash. Directory aliases are followed with cycle guards.

Material migrations

Material dependency checks cover enabled compiled materials, including those without a migration recipe. Missing textures and unreadable material resources appear in compatibility review. A successful parse proves neither that a current model uses the material nor that its shader renders the intended effect.

For a material with a current counterpart, a shared interface comparison reports missing named texture slots, enabled authored shader features that differ from the current reference, changed shaders, and ambiguous parameter tables. Texture slot comparisons require matching enabled feature values, treating omitted features as disabled. Different feature variants can intentionally omit detail or outline textures. Texture values may differ without a warning when their slot names remain present. These findings do not replace any authored values or produce material assets: an extra feature can be intentional, and a current material is not the full shader schema. For example, Old Mina's old eye material enables F_USE_STATUS_EFFECTS_PROXY while today's counterpart does not. The renderer requests g_tAltColor_Reveal, but neither material defines it. The scanner reports the unverified feature interface; it does not invent a texture binding or claim to know every runtime requirement. Review warnings remain distinct from successful gameplay tests.

The first reviewed migration, minion-static-glow-v1, restores the legacy candle wax color mod on current minions. It identifies the four authored material roles by resource path, resolves current model and role bindings from npc_units, and checks actual embedded draw calls. It transfers only the reviewed static emission scale, albedo factor, and tint to the current shader interface. Current texture parameters, shader features, rig indices, and geometry remain intact.

Amber wax, Sapphire shared body/head material, and Sapphire melee head material are separate targets. Sapphire head draw calls are isolated across the three live health groups and every registered LOD before applying a white emission mask. Body and fractured meshes retain their original bindings. Missing mesh groups, external or incomplete selected meshes, unknown health groups, authored material variants, conflicting providers, or another repair owning the same output cause the complete migration to be declined with a finding.

Generated materials live under dmm/compatibility/materials/<content hash>/. Their RERL IDs use MurmurHash64B with seed 0xEDABCDEF, verified against installed engine code and 276 installed resource references. Generated dependencies must resolve through the planned overlay or effective game/mod resources. Bundled default textures may differ in compiler provenance only when their DATA header, streamed pixel payload, resource version, and YCoCg interpretation match exactly.

This recipe establishes one reviewed correspondence. It does not infer arbitrary material renames or all authored edits from filename similarity. Additional resource families need evidence-backed mappings and game verification.

Regression corpus

Prepare pinned older GameBanana fixtures with:

bun apps/desktop/scripts/compatibility-corpus.ts /tmp/dmm-compatibility-corpus
DMM_REAL_CITADEL=/path/to/Deadlock/game/citadel \
  DMM_COMPAT_CORPUS=/tmp/dmm-compatibility-corpus/cases.json \
  DMM_COMPAT_REPORT=/tmp/dmm-compatibility-corpus/report.json \
  cargo test --manifest-path apps/desktop/src-tauri/Cargo.toml --lib \
  run_obsolete_mod_corpus -- --ignored --nocapture --test-threads=1

The preparer requires 7z, verifies each download's pinned SHA-256, and keeps the assets outside the repository. It does not install or enable the fixtures. The runner analyzes each mod separately, exports any overlay, verifies its compiled DATA with an independent reader, checks source archives remain unchanged, and records resource categories, current hero model usage, repairs, warnings, and timing. Analysis or export failures fail the run after writing its report.

The corpus now pins fifteen source archives: the original five older models, Toon Bebop, Ivy Gardener, Bull Abrams, Kelvin Amazon Worker, TF2 Engineer McGinnis, Gold Pocket, Scaramouche Seven, Nightsisters Merrin Vindicta, Minimalist Health Bar, and Square Top Bar. Merrin uses only the cape-with-cloth variant; alternative skins and optional gun audio are excluded from that case. The mixed_corpus_respects_per_mod_selection_and_load_order opt-in test uses all fifteen together, with none, all, alternating, Toon-only, and all-except-Toon selected, in forward and reverse order. It checks that exports preserve resources owned by opted-out effective providers and protected merged tables, repair findings belong to selected mods, compiled outputs decode with an independent reader, and source VPK hashes remain unchanged. Use the same variables as above, a separate DMM_COMPAT_REPORT output, and that test name. The four added fixtures still have camera, retired animation, hierarchy, or missing dependency findings; a completed export check does not mean they have complete repairs.

Of the next five cases, Scaramouche produces a verified animation/camera model export. Gold Pocket retains unresolved legacy-resource findings. Merrin's initial coordinated rig repair was declined because the checker treated unused branches in the current NM skeleton as missing deformation bones. The two HUD mods supply twelve compiled styles whose paths still exist in the installed game. There is no generic compiled-style repair adapter, so their lack of findings is a coverage gap rather than evidence of compatibility. Scaramouche passed a user-reported in-game test with its generated overlay. Gold Pocket failed its user-reported appearance test: the current hero_synth definition loads models/heroes_wip/pocket/pocket.vmdl, while the mod replaces retained legacy models/heroes_staging/synth assets. A read-only traversal of the current model, mesh, material, and texture references found 46 appearance assets and no mod overrides on that chain. Asset existence alone therefore does not establish that a mod still affects the active hero. The generic relocation regression now produces the current model path, independently decodes the compiled output, and preserves its authored material bindings. The user confirmed that Pocket is gold in-game with the generated repair, with normal movement, abilities, and cameras during the ultimate and briefcase. This records a user-reported pass for the shared hero-model migration. Merrin's original model loads in-game, but the user reported missing animations, sliding movement, and a stuck camera. The shared unused-branch proof and sampled camera dependency composition now produce its animation/camera repair. The real-file regression verifies current bindings, skinning preservation and independent DATA/MDAT decoding. Its generated overlay is applied with repair revision 25, and its in-game retest remains pending. All ten fifteen-mod selection/load-order scenarios pass with this proof. The same code also enables older Kelvin and Abrams exports, whose runtime behavior remains unverified. The two HUD cases still require in-game tests. Individual runtime checks were not independently observed.

Synthetic regressions also exercise 64 selected localization mods and a selected mod alongside up to 64 opted-out mods. These checks establish file-level scope and merge behavior, not in-game acceptance of every combination.

The initial corpus contains Old Mina, Mo and Mo, legacy Sliver & Saint, Summer break Dynamo, and 2B Yamato. On build 6745, all five main model paths still match current hero definitions. Their models lack current animation graph or skeleton bindings, which are reported separately from retired resource references. Sliver & Saint's cat form passes sampled-rig proof and receives both NM bindings and camera metadata while retaining its custom bones and geometry. Sliver's main form and Old Mina also pass the bounded sampled-rig proof. Their generated overlays have passed user tests for animation, movement, turning, and cameras; Old Mina additionally passed shooting, abilities, and umbrella appearance checks. Mo and Mo, Dynamo, and 2B Yamato still fail bone/pose proof and retain actionable warnings. These three have not passed runtime testing. Sliver's copied hero table also contains retired ability slots for unrelated heroes. If a required definition cannot be recovered, an existing CitadelHeroData_t row can restore the installed game's binding at that slot, or remove a direct optional slot that the current row no longer declares. Other authored fields, including model paths, remain intact. This fallback runs only after dependency retention fails because a definition is unavailable. A new hero row with unavailable bound abilities is omitted when no other hero definition inherits it, and the omitted row is reported. Shared templates, inheritance references, competing definitions, unsupported classes, and unknown reference schemas keep their unresolved warnings. Each restored binding is reported as a repair; the unavailable old ability is not claimed to have been recovered. Dynamo's missing mask/normal compiler input CRCs differ from today's inputs, and its custom watergun mask has no current counterpart. These textures are not silently replaced with similarly named or default textures.

The ignored legacy_cat_bindings_and_camera_preserve_authored_geometry test checks the composed repairs against that pinned fixture and independently decodes the rebuilt model. Setting DMM_COMPAT_LIVE_OVERLAY to the prepared overlay VPK also compares its cat model byte-for-byte with the fixture pipeline, checks that the unsupported copied Tokamak row is absent, and verifies all exported hero ability bindings resolve or match a current native fallback. This optional check targets the isolated Sliver test setup on the recorded game build. Unverified fixtures explicitly record runtime: not-tested: binary round trips and reported repairs are not in-game acceptance or a coverage percentage. Runtime tests need documented launch, hero/ability, and visual checks for each fixture before a migration can be considered verified.

Coordinated deformation-rig migration

When restoring current AG2/NM bindings fails the authored-pose proof, an embedded model can use a coordinated rig rebase. This adapter matches bone names without case ambiguity, preserves existing bone indices and remapping tables, and adds only required current bones and ancestors. It does not import unrelated current cloth branches. Existing bones keep their authored model-space bind frames while parents are reconciled by name. Added controls inherit their current local frame from the retained authored parent. A model's mesh binding is distinct from the NM animation skeleton's reference pose: a mod can be compiled in a seated pose while the current reference skeleton stands upright.

Vertex/index buffers, mesh inverse binds, morphs, materials, attachments, hitboxes, embedded physics and legacy animation blocks remain unchanged. Original inverse binds must agree with the authored model rig. External meshes/physics and bone-indexed procedural drivers still require a separate adapter and decline.

The serializer normalizes compact numeric array tags only when an edit requires it, preserves flags, and verifies the resulting values and encodings after decode. The adapter also checks serialized mesh skinning transforms, attachment frames and hitbox shapes against the original before publishing any model repair. It requires the same skinning transforms under a shared animated world pose, not just matching identity transforms at each model's own rest pose. Current NM branches omitted by both the current render model and the authored model do not count as missing deformation bones. This exception requires a known-good unoverridden reference, a mapped ancestor and a self-consistent NM reference pose. Every descendant must be absent from both models; branches with a mapped descendant and unanchored NM roots are rejected. No hero or bone-name allowlist is used.

After a proven deformation-rig rebase, camera dependencies sampled by the current game NM skeleton may retain the authored bind frames: the animation supplies their canonical world poses by name. Their named ancestor chains must still match. Unsampled camera dependencies continue to require matching poses, even when they share ancestors with sampled camera bones. Camera migration verifies the actual camera influences and their ancestor chains rather than requiring unrelated cloth branches to match.

Dynamo's revision 20 repair was accepted in-game; its missing-texture findings remain unresolved. Mo's revision 20 repair passed rest-pose checks but failed in-game: Krill's seated mesh stretched when driven from the replaced standing bind frame. Revision 21 retains authored frames. The opt-in corpus regression samples two installed idle clips at three times and compares every skinned vertex against the original binding; the standing-frame counterexample must produce a large error for the fixture to qualify. Runtime confirmation remains required for Mo and 2B Yamato, and for Dynamo after this changed migration rule. The adapter has no hero-name or mod-ID repair rules.

Overlay lifecycle

Original mod VPKs are read, never patched. Repaired resources are packed into a separate dmm_localization_* overlay that precedes addons in the game's search paths. This is preparation before launch, not runtime code injection.

A compatibility.json receipt beside the overlay records:

  • The ordered enabled-mod inputs, selected compatibility IDs, and file stamps
  • Game/core VPK indexes and numbered data chunks, plus steam.inf
  • Relevant loose resources with content hashes, plus the observed-history digest
  • The repair-rule revision and overlay SHA-256
  • Reviewed conflict resolutions and the compatibility report

The receipt records the VData repairs and findings from the applied resolutions, rather than the initial load-order preview. Rebuilt compiled resources are decoded again and their full runtime values, format GUID, flags, numeric widths, typed arrays, and object order are checked against the intended result. The supported v5 auxiliary-array conversion is verified against the equivalent v4 encoding. Compatibility rebuilds reject duplicate DATA blocks and unrepresented stream tails instead of silently dropping data outside the resource's block table.

When any enabled mod is fixed, launch reuses an overlay only when its receipt, inputs, and output hash match. Changed inputs or missing/outdated receipts trigger analysis again. Verified repairs are automatic. Conflicts between enabled mods and repairs that need approval use the review flow. Previously reviewed choices can restore a damaged overlay only when its inputs still match; choices are not carried across changed inputs.

The manager stages overlay and receipt writes before replacing their destination files. Mod changes and overlay deletion invalidate both files. Increment REPAIR_REVISION in manager/localization/receipt.rs when changing repair rules or output semantics so already-installed overlays are regenerated.

Addon analysis registers a mod only after validating its full VPK paths and saving its manifest entry. An exact duplicate file assignment transfers ownership to the identified mod; partial overlaps and ambiguous filenames are rejected.

Compatibility review reads a snapshot of the installed files. Records whose VPKs were all deleted are skipped and shown in the report. A partially missing mod blocks merging and identifies the missing files, so the user can reinstall or disable it. Review does not rewrite the manifest or modify the original VPKs. Deleting a mod also clears older manifest aliases of its deleted files.

Apply requires the input fingerprint returned by review and checks the inputs again after preparing the plan. If mods or game files changed, the user must review again. Duplicate, unavailable, or stale conflict choices fail in both preview and export, before output files are replaced.

On this page