-
Notifications
You must be signed in to change notification settings - Fork 52
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Initial implementation for Bannerlord * Added game version resolution Using RandomAccess for performance gains Added Tools for launching Added NoWarn for CS1591 * Renamed MountAndBladeBannerlord to MountAndBlade2Bannerlord * Leftover renaming fixes, game name reusage * Added proper version resolution * Added semi Xbox support Switched to static gamedomain * dotnet format * Added some theoretical UI usage code Process start additional checks * Added icon and game image * Adapted to latest changes * Merge fix * Fixed Bannerlord, added base for tests * Minor adjustments * Build fix * Update * Update * Added future store support Added metadata enrichment * Added initial doc * Fix * Added GamePathProvier for easier path calculation Namespace change Other minor fixes * Adapted Store code Removed old test code * Post merge fixes * Added installer tests, fixed LauncherManager IO * Lil refactor * Added BLSE test case just to be safe * Adapted for multimod support * Formatting adjustments * Added Xbox support Fixed test compilation * Use the parameteter IFileSystem instead of the class field, shouldn't really matter but whatever * Fix * Added ModuleInfoSort for sorting, still needs integration * Fix * Added sorting * Some improvements * Adapted to latest master Added Emitter adapter of our diagnotics system * Latest changes * Fix * Finalized adaptation * Test fix * Adapted latest changes * Fixed tests * Fix * Fixed tests Added custom sorting * Better implementation * Added MountAndBlade2BannerlordLoadoutSynchronizerTests Exposed ModSortRules Added SortRules propagation * File metadata rework Added ViewModelCreator delegate for tests * Fix * Fix * Fix Builds --------- Co-authored-by: Vitalii Mikhailov <[email protected]>
- Loading branch information
Showing
42 changed files
with
1,731 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
## General Info | ||
|
||
- Name: Mount & Blade II: Bannerlord | ||
- Release Date: 2020 | ||
- Engine: Custom - C++ Foundation, C# Scripting | ||
|
||
### Stores and Ids | ||
|
||
- [Steam](https://store.steampowered.com/app/261550/Mount__Blade_II_Bannerlord/): `261550` | ||
- [GOG](https://www.gog.com/game/mount_blade_ii_bannerlord): `1802539526`, `1564781494` | ||
- [Epic Game Store](https://store.epicgames.com/en-US/p/mount-and-blade-2): `Chickadee` | ||
- [Xbox Game Pass](https://www.xbox.com/en-US/games/store/mount-blade-ii-bannerlord/9pdhwz7x3p03): `TaleWorldsEntertainment.MountBladeIIBannerlord` | ||
|
||
### Engine and Mod Support | ||
|
||
Bannerlord uses .NET Framework 4.7.2 for Steam/GOG/Epic and .NET Core 3.1 for Xbox game Pass PC. | ||
Modding is supported out of the box. | ||
Bannerlord has a modding extension [BLSE](https://www.nexusmods.com/mountandblade2bannerlord/mods/1) that expands the modding capabilities. | ||
It's required to run mods on Xbox and is optional for Steam/GOG/Epic. | ||
|
||
## Overview of Mod loading process(es) | ||
|
||
## Uploaded Files Structure | ||
|
||
## Additional Considerations for Manager | ||
|
||
## Essential Mods & Tools | ||
|
||
## Deployment Strategy | ||
|
||
## Work To Do | ||
|
||
## Misc Notes |
4 changes: 4 additions & 0 deletions
4
src/Games/NexusMods.Games.MountAndBlade2Bannerlord/.editorconfig
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
[*.cs] | ||
max_line_length = 180 | ||
|
||
space_within_single_line_array_initializer_braces = true |
103 changes: 103 additions & 0 deletions
103
src/Games/NexusMods.Games.MountAndBlade2Bannerlord/Emitters/BuiltInEmitter.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
using Bannerlord.LauncherManager; | ||
using Bannerlord.LauncherManager.Localization; | ||
using Bannerlord.ModuleManager; | ||
using NexusMods.DataModel.Diagnostics; | ||
using NexusMods.DataModel.Diagnostics.Emitters; | ||
using NexusMods.DataModel.Diagnostics.References; | ||
using NexusMods.DataModel.Loadouts; | ||
using NexusMods.DataModel.Loadouts.Mods; | ||
using NexusMods.Games.MountAndBlade2Bannerlord.Extensions; | ||
using NexusMods.Games.MountAndBlade2Bannerlord.Utils; | ||
|
||
namespace NexusMods.Games.MountAndBlade2Bannerlord.Emitters; | ||
|
||
public class BuiltInEmitter : ILoadoutDiagnosticEmitter | ||
{ | ||
internal const string Source = "NexusMods.Games.MountAndBlade2Bannerlord"; | ||
|
||
public async IAsyncEnumerable<Diagnostic> Diagnose(Loadout loadout) | ||
{ | ||
await Task.Yield(); | ||
|
||
var viewModels = (await loadout.GetSortedViewModelsAsync()).ToList(); | ||
var lookup = viewModels.ToDictionary(x => x.ModuleInfoExtended.Id, x => x); | ||
var modules = lookup.Values.Select(x => x.ModuleInfoExtended).Concat(FeatureIds.LauncherFeatures.Select(x => new ModuleInfoExtended { Id = x })).ToList(); | ||
|
||
var ctx = new ModuleContext(lookup); | ||
foreach (var moduleViewModel in viewModels) | ||
{ | ||
foreach (var diagnostic in ModuleUtilities.ValidateModule(modules, moduleViewModel.ModuleInfoExtended, ctx.GetIsSelected, ctx.GetIsValid).Select(x => Render(loadout, moduleViewModel.Mod, x))) | ||
{ | ||
yield return diagnostic; | ||
} | ||
} | ||
} | ||
|
||
private static Diagnostic Render(Loadout loadout, Mod mod, ModuleIssue issue) | ||
{ | ||
static string Version(ApplicationVersionRange version) => version == ApplicationVersionRange.Empty | ||
? version.ToString() | ||
: version.Min == version.Max | ||
? version.Min.ToString() | ||
: ""; | ||
|
||
// We reuse the translation for now | ||
var (level, message) = issue.Type switch | ||
{ | ||
ModuleIssueType.Missing => (DiagnosticSeverity.Critical, new BUTRTextObject("{=J3Uh6MV4}Missing '{ID}' {VERSION} in modules list") | ||
.SetTextVariable("ID", issue.SourceId) | ||
.SetTextVariable("VERSION", issue.SourceVersion.Min.ToString())), | ||
|
||
ModuleIssueType.MissingDependencies => (DiagnosticSeverity.Critical, new BUTRTextObject("{=3eQSr6wt}Missing '{ID}' {VERSION}") | ||
.SetTextVariable("ID", issue.SourceId) | ||
.SetTextVariable("VERSION", Version(issue.SourceVersion))), | ||
ModuleIssueType.DependencyMissingDependencies => (DiagnosticSeverity.Critical, new BUTRTextObject("{=U858vdQX}'{ID}' is missing it's dependencies!") | ||
.SetTextVariable("ID", issue.SourceId)), | ||
|
||
ModuleIssueType.DependencyValidationError => (DiagnosticSeverity.Critical, new BUTRTextObject("{=1LS8Z5DU}'{ID}' has unresolved issues!") | ||
.SetTextVariable("ID", issue.SourceId)), | ||
|
||
ModuleIssueType.VersionMismatchLessThanOrEqual => (DiagnosticSeverity.Warning, new BUTRTextObject("{=Vjz9HQ41}'{ID}' wrong version <= {VERSION}") | ||
.SetTextVariable("ID", issue.SourceId) | ||
.SetTextVariable("VERSION", Version(issue.SourceVersion))), | ||
ModuleIssueType.VersionMismatchLessThan => (DiagnosticSeverity.Warning, new BUTRTextObject("{=ZvnlL7VE}'{ID}' wrong version < [{VERSION}]") | ||
.SetTextVariable("ID", issue.SourceId) | ||
.SetTextVariable("VERSION", Version(issue.SourceVersion))), | ||
ModuleIssueType.VersionMismatchGreaterThan => (DiagnosticSeverity.Warning, new BUTRTextObject("{=EfNuH2bG}'{ID}' wrong version > [{VERSION}]") | ||
.SetTextVariable("ID", issue.SourceId) | ||
.SetTextVariable("VERSION", Version(issue.SourceVersion))), | ||
|
||
ModuleIssueType.Incompatible => (DiagnosticSeverity.Warning, new BUTRTextObject("{=zXDidmpQ}'{ID}' is incompatible with this module") | ||
.SetTextVariable("ID", issue.SourceId)), | ||
|
||
ModuleIssueType.DependencyConflictDependentAndIncompatible => (DiagnosticSeverity.Critical, new BUTRTextObject("{=4KFwqKgG}Module '{ID}' is both depended upon and marked as incompatible") | ||
.SetTextVariable("ID", issue.SourceId)), | ||
ModuleIssueType.DependencyConflictDependentLoadBeforeAndAfter => (DiagnosticSeverity.Critical, new BUTRTextObject("{=9DRB6yXv}Module '{ID}' is both depended upon as LoadBefore and LoadAfter") | ||
.SetTextVariable("ID", issue.SourceId)), | ||
ModuleIssueType.DependencyConflictCircular => (DiagnosticSeverity.Critical, new BUTRTextObject("{=RC1V9BbP}Circular dependencies. '{TARGETID}' and '{SOURCEID}' depend on each other") | ||
.SetTextVariable("TARGETID", issue.Target.Id) | ||
.SetTextVariable("SOURCEID", issue.SourceId)), | ||
|
||
ModuleIssueType.DependencyNotLoadedBeforeThis => (DiagnosticSeverity.Warning, new BUTRTextObject("{=s3xbuejE}'{SOURCEID}' should be loaded before '{TARGETID}'") | ||
.SetTextVariable("TARGETID", issue.Target.Id) | ||
.SetTextVariable("SOURCEID", issue.SourceId)), | ||
|
||
ModuleIssueType.DependencyNotLoadedAfterThis => (DiagnosticSeverity.Warning, new BUTRTextObject("{=2ALJB7z2}'{SOURCEID}' should be loaded after '{TARGETID}'") | ||
.SetTextVariable("ID", issue.SourceId)), | ||
|
||
_ => throw new ArgumentOutOfRangeException(nameof(issue)) | ||
}; | ||
|
||
return new Diagnostic | ||
{ | ||
Id = new DiagnosticId(Source, (ushort) issue.Type), | ||
Message = DiagnosticMessage.From(message.ToString()), | ||
Severity = level, | ||
DataReferences = new IDataReference[] | ||
{ | ||
loadout.ToReference(), | ||
mod.ToReference(loadout) | ||
} | ||
}; | ||
} | ||
} |
64 changes: 64 additions & 0 deletions
64
src/Games/NexusMods.Games.MountAndBlade2Bannerlord/Extensions/LoadoutExtensions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
using Bannerlord.LauncherManager; | ||
using Bannerlord.LauncherManager.Models; | ||
using NexusMods.DataModel.Loadouts; | ||
using NexusMods.DataModel.Loadouts.ModFiles; | ||
using NexusMods.DataModel.Loadouts.Mods; | ||
using NexusMods.Games.MountAndBlade2Bannerlord.Models; | ||
|
||
namespace NexusMods.Games.MountAndBlade2Bannerlord.Extensions; | ||
|
||
internal delegate LoadoutModuleViewModel ViewModelCreator(Mod mod, ModuleInfoExtendedWithPath moduleInfo, int index); | ||
|
||
internal static class LoadoutExtensions | ||
{ | ||
private static LoadoutModuleViewModel Default(Mod mod, ModuleInfoExtendedWithPath moduleInfo, int index) => new() | ||
{ | ||
Mod = mod, | ||
ModuleInfoExtended = moduleInfo, | ||
IsValid = mod.GetSubModuleFileMetadata()?.IsValid == true, | ||
IsSelected = mod.Enabled, | ||
IsDisabled = mod.Status == ModStatus.Failed, | ||
Index = index, | ||
}; | ||
|
||
private static async Task<IEnumerable<Mod>> SortMods(Loadout loadout) | ||
{ | ||
var loadoutSynchronizer = (loadout.Installation.Game.Synchronizer as MountAndBlade2BannerlordLoadoutSynchronizer)!; | ||
|
||
var sorted = await loadoutSynchronizer.SortMods(loadout); | ||
return sorted; | ||
} | ||
|
||
public static IEnumerable<LoadoutModuleViewModel> GetViewModels(this Loadout loadout, IEnumerable<Mod> mods, ViewModelCreator? viewModelCreator = null) | ||
{ | ||
viewModelCreator ??= Default; | ||
var i = 0; | ||
return mods.Select(x => | ||
{ | ||
var moduleInfo = x.GetModuleInfo(); | ||
if (moduleInfo is null) return null; | ||
|
||
var subModule = x.Files.Values.OfType<StoredFile>().First(y => y.To.FileName.Path.Equals(Constants.SubModuleName, StringComparison.OrdinalIgnoreCase)); | ||
var subModulePath = loadout.Installation.LocationsRegister.GetResolvedPath(subModule.To).GetFullPath(); | ||
|
||
return viewModelCreator(x, new ModuleInfoExtendedWithPath(moduleInfo, subModulePath), i++); | ||
}).OfType<LoadoutModuleViewModel>(); | ||
} | ||
|
||
public static async Task<IEnumerable<LoadoutModuleViewModel>> GetSortedViewModelsAsync(this Loadout loadout, ViewModelCreator? viewModelCreator = null) | ||
{ | ||
var sortedMods = await SortMods(loadout); | ||
return GetViewModels(loadout, sortedMods, viewModelCreator); | ||
} | ||
|
||
public static IEnumerable<LoadoutModuleViewModel> GetViewModels(this Loadout loadout, ViewModelCreator? viewModelCreator = null) | ||
{ | ||
return GetViewModels(loadout, loadout.Mods.Values, viewModelCreator); | ||
} | ||
|
||
public static bool HasModuleInstalled(this Loadout loadout, string moduleId) => loadout.Mods.Values.Any(x => | ||
x.GetModuleInfo() is { } moduleInfo && moduleInfo.Id.Equals(moduleId, StringComparison.OrdinalIgnoreCase)); | ||
|
||
public static bool HasInstalledFile(this Loadout loadout, string filename) => loadout.Mods.Values.Any(x => | ||
x.GetModuleFileMetadatas().Any(y => y.OriginalRelativePath.EndsWith(filename, StringComparison.OrdinalIgnoreCase))); | ||
} |
16 changes: 16 additions & 0 deletions
16
src/Games/NexusMods.Games.MountAndBlade2Bannerlord/Extensions/ModExtensions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
using Bannerlord.ModuleManager; | ||
using NexusMods.DataModel.Loadouts; | ||
using NexusMods.DataModel.Loadouts.Mods; | ||
using NexusMods.Games.MountAndBlade2Bannerlord.Models; | ||
|
||
namespace NexusMods.Games.MountAndBlade2Bannerlord.Extensions; | ||
|
||
internal static class ModExtensions | ||
{ | ||
public static SubModuleFileMetadata? GetSubModuleFileMetadata(this Mod mod) => mod.Files.SelectMany(y => y.Value.Metadata).OfType<SubModuleFileMetadata>().FirstOrDefault(); | ||
public static ModuleInfoExtended? GetModuleInfo(this Mod mod) => GetSubModuleFileMetadata(mod)?.ModuleInfo; | ||
|
||
public static IEnumerable<ModuleFileMetadata> GetModuleFileMetadatas(this Mod mod) => mod.Files.Values.Select(GetModuleFileMetadata).OfType<ModuleFileMetadata>(); | ||
public static ModuleFileMetadata? GetModuleFileMetadata(this AModFile modFile) => modFile.Metadata.OfType<ModuleFileMetadata>().FirstOrDefault(); | ||
public static string? GetOriginalRelativePath(this AModFile mod) => GetModuleFileMetadata(mod)?.OriginalRelativePath; | ||
} |
Oops, something went wrong.