BetaDeps v0.7.0 — PatchShield + dependency-conflict fixes
BetaDeps v0.7.0 — PatchShield + dependency-conflict fixes + opt-in recovery
Released 2026-05-24.
Headline
PatchShield: a generic Harmony finalizer that catches the entire class of "consumer mod built against a stale TaleWorlds API" crashes. When a mod's prefix throws MissingMethodException, MissingFieldException, or TypeLoadException because TaleWorlds renamed/removed something the mod was patching, BetaDeps now (a) logs it, (b) synthesizes a sensible default return value so downstream callers don't NRE, and (c) auto-unpatches the offending mod's prefix so the broken patch never fires again. Before v0.7 these crashed the game during campaign init. With the shield, the offending patch becomes a logged warning and the game continues.
This solves real-world cases like the AIInfluence + NavalDLC campaign-load crash chain, plus a category of issues every time TaleWorlds ships a Bannerlord patch and not every mod author updates immediately.
Auto-disable also stays opt-in. Click Toggle Auto-Disable in Mod Config to enable single-launch recovery for the small set of known-CTD mods (HotScenes, RBM, etc.). All other recovery is now done live by the shield without any flag.
New
PatchShield— installed atOnBeforeInitialModuleScreenSetAsRootand re-run at every late lifecycle hook (OnGameInitializationFinished,OnGameStart,OnAfterGameInitializationFinished,OnNewGameCreated) so consumer mods that defer theirPatchAllpastOnSubModuleLoadare also covered. Idempotent: each pass only wraps newly-patched methods. Reportsshield pass: +N new, X already-shielded, Y skippedper pass.- Auto-unpatch on first throw. When the shield catches one of the three swallowable exceptions, it enumerates
Harmony.GetPatchInfo(originalMethod)and removes every non-BetaDepsownerfrom the patch chain viaHarmony.Unpatch(method, HarmonyPatchType.Prefix, owner). Subsequent calls run the unmodified original. Before this, a broken prefix would fire hundreds of times during NPC generation; now it fires once and is removed. MCM.Abstractions.Base.PerSave.PerSaveSettings<TSelf>— generic singleton base class consumer mods like Detailed Character Creation reference at JIT/type-load time. Without it the CLR throwsTypeLoadExceptiononDccPerSaveSettings.get_SaveInstance(), taking out the campaign init for anyone running DCC plus another mod that patchesOnGameInitializationFinished.AttributePerSaveSettings<TSelf>re-parented to inherit from it so the full upstream inheritance chain resolves.ISettingsBuilder.CreatePreset(string, string, Action<ISettingsPresetBuilder>)+ concreteSettingsPresetBuilderImpl— closes a missing-method gap that broke Retinues' fluent-builder MCM panel.ISettingsPropertyGroupBuilder.SetGroupOrder(int)— second missing-method gap on the same panel.- Auto-disable opt-in (carried forward from earlier v0.7 work). All recovery features that modify
LauncherData.xmlship OFF by default; click Toggle Auto-Disable in Mod Config to opt in. PatchShield runs regardless because it doesn't touch the launcher's modlist — it just defangs broken patches in memory.
Fixed
- Character Development Editor "dependency conflict" dialog. Traced via IL inspection: our impersonated
Bannerlord.UIExtenderEx.Prefabs.PrefabExtensionReplacePatch.GetPrefabExtension()declared a return type ofXmlNode, but the upstream BUTR ABI declares it asXmlDocument. Consumer mods compile theiroverride GetPrefabExtension()against the upstream signature; the CLR rejected those overrides at type-load time with "does not have an implementation" because the return types differed. BothPrefabExtensionReplacePatch.GetPrefabExtension()andPrefabExtensionInsertAsSiblingPatch.GetPrefabExtension()now returnXmlDocument. CDE and any mod hitting the same ABI mismatch now load cleanly. - Defense-in-depth shim on
CollectModuleAssemblyTypes. Transpiler that, when a consumer assembly'sGetTypes()throwsReflectionTypeLoadException, performs a lenient pass and signalsSuccessto the engine if the specificSubModuleclass named inSubModule.xmlloaded cleanly and has a usable parameterless constructor. Catches the same class of issue for any future ABI mismatch we haven't found yet, without papering over genuinely-broken mods. - IDontCare 658/658 self-test pass.
McmSelfTestnow classifies properties type-first (checkingMCM.Common.Dropdown<T>ancestry) before falling back to[SettingProperty*]attribute classification. IDontCare'sGlobalFilterModeis declared asDropdown<string>but decorated with[SettingPropertyBool], which used to misroute the round-trip; now it's classified correctly. Done-semantics also re-snapshots expected values after all mutations to handle computed properties (AdvancedFilteringStringsToFilterrecomputes from other dropdowns). - Alias-folder versions widened to v2.4.99 / v2.10.99 / v2.14.99 (Harmony / ButterLib / UIExtenderEx). Satisfies modern consumer-mod minimum-version checks.
- Stale launcher entries no longer treated as suspects. BannerFix-style ghost entries (in
LauncherData.xmlbut folder missing) are correctly skipped instead of triggering a phantom disable.
Diagnosed incompatible community mods
While shipping v0.7 we manually traced three community mods that are genuinely incompatible with the current Bannerlord build, beyond what BetaDeps can shield. These are mod-authoring issues, not BetaDeps bugs — listed here so users can find them:
- HotScenes — crashes the game before the main menu loads.
- RBM (Realistic Battle Mod) — crashes during campaign load, before the tutorial mission.
- JoinAnyBattle — crashes during the tutorial → world-map transition.
If you use any of these and your campaign won't load, disable them in BLSE's Mods tab. PatchShield catches a lot of patch-API mismatches but cannot rewrite a mod's own internal logic.
Changed defaults (vs. v0.6.0)
- Auto-disable on first run: OFF (was ON in v0.6.0). PatchShield runs regardless.
Known limitations
- Mod Organizer 2 is not officially supported. MO2's virtual filesystem layer can hide the alias folders BetaDeps creates in
Modules\. Install BetaDeps directly into<Bannerlord>\Modules\. - Legacy strict-wildcard mods declaring
v2.4.0.*Harmony minimums (e.g. older CargoHolds, if still maintained) will not match the bumped alias versionv2.4.99.0. Modern modlist support takes precedence.
Files in this release
BetaDeps-v0.7.0.zip— drop intoModules\(unzip →Modules\BetaDeps\)
Verifying the install
After first launch, your Modules\BetaDeps\runtime.log should contain lines like:
[BetaDeps.PatchShield] shield pass: +510 new, 0 already-shielded, 0 skipped (total shielded: 510)
[BetaDeps.PatchShield] shield pass: +142 new, 510 already-shielded, 0 skipped (total shielded: 652)
If a consumer mod's prefix throws during campaign load, you'll see:
[BetaDeps.PatchShield] swallowed MissingMethodException from a patch on <class>.<method>: ...
[BetaDeps.PatchShield] unpatched prefixes from owner '<modid>' on <class>::<method>
That's the shield doing its job. The game continues; the broken patch is gone for the rest of the session.