Skip to content

About

A library that enables multiple mods to alter standard game interface.

Topics

Resources

Stars

24 stars

Watchers

0 watching

Forks

Latest commit

 

History

841 Commits

Folders and files

Repository files navigation

Bannerlord.UIExtenderEx

Logo
Lines Of Code CodeFactor Code Climate maintainability Documentation Translation status
GitHub Workflow Status (event) CodeCov
NuGet Bannerlord.UIExtenderEx NuGet Bannerlord.UIExtenderEx.Analyzers
NexusMods UIExtenderEx
Steam Mod Configuration Menu Steam Downloads Steam Views Steam Subscriptions Steam Favorites

A library that enables multiple mods to alter standard game interface.
Previously, a fork of UIExtenderLib that was de-forked.

Installation

This module should be one of the highest in loading order. Ideally, it should be loaded after Bannerlord.Harmony or Bannerlord.ButterLib.

For Players

This mod is a dependency mod that does not provide anything by itself. You need to additionally install mods that use it.

Usage

The game's UI follows the Model-View-ViewModel pattern. Prefabs, the movie XML, are the View: UIExtenderEx changes them with prefab extensions. ViewModels supply the data the prefabs bind to: UIExtenderEx extends them with mixins.

Check the Articles section of our documentation!

Add Bannerlord.UIExtenderEx.Analyzers to your mod to have your mixins and prefab XML checked while you build: members that replace the game's, names two mixins both add, refresh methods that do not exist, mixins that would never run or would crash the screen, and in your XML misspelled attributes, values the loader cannot convert, and bindings to members the ViewModel does not have. Most of them come with a code fix. See Analyzers.

Pre-compiled prefabs (AutoGens)

The game uses two prefab systems: XML prefabs that are parsed at runtime, and C# prefabs that TaleWorlds pre-compiles from the same XML with TaleWorlds.MountAndBlade.GauntletUI.CodeGenerator.exe. We call the latter AutoGens. AutoGens skip the XML parsing and bind to ViewModels with typed code instead of reflection, which is noticeably faster, especially on the Mono runtime.

UIExtenderEx patches the XML. The game's AutoGens were built from the unpatched XML, so they would silently ignore every patch. UIExtenderEx used to disable AutoGens globally because of that.

UIExtenderEx now keeps AutoGens enabled and recompiles only the movies a patch affects:

  • When a movie is loaded whose prefab tree contains a patched prefab, UIExtenderEx generates C# from the patched XML with a fork of the game's own code generator (TaleWorlds.GauntletUI.CodeGenerator, included with TaleWorlds' permission), compiles it on a background thread and registers the result in place of the game's variant. The first load of such a movie still uses XML.
  • Compiled prefabs are cached in Modules/Bannerlord.UIExtenderEx/CompiledPrefabs. The cache key is a fingerprint of the patched XML, the ViewModel and widget assemblies and the enabled mixins, so the next game session uses them immediately and any change triggers a rebuild. Superseded builds of a movie are deleted when the new one is compiled, and the whole cache is cleared once UIExtenderEx or the game is updated.
  • Properties and commands contributed by ViewModel mixins are bound through typed access to the mixin instance, which the stock generator cannot do.
  • Enabling or disabling patches makes the game parse the affected prefabs again the next time a movie uses them, instead of reusing the copies it keeps for movies currently open. Open movies keep what they show; the next open gets the patched version, in XML and compiled form alike.
  • Widget classes and prefabs that mods register at runtime through WidgetFactoryManager are generated like the game's own. A widget type the factory cannot resolve fails generation with its name; the report lands in CompiledPrefabs/Failed/<Movie>/<ViewModel>/errors.txt, next to compile failures.
  • The compiler ships with UIExtenderEx: Roslyn and every assembly it binds to are ILRepacked into Bannerlord.UIExtenderEx.Compiler and internalized. Nothing about it is resolved by name, so no other module's copy of System.Collections.Immutable or System.Reflection.Metadata can reach it and module load order cannot change what it does. It used to be the Roslyn in the game's mono folder, which made UIExtenderEx compete with other modules for those two assemblies.
  • While the game loads, a worker thread brings the cached assemblies into the process and warms the compiler up, so the first patched movie pays neither Roslyn's start-up cost nor the assembly load. Loading an assembly costs 90 to 210 ms in a modded game, spent in other mods' assembly-load listeners, which is why freshly compiled assemblies are loaded on the worker too and the main thread only registers them.
  • Mixins and mod ViewModels may stay internal: the generated code binds to them the way publicizer tools do (IgnoresAccessChecksTo).
  • The module runs on both runtimes the game ships on, the Mono embedded in the Steam, GOG and Epic builds and the .NET 6 of the Xbox PC / Microsoft Store build. Bannerlord.UIExtenderEx.dll, the one assembly mods use, is netstandard2.0 and serves both; only the compiled-prefab part, which carries the compiler, is built per runtime. The NuGet package holds Bannerlord.UIExtenderEx.dll for netstandard2.0 alone, as it did before 3.0.0.
  • A compiled assembly is loaded after the game built its widget type table, so UIExtenderEx adds its widget classes to that table when it registers one, in place. The game itself only offers a full rescan of every loaded assembly for this, which is what UIExtenderEx falls back to if the table cannot be reached.

Settings control this behaviour. They are declared in the <Settings> block of SubModule.xml, which is also where they are stored: each property has a Default, and the current value is a Value attribute next to it. Edit it by hand, or let MCM do it: when MCM is loaded it shows the block in its options screen and writes changes back into SubModule.xml. Updating UIExtenderEx replaces that file, so changed values need to be applied again afterwards.

  • CompiledPrefabs (default true): set to false to skip the recompilation; affected movies then always load from XML.
  • DumpGeneratedCode (default false): write the generated C# next to the cached assemblies.
  • DisableGeneratedPrefabs (default false): the previous behaviour, every movie loads from XML.
  • DumpXML (default false): dump the patched XML of every movie.
  • RecordTimings (default false): write movie load, compile, and warm-up timings to CompiledPrefabs/Timings/, for investigating load times.

CompiledPrefabs and DisableGeneratedPrefabs are read at startup and need a restart. The two dump settings apply within a second of the file being saved.

For mod authors, Compiled Prefabs describes what changes for a mod and how to check that a movie is compiled. For maintainers, the Compiled Prefabs section of the documentation describes the pipeline: the load decision, the manager and its cache, the fingerprint, the forked generator, compilation and the tests.

About

A library that enables multiple mods to alter standard game interface.

Topics

Resources

Stars

24 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages