Skip to content

BetaDeps v0.7.5 - MCM tab cascade fix + Vortex installer fix + fluent shim expansion

Choose a tag to compare

@Trashpanda62 Trashpanda62 released this 26 May 03:29
· 49 commits to main since this release

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.