Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Game: Bannerlord Support #130

Closed
wants to merge 84 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
7e0ab64
Initial implementation for Bannerlord
Aragas Feb 20, 2023
89103e3
Added game version resolution
Aragas Feb 20, 2023
6ca582c
Renamed MountAndBladeBannerlord to MountAndBlade2Bannerlord
Aragas Feb 21, 2023
1d7f0ab
Leftover renaming fixes, game name reusage
Aragas Feb 21, 2023
afffdc9
Added proper version resolution
Aragas Feb 21, 2023
45f228c
Merge branch 'main' into bannerlord
Aragas Mar 1, 2023
02c8ac6
Added semi Xbox support
Aragas Mar 1, 2023
9b963fb
Merge branch 'main' into bannerlord
Aragas Mar 3, 2023
05b5613
dotnet format
Aragas Mar 3, 2023
d726888
Merge branch 'main' into bannerlord
Aragas Mar 7, 2023
f901585
Added some theoretical UI usage code
Aragas Mar 7, 2023
14b155a
Added icon and game image
Aragas Mar 8, 2023
113f1ff
Merge branch 'main' into bannerlord
Aragas Mar 8, 2023
5dd227a
Adapted to latest changes
Aragas Mar 8, 2023
f4ef2f6
Merge branch 'main' into bannerlord
Aragas Mar 9, 2023
3f33c18
Merge fix
Aragas Mar 9, 2023
47f2ad0
Merge branch 'main' into bannerlord
Aragas Mar 13, 2023
613a6e8
Fixed Bannerlord, added base for tests
Aragas Mar 13, 2023
8da284f
Minor adjustments
Aragas Mar 17, 2023
6d64b2f
Merge branch 'main' into bannerlord
Aragas Mar 18, 2023
60c2b1c
Merge branch 'main' into bannerlord
Aragas Mar 24, 2023
0de8be6
Build fix
Aragas Mar 27, 2023
28a77d6
Merge branch 'main' into bannerlord
Aragas Mar 30, 2023
98501ca
Update
Aragas Mar 30, 2023
152cdc1
Merge branch 'main' into bannerlord
Aragas Apr 5, 2023
a18d230
Update
Aragas Apr 5, 2023
5cb990a
Merge branch 'main' into bannerlord
Aragas Apr 12, 2023
fd3eea9
Added future store support
Aragas Apr 12, 2023
46a8648
Added initial doc
Aragas Apr 12, 2023
d8bb362
Merge branch 'main' into bannerlord
Aragas Apr 12, 2023
a3691ef
Fix
Aragas Apr 12, 2023
ce589b9
Merge branch 'main' into bannerlord
Aragas Apr 14, 2023
ea40298
Added GamePathProvier for easier path calculation
Aragas Apr 14, 2023
513b385
Merge branch 'main' into bannerlord
Aragas Apr 18, 2023
fbab384
Merge branch 'main' into origin_bannerlord
Aragas Apr 19, 2023
fe2fd22
Adapted Store code
Aragas Apr 19, 2023
cff3ce7
Post merge fixes
Aragas Apr 19, 2023
56bb319
Added installer tests, fixed LauncherManager IO
Aragas Apr 19, 2023
63ed4c2
Lil refactor
Aragas Apr 20, 2023
2ea0f55
Added BLSE test case just to be safe
Aragas Apr 20, 2023
24ea880
Merge branch 'main' into origin_bannerlord
Aragas Apr 25, 2023
81ba458
Adapted for multimod support
Aragas Apr 25, 2023
0eca0ee
Formatting adjustments
Aragas Apr 25, 2023
398aded
Merge branch 'main' into origin_bannerlord
Aragas Apr 25, 2023
c8284e7
Merge branch 'main' into origin_bannerlord
Aragas Apr 25, 2023
6eb12e9
Merge branch 'main' into origin_bannerlord
Aragas May 15, 2023
44c3b43
Merge branch 'main' into origin_bannerlord
Aragas May 17, 2023
fada441
Added Xbox support
Aragas May 17, 2023
60d20a4
Use the parameteter IFileSystem instead of the class field, shouldn't…
Aragas May 17, 2023
4f6f120
Merge branch 'nexusmods_main' into origin_bannerlord
Aragas May 30, 2023
60362a2
Fix
Aragas May 30, 2023
b0a6261
Added ModuleInfoSort for sorting, still needs integration
Aragas May 31, 2023
b38dc43
Merge branch 'nexusmods_main' into origin_bannerlord
Aragas May 31, 2023
90594c0
Fix
Aragas May 31, 2023
a2bf4a3
Merge branch 'nexusmods_main' into origin_bannerlord
Aragas Jun 1, 2023
16d570e
Added sorting
Aragas Jun 2, 2023
7b4c160
Some improvements
Aragas Jun 2, 2023
a21806c
Merge branch 'nexusmods_main' into origin_bannerlord
Aragas Jun 14, 2023
cba9263
Merge branch 'main' into bannerlord
Aragas Jun 29, 2023
af69f3e
Merge branch 'bannerlord' of https://github.com/BUTR/NexusMods.App in…
Aragas Jul 5, 2023
edf6981
Merge branch 'main' into bannerlord
Aragas Jul 22, 2023
799a362
Merge branch 'nexusmods_main' into origin_bannerlord
Aragas Jul 26, 2023
df163c4
Merge branch 'nexusmods_main' into origin_bannerlord
Aragas Aug 3, 2023
c8f3d44
Adapted to latest master
Aragas Aug 3, 2023
abd8761
Merge branch 'nexusmods_main' into origin_bannerlord
Aragas Aug 3, 2023
e7ad364
Merge branch 'nexusmods_main' into origin_bannerlord
Aragas Aug 10, 2023
96587e0
Merge branch 'main' into origin_bannerlord
Aragas Sep 30, 2023
d306cdb
Latest changes
Aragas Sep 30, 2023
633abae
Merge branch 'main' into origin_bannerlord
Aragas Sep 30, 2023
decc249
Fix
Aragas Sep 30, 2023
b6e7b4f
Finalized adaptation
Aragas Sep 30, 2023
14ff496
Test fix
Aragas Sep 30, 2023
5b59c0b
Merge branch 'nexusmods_main' into origin_bannerlord
Aragas Nov 9, 2023
3d5d87f
Adapted latest changes
Aragas Nov 9, 2023
1bfc93d
Fixed tests
Aragas Nov 16, 2023
38062c6
Merge branch 'nexusmods_main' into origin_bannerlord
Aragas Nov 16, 2023
54dd151
Fix
Aragas Nov 16, 2023
19b9f32
Fixed tests
Aragas Nov 17, 2023
9e435be
Better implementation
Aragas Nov 17, 2023
ac86df0
Added MountAndBlade2BannerlordLoadoutSynchronizerTests
Aragas Nov 17, 2023
f1ab3b6
File metadata rework
Aragas Nov 17, 2023
6becbf9
Fix
Aragas Nov 20, 2023
a402804
Merge branch 'nexusmods_main' into origin_bannerlord
Aragas Nov 20, 2023
7d8b333
Fix
Aragas Nov 20, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
<PackageVersion Include="NexusMods.Telemetry.OpenTelemetry" Version="1.0.0" />
<PackageVersion Include="FomodInstaller.Interface" Version="1.2.0" />
<PackageVersion Include="FomodInstaller.Scripting.XmlScript" Version="1.0.0" />
<!-- Bannerlord -->
<PackageVersion Include="Bannerlord.LauncherManager" Version="1.0.76" />
<PackageVersion Include="FetchBannerlordVersion" Version="1.0.6.39" />
</ItemGroup>
<ItemGroup>
<PackageVersion Include="JetBrains.Annotations" Version="2023.3.0" PrivateAssets="all" />
Expand Down
14 changes: 14 additions & 0 deletions NexusMods.App.sln
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarks", "benchmarks",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NexusMods.Common.Tests", "tests\NexusMods.Common.Tests\NexusMods.Common.Tests.csproj", "{FE0B804A-949E-44E7-9531-B16664ACEC01}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NexusMods.Games.MountAndBlade2Bannerlord", "src\Games\NexusMods.Games.MountAndBlade2Bannerlord\NexusMods.Games.MountAndBlade2Bannerlord.csproj", "{3E970563-DAE0-4168-AE8D-AB09A786C8A3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NexusMods.Games.MountAndBlade2Bannerlord.Tests", "tests\Games\NexusMods.Games.MountAndBlade2Bannerlord.Tests\NexusMods.Games.MountAndBlade2Bannerlord.Tests.csproj", "{355C8D44-F46F-4AA2-96C0-DDB6844D8BEA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NexusMods.Networking.NexusWebApi.NMA", "src\Networking\NexusMods.Networking.NexusWebApi.NMA\NexusMods.Networking.NexusWebApi.NMA.csproj", "{871E2565-BD95-43D1-9EC3-CAAC74D55507}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NexusMods.Networking.Downloaders", "src\Networking\NexusMods.Networking.Downloaders\NexusMods.Networking.Downloaders.csproj", "{3FBDEE15-9892-40EF-9593-6353068FAF48}"
Expand Down Expand Up @@ -285,6 +289,14 @@ Global
{FE0B804A-949E-44E7-9531-B16664ACEC01}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FE0B804A-949E-44E7-9531-B16664ACEC01}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FE0B804A-949E-44E7-9531-B16664ACEC01}.Release|Any CPU.Build.0 = Release|Any CPU
{3E970563-DAE0-4168-AE8D-AB09A786C8A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3E970563-DAE0-4168-AE8D-AB09A786C8A3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3E970563-DAE0-4168-AE8D-AB09A786C8A3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3E970563-DAE0-4168-AE8D-AB09A786C8A3}.Release|Any CPU.Build.0 = Release|Any CPU
{355C8D44-F46F-4AA2-96C0-DDB6844D8BEA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{355C8D44-F46F-4AA2-96C0-DDB6844D8BEA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{355C8D44-F46F-4AA2-96C0-DDB6844D8BEA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{355C8D44-F46F-4AA2-96C0-DDB6844D8BEA}.Release|Any CPU.Build.0 = Release|Any CPU
{871E2565-BD95-43D1-9EC3-CAAC74D55507}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{871E2565-BD95-43D1-9EC3-CAAC74D55507}.Debug|Any CPU.Build.0 = Debug|Any CPU
{871E2565-BD95-43D1-9EC3-CAAC74D55507}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -372,6 +384,8 @@ Global
{83B2A024-0218-4F65-B75B-0102DAF38443} = {02A589BE-50CA-4D29-BA99-81EEA2410F8D}
{CB61A764-B3BB-42C0-8CDB-DBE57FB80DF5} = {CF7454A5-0EBB-46E7-9A10-614380DB95D9}
{FE0B804A-949E-44E7-9531-B16664ACEC01} = {52AF9D62-7D5B-4AD0-BA12-86F2AA67428B}
{3E970563-DAE0-4168-AE8D-AB09A786C8A3} = {70D38D24-79AE-4600-8E83-17F3C11BA81F}
{355C8D44-F46F-4AA2-96C0-DDB6844D8BEA} = {05B06AC1-7F2B-492F-983E-5BC63CDBF20D}
{871E2565-BD95-43D1-9EC3-CAAC74D55507} = {D7E9D8F5-8AC8-4ADA-B219-C549084AD84C}
{3FBDEE15-9892-40EF-9593-6353068FAF48} = {D7E9D8F5-8AC8-4ADA-B219-C549084AD84C}
{09B037AB-07BB-4154-95FD-6EA2E55C4568} = {897C4198-884F-448A-B0B0-C2A6D971EAE0}
Expand Down
33 changes: 33 additions & 0 deletions docs/games/000X-Bannerlord.md
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
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
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)
}
};
}
}
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)));
}
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;
}
Loading
Loading