Skip to content

Releases: Trashpanda62/Betadeps

BetaDeps v0.9.1

08 Jun 22:48

Choose a tag to compare

  • 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

05 Jun 00:04

Choose a tag to compare

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 under Modules\ instead of nested inside Modules\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 the CREST_SHOW_STUBS=1 User-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.0 across all six BetaDeps assemblies.

v0.8.0 — Real BUTR modules + Mod Configuration cleanup

27 May 20:23

Choose a tag to compare

BetaDeps v0.8.0 changelog

  • Promoted Bannerlord.Harmony, Bannerlord.UIExtenderEx, Bannerlord.ButterLib, and Bannerlord.MBOptionScreen from 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.0 across all six BetaDeps assemblies.
  • Removed the fomod/ Vortex installer wizard from the project. Vortex's flat-extract fallback deploys the Modules\ 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=1 once 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.md with 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): four Execute* toggle methods, three helper methods, four *ButtonText properties, and supporting state.
  • Removed stale build artifacts and the test-write.tmp scratch file from the source tree.
  • Added BetaDeps.HarmonyHost project producing Bannerlord.Harmony.dll for the real-module load path.
  • Reworked scripts/Build-Phase1.ps1 staging logic: each dependency DLL now stages exclusively into its real module's bin instead of being mirrored into Modules\BetaDeps\bin.
  • Aragas-strings DLL verifier list updated to scan the new real-module DLL locations.
  • Untracked CLAUDE.md and scripts/ from git per GitHub issue request; stripped Claude mentions from tracked files.

BetaDeps v0.7.5.1 - remove .bat for Nexus malware scanner

26 May 04:19

Choose a tag to compare

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

26 May 03:29

Choose a tag to compare

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:

  1. API surface fix. Added a non-generic ISettingsPropertyBuilder in the parent FluentBuilder namespace (different arity from the existing generic, so they coexist legally). AddValueFormat on the typed builders now returns that parent-namespace variant, matching consumer-mod IL exactly.

  2. Defense-in-depth guard. SettingsStorage.Save and .Load now 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.

  3. BuildAsPerCampaign() + FluentGlobalSettings.Unregister() shims added. Surfaced when the AddValueFormat fix exposed the NEXT layer of API drift — XorberaxLegacy also calls ISettingsBuilder.BuildAsPerCampaign() returning a FluentPerCampaignSettings (new file in the per-campaign scope namespace) and FluentGlobalSettings.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:

  1. 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.
  2. 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.
  3. Belt-and-suspenders refuse-to-block. SubModuleConstructionGuard now 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.bat from your Modules\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.md documents 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

25 May 16:48

Choose a tag to compare

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.md if 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

25 May 07:54

Choose a tag to compare

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 MissingMethodException or MissingFieldException fires, 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's Mission.GetFormationSpawnFrame crash. 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.xml for Name/Id/Version/Author/DependedModules, plus the DLL's AssemblyVersion and every TaleWorlds.* referenced assembly. Single-stop "what version shipped against what API".
  • Cecil import scan — walks the mod DLL's MemberReferences via Mono.Cecil and lists every TaleWorlds.* 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.StackTrace walked frame-by-frame with IL offsets.
  • First-arg summary — for LoadSaveGameData this is the save name; for LoadGameAction it's the full SaveGameFileInfo dump including the mod-list captured at save time; for SetMissionMode it's the MissionMode value, 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:

  • RetinuesTypeLoadException: Could not load type 'Attribute' at module load. Retinues's own Safety.Attribute base 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.GetFormationSpawnFrame with 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, so MissionCombatantsLogic still trips an MBIllegalValueException downstream. 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

24 May 07:09

Choose a tag to compare

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 PatchShield button in Mod Config. Sits next to Toggle Auto-Disable in the Options → Mod Config button row. Default behavior is PatchShield ON (the v0.7.0 shipped state). Click the new button to create Modules\BetaDeps\patchshield-disabled.flag; the next launch's PatchShield.Install() then bails on every lifecycle pass and consumer-mod prefixes that throw MissingMethodException / MissingFieldException / TypeLoadException propagate 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-Disable is opt-IN (file presence = enabled). Off by default because it modifies LauncherData.xml.
  • Toggle PatchShield is opt-OUT (file presence = disabled). On by default because it only intercepts managed exceptions.

Files in this release

  • BetaDeps-v0.7.1.zip — drop into Modules\ (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

24 May 05:24

Choose a tag to compare

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 at OnBeforeInitialModuleScreenSetAsRoot and re-run at every late lifecycle hook (OnGameInitializationFinished, OnGameStart, OnAfterGameInitializationFinished, OnNewGameCreated) so consumer mods that defer their PatchAll past OnSubModuleLoad are also covered. Idempotent: each pass only wraps newly-patched methods. Reports shield pass: +N new, X already-shielded, Y skipped per 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-BetaDeps owner from the patch chain via Harmony.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 throws TypeLoadException on DccPerSaveSettings.get_SaveInstance(), taking out the campaign init for anyone running DCC plus another mod that patches OnGameInitializationFinished. AttributePerSaveSettings<TSelf> re-parented to inherit from it so the full upstream inheritance chain resolves.
  • ISettingsBuilder.CreatePreset(string, string, Action<ISettingsPresetBuilder>) + concrete SettingsPresetBuilderImpl — 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.xml ship 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 of XmlNode, but the upstream BUTR ABI declares it as XmlDocument. Consumer mods compile their override 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. Both PrefabExtensionReplacePatch.GetPrefabExtension() and PrefabExtensionInsertAsSiblingPatch.GetPrefabExtension() now return XmlDocument. CDE and any mod hitting the same ABI mismatch now load cleanly.
  • Defense-in-depth shim on CollectModuleAssemblyTypes. Transpiler that, when a consumer assembly's GetTypes() throws ReflectionTypeLoadException, performs a lenient pass and signals Success to the engine if the specific SubModule class named in SubModule.xml loaded 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. McmSelfTest now classifies properties type-first (checking MCM.Common.Dropdown<T> ancestry) before falling back to [SettingProperty*] attribute classification. IDontCare's GlobalFilterMode is declared as Dropdown<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 (AdvancedFilteringStringsToFilter recomputes 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.xml but 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 version v2.4.99.0. Modern modlist support takes precedence.

Files in this release

  • BetaDeps-v0.7.0.zip — drop into Modules\ (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

23 May 08:06

Choose a tag to compare

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.log with 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

  1. Download BetaDeps-v0.6.0.zip from the assets below
  2. Extract into your Bannerlord Modules\ folder
  3. Enable BetaDeps in BLSE LauncherEx at the top of your load order

See the Nexus page for detailed install and compatibility info.