Releases: Trashpanda62/Betadeps
BetaDeps v0.9.1
- Fix UIExtenderEx PrefabExtensionSetAttributePatch.Attribute defined as class instead of readonly struct (TypeLoadException with BetterTime and similar mods)
- Fix AmbiguousMatchException on GetProperty("Attributes") when consumer patch overrides a virtual property
- Fix InsertType.Prepend inserting as first child instead of preceding sibling
- Fix MCM settings discovery missing MCM.Abstractions and Bannerlord.MCM assembly names
- Fix MCM search stale row list on empty-filter early return
BetaDeps v0.8.1
BetaDeps v0.8.1 changelog
- Zip layout changed: all five module folders (
BetaDeps,Bannerlord.Harmony,Bannerlord.UIExtenderEx,Bannerlord.ButterLib,Bannerlord.MBOptionScreen) now ship pre-positioned as siblings underModules\instead of nested insideModules\BetaDeps\aliases\. - Fixes the "greyed-out checkbox" in the launcher on clean install — v0.8.0 reversed the dependency direction so BetaDeps depends on the four BUTR-named modules, but the v0.7.x single-folder zip layout meant those modules weren't present on first launch, so the engine refused to let users enable BetaDeps.
- Fixes the Vortex "failed to set game mode M&Bbannerlord2" profile-corruption error reported on v0.8.0.
- BLSE LauncherEx now surfaces the four dependency modules (
Bannerlord.Harmony,Bannerlord.UIExtenderEx,Bannerlord.ButterLib,Bannerlord.MBOptionScreen) automatically. BetaDeps writes theCREST_SHOW_STUBS=1User-scope env var on every load; no manual setup required. On a fresh install the dep modules become visible starting on the second BLSE launch (BLSE reads its hide-stubs setting at launcher startup, before BetaDeps loads). - Added MO2 / USVFS auto-detection. When BetaDeps runs under Mod Organizer 2 it now resolves the real on-disk game Modules folder via
Process.MainModule.FileName(which the kernel sets before USVFS hooks install) and redirects the alias-folder bootstrap there, so the four dependency folders survive the virtualisation layer on the next launch. - Updated Troubleshooting and How-to-Install sections of the Nexus description to reflect the automatic BLSE behavior and the no-action-required MO2 path.
- Added a "raw BUTR-equivalent stack" note in
docs/BETADEPS-NATIVE-API.md: modders who want the canonical Harmony / UIExtenderEx / ButterLib / MCM DLLs without BetaDeps's defensive layers can disable the BetaDeps module in their launcher; the four dependency modules ship the real DLLs and run standalone as of v0.8. - Bumped AssemblyVersion / FileVersion to
0.8.1.0across all six BetaDeps assemblies.
v0.8.0 — Real BUTR modules + Mod Configuration cleanup
BetaDeps v0.8.0 changelog
- Promoted
Bannerlord.Harmony,Bannerlord.UIExtenderEx,Bannerlord.ButterLib, andBannerlord.MBOptionScreenfrom stub folders to real modules carrying their canonical DLLs. - Engine now loads the four dependency modules in dependency order before BetaDeps's own infrastructure.
- Eliminated the "two Harmony copies in memory" failure mode caused by consumer mods bundling their own
0Harmony.dll. - The four BUTR dependency modules ship inside
Modules\BetaDeps\aliases\and get materialized to top-level sibling folders by BetaDeps on first launch (existing behavior). The public zip layout remains "one BetaDeps folder under Modules" — same single-install footprint users have always seen. - Bumped AssemblyVersion / FileVersion to
0.8.0.0across all six BetaDeps assemblies. - Removed the
fomod/Vortex installer wizard from the project. Vortex's flat-extract fallback deploys theModules\tree the same way every other Bannerlord mod is installed. - Added search field at the top of the Mod Configuration tab — filters the modlist by name in real time.
- Added click-to-edit on slider values. Click the number to the right of any slider and type a new value.
- Added section dividers between groups inside a mod's settings (matches vanilla Video / Audio / Gameplay layout).
- Added hover-to-see-description right-side column. Hover any setting row to see its description in a dedicated narrower column on the right, separated from settings by a vertical divider line.
- Renamed the Mod Config tab to "Mod Configuration" to match the page title.
- Added mod count to the dynamic page title (e.g. "Mod Configuration · 35 mods"). Removed the redundant subtitle line.
- Removed "Run Self-Test", "Send to GitHub", "Toggle Auto-Disable", "Toggle PatchShield", and "Toggle SaveShield Swallow" buttons from the visible UI.
- Added "Send Bug Report" button that runs the self-test, writes
selftest.log+selftest.json, and opens a pre-filled GitHub issue in one click. - Empty-state message rewritten to be more helpful.
- Mod cycler row's "Mod N of M" subtitle now has visible spacing from the mod name above it.
- Slider handle widened from 14×38 to 18×42 for easier grabbing.
- Documented the BLSE LauncherEx drag-reorder workaround. Set Windows env var
CREST_SHOW_STUBS=1once to bring our four dependency modules back into BLSE's mod list. - Documented modder debugging flag files:
patchshield-disabled.flag,saveshield-swallow-disabled.flag,auto-disable-enabled.flag,betadeps-run-selftest.flag. - Updated
docs/BETADEPS-NATIVE-API.mdwith the v0.8 module layout and the flag-file debugging section. - Updated Nexus description's compatibility list to reflect the full tested modlist.
- Removed specific failure-reason call-outs from the "Not currently compatible" mod list per mod-author feedback.
- Removed orphan code from
OptionsVMMixin.cs(358 lines): fourExecute*toggle methods, three helper methods, four*ButtonTextproperties, and supporting state. - Removed stale build artifacts and the
test-write.tmpscratch file from the source tree. - Added
BetaDeps.HarmonyHostproject producingBannerlord.Harmony.dllfor the real-module load path. - Reworked
scripts/Build-Phase1.ps1staging logic: each dependency DLL now stages exclusively into its real module's bin instead of being mirrored intoModules\BetaDeps\bin. - Aragas-strings DLL verifier list updated to scan the new real-module DLL locations.
- Untracked
CLAUDE.mdandscripts/from git per GitHub issue request; stripped Claude mentions from tracked files.
BetaDeps v0.7.5.1 - remove .bat for Nexus malware scanner
Removed BetaDeps-Reset-State.bat from v0.7.5. The file's shape triggered Nexus's automated malware scanner. Manual reset procedure documented in the Nexus description's Troubleshooting section. Functionally identical to v0.7.5 otherwise.
BetaDeps v0.7.5 - MCM tab cascade fix + Vortex installer fix + fluent shim expansion
BetaDeps v0.7.5 — Vortex installer fix + MCM tab cascade fix
Folds v0.7.4's hot-fix work in with the MCM-tab-disappearing-after-crash bug surfaced by OQrock, ehmealeo, and tomas352000 over the last 24 hours. TacticsEditor is also pulled from this ship — it stays in the source tree but isn't bundled, holding for the Bannerwar v1.0 ship.
MCM AddValueFormat shim + settings storage feedback-loop guard
Surfaced during the v0.7.5 play-test cycle: XorberaxLegacy and AdjustableLeveling both call MCM.Abstractions.FluentBuilder.Models.ISettingsPropertyIntegerBuilder.AddValueFormat(string) and expect it to return the PARENT-namespace MCM.Abstractions.FluentBuilder.ISettingsPropertyBuilder (non-generic). Our shim was returning the nested-namespace Models.ISettingsPropertyBuilder, which is a different CLR type. Signature mismatch → MissingMethodException → MCM's reset-to-default flow caught the exception and re-triggered a save, which triggered a load, which triggered another save, looping until the new-campaign init step CTD'd the game.
Two-layer fix:
-
API surface fix. Added a non-generic
ISettingsPropertyBuilderin the parent FluentBuilder namespace (different arity from the existing generic, so they coexist legally).AddValueFormaton the typed builders now returns that parent-namespace variant, matching consumer-mod IL exactly. -
Defense-in-depth guard.
SettingsStorage.Saveand.Loadnow detect re-entrant calls for the same settings ID within a 100ms window and skip the second call with a one-time warning log. So if any future API mismatch causes a similar feedback loop, the game survives instead of CTDing. -
BuildAsPerCampaign()+FluentGlobalSettings.Unregister()shims added. Surfaced when the AddValueFormat fix exposed the NEXT layer of API drift — XorberaxLegacy also callsISettingsBuilder.BuildAsPerCampaign()returning aFluentPerCampaignSettings(new file in the per-campaign scope namespace) andFluentGlobalSettings.Unregister()during teardown. Both added.
The MCM "Mod Configuration" tab disappearing fix (the headline)
After a game crash, some users found the "Mod Configuration" entry was missing from the in-game Options screen on next launch. OQrock confirmed an uninstall + reinstall of BetaDeps brought it back — which pointed at one of our own state files as the culprit.
It was: betadeps-disabled-mods.log. After a crash, BetaDeps' suspect-detection logic compares the currently-enabled modlist against the last known clean modlist; anything not in the clean list and not in our self-protection allowlist becomes a "suspect" and gets auto-disabled. The allowlist used an explicit hardcoded set of 5 module IDs, and downstream knock-on effects from disabling a UI mod could cascade into our MCM tab not rendering properly. Reinstall deleted the log → clean slate → tab returned.
v0.7.5 fixes this three ways:
- Prefix-aware self-protection. Any module ID starting with
BetaDeps.is treated as ours, in addition to the original 5 explicit ids. No future submodule can ever be auto-disabled by us. - Time-decayed block list. Entries older than 7 days are forgiven on load — the line stays in the file for triage history, but it stops actively blocking. A crash that hasn't recurred in a week is more likely a one-off than a chronic problem.
- Belt-and-suspenders refuse-to-block.
SubModuleConstructionGuardnow hard-rejects any BetaDeps-owned id in the disabled log even if a future bug somehow wrote one there.
And as a user-facing escape hatch for anyone hit by this on older versions or by some other state issue:
BetaDeps-Reset-State.bat
New file shipped at the root of Modules\BetaDeps\. Double-click it, hit Y, it wipes every state file BetaDeps maintains between sessions (block list, last-good modlist, incompatible-mods scan, the SaveShield ledger, runtime.log, selftest.log, all toggle flags). Your actual mod settings in Documents\...\Configs\ModSettings\Global\ are not touched. Your mods are not uninstalled. Equivalent to "uninstall + reinstall BetaDeps" without the re-download.
Vortex installer fix (also in v0.7.5)
v0.7.2/v0.7.3 fomod/ModuleConfig.xml used double-hyphens (--) inside XML comments as a stylistic dash. That's illegal per the XML 1.0 spec, and Vortex's FOMOD parser correctly rejected it at line 7 col 32 the moment a user clicked "Install."
Two users (RonnieJay + Xearokk) reported the failure within hours of the v0.7.2 push. Both ended up installing manually, which worked because the regular launcher doesn't touch fomod/. But the Vortex path advertised on the description was broken from v0.7.2 through v0.7.4. v0.7.5 swaps the double-hyphens for colons.
Build-time XML lint (so the Vortex bug class can't ship again)
New scripts/Validate-Xml.ps1 walks every shipped .xml file and checks for -- inside XML comment bodies plus basic well-formedness via XmlDocument.LoadXml. Wired into Build-Phase1.ps1 between the DLL verification step and the zip-packaging step, so the bug class literally cannot reach the ship zip. Escape hatch via $env:BETADEPS_SKIP_XML_LINT='1' for emergency overrides.
Session-summary log lines
PatchShield and SaveShield each now write a one-line session summary to runtime.log at AppDomain.ProcessExit. Mod authors fielding bug reports can ask for just the SESSION SUMMARY line as a first pass instead of the whole file.
Catalog-Triage script
New scripts/Catalog-Triage.ps1 reads Modules\BetaDeps\failed-mods-catalog.txt (the SaveShield append-only ledger from v0.7.3) and prints a sorted leaderboard of which mods have caught failures across all your sessions. Drives the v1.0 wide-fleet bug bash directly — mods with high DIST (distinct failure modes) are the priority targets.
Compat-Scan script (new)
scripts/Compat-Scan.ps1 is an offline static analyzer that opens any mod folder, walks every TaleWorlds.* MemberReference in its DLLs via Mono.Cecil, and emits a markdown report of every broken call with the closest matching current signature. Categories: MISSING_TYPE, MISSING_METHOD, METHOD_SIGNATURE_CHANGED, FIELD_NOW_PROPERTY, MISSING_FIELD. Pairs with SaveShield's runtime CULPRIT identification for full coverage — Compat-Scan tells you what WILL break, SaveShield tells you what DID break.
Useful for mod authors who want a deterministic patch list before launching, and for us when triaging which mods need outreach.
Direct-API guide updated for v0.7.3+
docs/BETADEPS-NATIVE-API.md was stuck on v0.7.1 status. Now reflects the full SaveShield surface (FailureRecord schema, RecentFailures ring buffer, render methods, ToggleSwallow, ModManifest, FailedModsCatalog), plus the PatchShield UnpatchedOwnerCounts addition. This is the doc the Nexus "Mod Creators" section links to, so authors clicking through now get current info.
TacticsEditor held for Bannerwar ship
BetaDeps.TacticsEditor is no longer in the shipping zip. The source still builds locally so it doesn't bitrot, but it doesn't go into dist\ or the public zip. The build script also removes any stale BetaDeps.TacticsEditor folder from your live install on deploy — that prevents in-place upgraders from continuing to load the unsupported folder.
It comes back when Bannerwar v1.0 ships, packaged as part of that release rather than as a free-floating dependency in BetaDeps.
Who needs to update
- Users on v0.7.3/v0.7.4 who hit the disappearing MCM tab: install v0.7.5 normally; the fix is automatic. If the tab is still missing right after the upgrade, run
BetaDeps-Reset-State.batfrom yourModules\BetaDeps\folder. - Users who tried Vortex install on v0.7.2-v0.7.4 and got the XML parse error: v0.7.5's FOMOD parses cleanly. Uninstall the broken Vortex entry, drop the v0.7.5 zip in.
- Users on v0.7.3 manual install and happy: straightforward update; no manual intervention required.
- Mod authors with BetaDeps-native code: the new SaveShield section in
docs/BETADEPS-NATIVE-API.mddocuments the public surface for querying diagnostic state from your own code. Compat-Scan is also worth a look — run it against your mod, get a free pre-flight check.
Lessons learned
Self-protection logic needs both an allow list AND a deny refusal at the use site. A single allow list with hardcoded names is one missed entry away from disabling itself. Prefix-aware checks plus a hard refusal in the consuming code make the whole class of bug impossible.
BetaDeps v0.7.4 - Vortex installer fix + polish
BetaDeps v0.7.4 — Vortex installer fix + polish
Headline hot-fix release. Centerpiece is the Vortex installer fix that unblocks two users today (RonnieJay + Xearokk), but the version also picks up a handful of low-risk improvements that were ready to ship alongside.
Vortex installer fix (the headline)
v0.7.2/v0.7.3 fomod/ModuleConfig.xml used double-hyphens (--) inside XML comments as a stylistic dash. That's illegal per the XML 1.0 spec (Xml_InvalidCommentChars), and Vortex's FOMOD parser correctly rejected it at line 7 col 32 the moment a user clicked "Install."
Two users reported the failure within hours of the v0.7.2 push: same System.Xml.XmlException stack from FomodInstaller.Scripting.XmlScript.XmlScriptType.Validate. Both ended up installing manually, which worked fine because Bannerlord's launcher never touches fomod/. But the Vortex path advertised on the Nexus description was broken from v0.7.2 through v0.7.3.
v0.7.4 swaps the double-hyphens for colons. The Vortex FOMOD installer parses it cleanly.
Build-time XML lint (so this doesn't happen again)
New scripts/Validate-Xml.ps1 walks every shipped .xml file and checks for two failure modes: -- inside XML comment bodies (the exact bug above) and basic well-formedness via XmlDocument.LoadXml. Wired into Build-Phase1.ps1 between the DLL verification step and the zip-packaging step, so the bug class literally cannot ship again. Escape hatch via $env:BETADEPS_SKIP_XML_LINT='1' for emergency overrides.
Session-summary log lines
PatchShield and SaveShield each now write a one-line session summary to runtime.log at AppDomain.ProcessExit. The lines look like:
[BetaDeps.PatchShield] SESSION SUMMARY: shielded 590 method(s),
unpatched 4 prefix(es), swallowed 6 exception(s)
(MissingMethod 4, MissingField 1, TypeLoad 1, other 0).
Top unpatched owner: AIInfluence (3).
[BetaDeps.SaveShield] SESSION SUMMARY: shielded 7 entry-point(s),
caught 3 failure(s) (duplicate-key 0, other 3),
swallowed 3, swallow-mode ON. Top CULPRIT: Reinforcement System (3).
Lets users grepping runtime.log get the gist in one line per shield instead of scanning the whole file. Mod authors fielding bug reports can ask for just the SESSION SUMMARY line as a first pass.
Catalog-Triage script
New scripts/Catalog-Triage.ps1 reads Modules\BetaDeps\failed-mods-catalog.txt (the SaveShield append-only ledger from v0.7.3) and prints a sorted leaderboard of which mods have caught failures across all your sessions. Output looks like:
MOD DIST TOTAL LATEST EXCEPTIONS
-------------------------------- ---- ----- -------------------- ----------
Reinforcement System 3 8 2026-05-25 02:26:51 System.MissingMethodException (x6); System.TypeLoadException (x2)
Retinues 1 4 2026-05-25 01:57:53 System.TypeLoadException (x4)
Drives the v1.0 wide-fleet bug bash directly — mods with high DIST (distinct failure modes) are the priority targets.
Direct-API guide updated for v0.7.3+
docs/BETADEPS-NATIVE-API.md was stuck on v0.7.1 status. v0.7.4 brings it current: full SaveShield section documenting the public surface (FailureRecord 18-field schema, RecentFailures ring buffer, three render methods, ToggleSwallow, ModManifest, FailedModsCatalog), a PatchShield.UnpatchedOwnerCounts subsection for the per-mod auto-unpatch tracker. This is the doc the Nexus "Mod Creators" section links to, so authors clicking through now get current info.
Who needs to update
- Users who tried Vortex install on v0.7.2/v0.7.3 and got the XML error: delete the broken install (or leave your manual install in place, they don't conflict), uninstall the v0.7.2/v0.7.3 entry from Vortex, then drop the v0.7.4 zip in. Wizard will work this time.
- Users who manually installed v0.7.3 and are happy with it: no action needed. The v0.7.4 deltas are all polish improvements (session summaries, XML lint, docs); no behavior change for already-working installs.
- Mod authors with BetaDeps-native code: read the new SaveShield section in
docs/BETADEPS-NATIVE-API.mdif you want to query SaveShield's diagnostic state from your own code.
Lesson learned
XML comments cannot contain -- anywhere in their content. The spec is explicit and parsers correctly reject it. v0.7.4's build-time lint enforces this from now on.
BetaDeps v0.7.3 - SaveShield
BetaDeps v0.7.3 — SaveShield + ON-by-default swallow-mode
Headline release. SaveShield is BetaDeps's second defensive layer, joining PatchShield. The two now work side-by-side: PatchShield wraps Harmony-patched methods; SaveShield wraps the engine-level entry points where consumer-mod handlers run during save deserialization and battle init.
SaveShield core
Shields seven Bannerlord entry points by default: MBSaveLoad.LoadSaveGameData, SaveManager.Load (both overloads), SandBoxSaveHelper.LoadGameAction, MissionState.FinishMissionLoading, Mission.SetMissionMode, Mission.OnInitialize, Mission.SpawnTroop. When a consumer-mod handler throws from any of these, SaveShield catches the throw, writes a full diagnostic block to runtime.log, and — by default — drops the throw so the load continues. Saves that crashed on load now load. Battles that crashed on entry now enter.
Swallow-mode ON by default
This is the big behavioral change. v0.7.2 shipped swallow-mode as opt-in; v0.7.3 makes it the protective default. Naming convention now matches PatchShield exactly — both default ON, both opt-out via a *-disabled.flag file. Click Toggle SaveShield Swallow in Mod Config to opt out (creates saveshield-swallow-disabled.flag next to runtime.log). Migration: the legacy v0.7.2 saveshield-swallow.flag file is auto-deleted on first launch so it doesn't sit stale.
The swallow only fires when the deepest non-engine, non-BetaDeps stack frame is from a mod assembly — so engine bugs and BetaDeps bugs continue to propagate unmodified.
Mod-creator diagnostics (the SaveShield FAILURE block)
Every caught exception writes a labeled block to runtime.log with all of the following:
- CULPRIT — name of the assembly that owns the deepest non-engine frame. Mod authors get a one-line answer about which mod to investigate.
- Current API signature probe — when
MissingMethodExceptionorMissingFieldExceptionfires, SaveShield reflects the named type on the current build and prints every current overload. The mod author sees exactly what to migrate to. (Real example shipped: ReinforcementSystem'sMission.GetFormationSpawnFramecrash. SaveShield printed the current 6-parameter signature against the 5-parameter signature the mod called, pinpointing the API drift.) - CULPRIT manifest — reads the offending mod's
SubModule.xmlfor Name/Id/Version/Author/DependedModules, plus the DLL'sAssemblyVersionand everyTaleWorlds.*referenced assembly. Single-stop "what version shipped against what API". - Cecil import scan — walks the mod DLL's
MemberReferencesvia Mono.Cecil and lists everyTaleWorlds.*member referenced whose name matches the failing call. Distinguishes compile-time-bound calls from reflection-bound ones. - Finalizer call chain — frames that led TO the patched method (not just the exception's throw stack). Often reveals the engine code path that triggered the failure.
- Parsed frames — exception's
System.Diagnostics.StackTracewalked frame-by-frame with IL offsets. - First-arg summary — for
LoadSaveGameDatathis is the save name; forLoadGameActionit's the fullSaveGameFileInfodump including the mod-list captured at save time; forSetMissionModeit's theMissionModevalue, etc.
selftest.log + selftest.json
selftest.log gets a new SaveShield status section between PatchShield and the installed-vs-enabled list. Counters (Methods shielded, Duplicate-key hits, Other load failures, Swallow-mode state, Exceptions swallowed) plus the full text of every FAILURE block recorded this session, plus a copy-paste-ready GitHub-issue markdown snippet of the most-recent failure.
New selftest.json sidecar is written next to selftest.log — schema-versioned, same data in machine-readable form. AI assistants and CI tooling can parse it without interpreting the human-readable layout.
Send-to-GitHub button enrichment
The pre-fill URL on the "Send to GitHub" button now embeds the most-recent SaveShield FAILURE block as inline markdown (CULPRIT table, current API signatures, manifest probe, stack frames). Bug reports come with the diagnosis already filled in.
failed-mods-catalog.txt
New append-only ledger at Modules\BetaDeps\failed-mods-catalog.txt. Each session, the first time a (CULPRIT, ExceptionType, OwnerMethod) triple is seen, a one-line entry is appended. Build up a personal incompatibility ledger across sessions.
PatchShield owner-counts
PatchShield now tracks which Harmony owner IDs got auto-unpatched and how many of each. selftest.log surfaces this as "AIInfluence: 4 patches unpatched" rows, so you see at a glance which mods are bleeding.
Incompatibility list updates
Two more mods moved to the Known incompatible list, both with detailed cause notes:
- Retinues —
TypeLoadException: Could not load type 'Attribute'at module load. Retinues's ownSafety.Attributebase class fails to load due to value-type mismatch with one of its referenced assemblies. Downstream symptom is the "Retinues Dependency Error: Harmony: Error" dialog. Needs a Retinues mod-author recompile. - ReinforcementSystem — calls
Mission.GetFormationSpawnFramewith the v1.2.x 5-parameter signature; current Bannerlord added a 6th parameter (Boolean useDefaultClassIfNotFound). SaveShield swallows the throw, but the broken handler was setting up team state that the engine then expected, soMissionCombatantsLogicstill trips anMBIllegalValueExceptiondownstream. Needs a ReinforcementSystem mod-author one-line fix.
Caveats
Swallow-mode validated end-to-end against ReinforcementSystem. It works as designed for mods whose broken handler is non-essential (drops the handler, game keeps running). It can't rescue battles when the broken handler is doing critical engine-state setup — in that case you'll see a SAVE-LOAD FAILURE block, then a follow-up MISSION-INIT FAILURE from inside TaleWorlds itself (with CULPRIT: (no non-engine frame found)) because the engine noticed inconsistent state. Disable the named CULPRIT mod from the first block and the engine-level follow-up goes away.
BetaDeps v0.7.1 — PatchShield opt-out
BetaDeps v0.7.1 — PatchShield opt-out
Released 2026-05-24.
Small follow-up to v0.7.0 that adds a way to turn PatchShield off.
New
Toggle PatchShieldbutton in Mod Config. Sits next toToggle Auto-Disablein the Options → Mod Config button row. Default behavior is PatchShield ON (the v0.7.0 shipped state). Click the new button to createModules\BetaDeps\patchshield-disabled.flag; the next launch'sPatchShield.Install()then bails on every lifecycle pass and consumer-mod prefixes that throwMissingMethodException/MissingFieldException/TypeLoadExceptionpropagate unmodified. Click again to delete the flag and re-enable.- Confirmation popup explains the new state and that a restart is required.
Why
Mod authors debugging their own work want exceptions to surface unmodified instead of being swallowed and unpatched. Users who suspect PatchShield is interfering with another shim need an off-switch. PatchShield only catches managed exceptions in-memory (no LauncherData.xml edits), so the conservative default stays ON — but you can now opt out without a code change.
Note the asymmetry with Toggle Auto-Disable:
Toggle Auto-Disableis opt-IN (file presence = enabled). Off by default because it modifiesLauncherData.xml.Toggle PatchShieldis opt-OUT (file presence = disabled). On by default because it only intercepts managed exceptions.
Files in this release
BetaDeps-v0.7.1.zip— drop intoModules\(unzip →Modules\BetaDeps\)
Verifying the install
When PatchShield is on (default), Modules\BetaDeps\runtime.log shows lines like:
[BetaDeps.PatchShield] shield pass: +510 new, 0 already-shielded, 0 skipped (total shielded: 510)
After clicking Toggle PatchShield and restarting, the same log instead shows:
[BetaDeps.PatchShield] patchshield-disabled.flag present — PatchShield disabled; skipping install. Click 'Toggle PatchShield' in Mod Config to re-enable.
That confirms the opt-out took effect.
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.
BetaDeps v0.6.0
v0.6.0 — 2026-05-23 (Auto-disable broken mods after game updates)
New
- Auto-disable for crash-causing mods. BetaDeps now records which mods load cleanly to the main menu each session (the "last-good modlist"). If a launch crashes before reaching the main menu, BetaDeps diffs your enabled mods against that baseline on the next startup, identifies whichever mod is new-and-broken, and auto-disables it in LauncherData.xml. No more spending an hour bisecting your modlist after a TaleWorlds patch breaks half your mods — hit Play, crash once, hit Play again, the broken mod is flagged and the game boots. Disable reasons land in
Modules\BetaDeps\betadeps-disabled-mods.logwith a timestamp. - "Send to GitHub" button in Mod Config. One click opens a pre-filled GitHub issue in your default browser — title, environment info, BetaDeps version, the last-good baseline, the auto-disable history, and the top of your Self-Test report all pre-populated in Markdown. Just drag-and-drop runtime.log / selftest.log as attachments and hit Submit.
- Auto-disable diagnostics in the Self-Test report. Three new sections appear at the bottom of every
selftest.log: which mods loaded cleanly this session, the auto-disable history (last 20 entries with timestamps and reasons), and the incompatible-mods scan. Users sending in reports now give us the full picture without having to attach extra files. - Path redaction in shared output. Anywhere BetaDeps shows a path that includes your Windows username (the Pre-test backup notice in the Self-Test popup, the GitHub issue body, etc.), the per-user portion is replaced with
<Documents>so screenshots and copy-pasted reports don't leak your username.
Fixed
- Orphan settings no longer show in Mod Config. When a mod was disabled in BLSE but its DLLs were still loaded in the AppDomain (e.g. ROT after un-checking ROT-Core), its settings used to ghost into the Mod Config menu. The settings registry now intersects "DLL loaded" with "enabled in LauncherData.xml" before showing settings, so disabled mods cleanly disappear.
- Content-only mods are no longer flagged as crash suspects. RBM_WS and similar XML/asset-only mods get a SKIP-SUSPECT entry instead of being disabled — they can't crash the engine because they don't ship code.
- Stale launcher entries no longer flagged. If LauncherData.xml has an entry for a mod whose folder no longer exists on disk (BannerFix, faster_ships in recent cleanup), the detector skips it instead of trying to disable a phantom.
- Launch marker now writes first. The "session in progress" marker BetaDeps uses to detect crashes is now the very first thing written when BetaDeps loads, before any other code that could throw. Hard-crashing mods that die microseconds after BetaDeps loads still leave a marker behind for the next session's recovery logic.
Known limitations
- Auto-disable currently requires two launches: one crash to establish "this mod is broken", one to apply the disable. Single-launch recovery is on the v0.7 roadmap.
- Hard-crashing mods that take down the game process before the main menu (e.g. mods compiled against significantly older Bannerlord versions like Banner Kings, ROT, Detailed Character Creation) still need two launches even with v0.6 — and on some game versions the second launch may overwrite our LauncherData.xml change before our in-memory disable lands. v0.7 will Harmony-patch the engine's SubModule construction loop so these mods get blocked before they can crash.
Install
- Download
BetaDeps-v0.6.0.zipfrom the assets below - Extract into your Bannerlord
Modules\folder - Enable BetaDeps in BLSE LauncherEx at the top of your load order
See the Nexus page for detailed install and compatibility info.