Skip to content

Commit 875878e

Browse files
committed
WIP: Rework Load Order support for Red mods:
- separate sorting data from the items being sorted, allowing more control - Use ObservableList as base, and persist when it changes
1 parent 82c664c commit 875878e

File tree

137 files changed

+867
-442
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

137 files changed

+867
-442
lines changed

Directory.Packages.props

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@
2626
<PackageVersion Include="FetchBannerlordVersion" Version="1.0.6.45" />
2727
<PackageVersion Include="NLog" Version="5.2.8" />
2828
<PackageVersion Include="Noggog.CSharpExt" Version="2.64.0" />
29-
<PackageVersion Include="ObservableCollections" Version="2.2.0" />
30-
<PackageVersion Include="ObservableCollections.R3" Version="3.1.0" />
29+
<PackageVersion Include="ObservableCollections" Version="3.3.1" />
30+
<PackageVersion Include="ObservableCollections.R3" Version="3.3.1" />
3131
<PackageVersion Include="QoiSharp" Version="1.0.0" />
3232
<PackageVersion Include="R3" Version="1.2.9" />
3333
<PackageVersion Include="R3Extensions.Avalonia" Version="1.2.9" />

NexusMods.App.sln

-7
Original file line numberDiff line numberDiff line change
@@ -268,8 +268,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NexusMods.Abstractions.Reso
268268
EndProject
269269
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NexusMods.Abstractions.Resources.Resilience", "src\Abstractions\NexusMods.Abstractions.Resources.Resilience\NexusMods.Abstractions.Resources.Resilience.csproj", "{04219A58-C99C-4C3B-A477-5E4B29D1F275}"
270270
EndProject
271-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NexusMods.Abstractions.Games.UI", "src\Abstractions\NexusMods.Abstractions.Games.UI\NexusMods.Abstractions.Games.UI.csproj", "{9303B42B-9525-4106-B60A-5AFAFB8944E7}"
272-
EndProject
273271
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NexusMods.Abstractions.UI", "src\Abstractions\NexusMods.Abstractions.UI\NexusMods.Abstractions.UI.csproj", "{A7417BB1-7E2C-413A-A999-546F021F683B}"
274272
EndProject
275273
Global
@@ -702,10 +700,6 @@ Global
702700
{04219A58-C99C-4C3B-A477-5E4B29D1F275}.Debug|Any CPU.Build.0 = Debug|Any CPU
703701
{04219A58-C99C-4C3B-A477-5E4B29D1F275}.Release|Any CPU.ActiveCfg = Release|Any CPU
704702
{04219A58-C99C-4C3B-A477-5E4B29D1F275}.Release|Any CPU.Build.0 = Release|Any CPU
705-
{9303B42B-9525-4106-B60A-5AFAFB8944E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
706-
{9303B42B-9525-4106-B60A-5AFAFB8944E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
707-
{9303B42B-9525-4106-B60A-5AFAFB8944E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
708-
{9303B42B-9525-4106-B60A-5AFAFB8944E7}.Release|Any CPU.Build.0 = Release|Any CPU
709703
{A7417BB1-7E2C-413A-A999-546F021F683B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
710704
{A7417BB1-7E2C-413A-A999-546F021F683B}.Debug|Any CPU.Build.0 = Debug|Any CPU
711705
{A7417BB1-7E2C-413A-A999-546F021F683B}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -834,7 +828,6 @@ Global
834828
{BE8C17C4-E3B0-4D07-8CD0-0D15C3CCA9D5} = {0CB73565-1207-4A56-A79F-6A8E9BBD795C}
835829
{D3BA5B5A-668A-443B-872C-3116CBB0BC0D} = {0CB73565-1207-4A56-A79F-6A8E9BBD795C}
836830
{04219A58-C99C-4C3B-A477-5E4B29D1F275} = {0CB73565-1207-4A56-A79F-6A8E9BBD795C}
837-
{9303B42B-9525-4106-B60A-5AFAFB8944E7} = {0CB73565-1207-4A56-A79F-6A8E9BBD795C}
838831
{A7417BB1-7E2C-413A-A999-546F021F683B} = {0CB73565-1207-4A56-A79F-6A8E9BBD795C}
839832
EndGlobalSection
840833
GlobalSection(ExtensibilityGlobals) = postSolution

benchmarks/NexusMods.Benchmarks/Benchmarks/Sorting.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using BenchmarkDotNet.Attributes;
2-
using NexusMods.Abstractions.DataModel.Entities;
3-
using NexusMods.Abstractions.DataModel.Entities.Sorting;
2+
using NexusMods.Abstractions.Loadouts;
3+
using NexusMods.Abstractions.Loadouts.Sorting;
44
using NexusMods.Benchmarks.Interfaces;
55
using NexusMods.DataModel.Sorting;
66

src/Abstractions/NexusMods.Abstractions.GameLocators/GameInstallation.cs

-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using NexusMods.Abstractions.GameLocators.GameCapabilities;
2-
using NexusMods.Abstractions.Games;
32
using NexusMods.MnemonicDB.Abstractions;
43
using NexusMods.Paths;
54

src/Abstractions/NexusMods.Abstractions.GameLocators/GameLocatorResult.cs

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
using NexusMods.Abstractions.Games;
21
using NexusMods.Paths;
32

43
namespace NexusMods.Abstractions.GameLocators;

src/Abstractions/NexusMods.Abstractions.GameLocators/IGameLocatorResultMetadata.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using JetBrains.Annotations;
2-
using NexusMods.Abstractions.GameLocators;
32

4-
namespace NexusMods.Abstractions.Games;
3+
namespace NexusMods.Abstractions.GameLocators;
54

65
/// <summary>
76
/// Additional store specific metadata for <see cref="GameLocatorResult"/>.

src/Abstractions/NexusMods.Abstractions.GameLocators/Stores/EADesktop/EADesktopLocatorResultMetadata.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using JetBrains.Annotations;
2-
using NexusMods.Abstractions.GameLocators.Stores.EADesktop;
32

4-
namespace NexusMods.Abstractions.Games.Stores.EADesktop;
3+
namespace NexusMods.Abstractions.GameLocators.Stores.EADesktop;
54

65
/// <summary>
76
/// Metadata for games found that implement <see cref="IEADesktopGame"/>.

src/Abstractions/NexusMods.Abstractions.GameLocators/Stores/EGS/EpicLocatorResultMetadata.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using JetBrains.Annotations;
2-
using NexusMods.Abstractions.GameLocators.Stores.EGS;
32

4-
namespace NexusMods.Abstractions.Games.Stores.EGS;
3+
namespace NexusMods.Abstractions.GameLocators.Stores.EGS;
54

65
/// <summary>
76
/// Metadata for games found that implement <see cref="IEpicGame"/>.

src/Abstractions/NexusMods.Abstractions.GameLocators/Stores/GOG/GOGLocatorResultMetadata.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using JetBrains.Annotations;
2-
using NexusMods.Abstractions.GameLocators.Stores.GOG;
32

4-
namespace NexusMods.Abstractions.Games.Stores.GOG;
3+
namespace NexusMods.Abstractions.GameLocators.Stores.GOG;
54

65
/// <summary>
76
/// Metadata for games found that implement <see cref="IGogGame"/>.

src/Abstractions/NexusMods.Abstractions.GameLocators/Stores/Origin/OriginLocatorResultMetadata.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using JetBrains.Annotations;
2-
using NexusMods.Abstractions.GameLocators.Stores.Origin;
32

4-
namespace NexusMods.Abstractions.Games.Stores.Origin;
3+
namespace NexusMods.Abstractions.GameLocators.Stores.Origin;
54

65
/// <summary>
76
/// Metadata for games found that implement <see cref="IOriginGame"/>.

src/Abstractions/NexusMods.Abstractions.GameLocators/Stores/Steam/SteamLocatorResultMetadata.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
using JetBrains.Annotations;
2-
using NexusMods.Abstractions.GameLocators.Stores.Steam;
32
using NexusMods.Paths;
43

5-
namespace NexusMods.Abstractions.Games.Stores.Steam;
4+
namespace NexusMods.Abstractions.GameLocators.Stores.Steam;
65

76
/// <summary>
87
/// Metadata for games found that implement <see cref="ISteamGame"/>.

src/Abstractions/NexusMods.Abstractions.GameLocators/Stores/Xbox/XboxLocatorResultMetadata.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using JetBrains.Annotations;
2-
using NexusMods.Abstractions.GameLocators.Stores.Xbox;
32

4-
namespace NexusMods.Abstractions.Games.Stores.Xbox;
3+
namespace NexusMods.Abstractions.GameLocators.Stores.Xbox;
54

65
/// <summary>
76
/// Metadata for games found that implement <see cref="IXboxGame"/>.

src/Abstractions/NexusMods.Abstractions.GameLocators/Trees/AGamePathNodeTree.cs

-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System.Diagnostics.CodeAnalysis;
2-
using NexusMods.Abstractions.Games.Trees;
32
using NexusMods.Paths;
43
using NexusMods.Paths.Trees;
54
using NexusMods.Paths.Trees.Traits;

src/Abstractions/NexusMods.Abstractions.GameLocators/Trees/GamePathNode.cs

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
using System.Runtime.CompilerServices;
2-
using NexusMods.Abstractions.GameLocators;
31
using NexusMods.Paths;
42
using NexusMods.Paths.Trees;
53
using NexusMods.Paths.Trees.Traits;
64

7-
namespace NexusMods.Abstractions.Games.Trees;
5+
namespace NexusMods.Abstractions.GameLocators.Trees;
86

97
/// <summary>
108
/// Represents a tree node used for storing a game pathed location.

src/Abstractions/NexusMods.Abstractions.Games.UI/NexusMods.Abstractions.Games.UI.csproj

-13
This file was deleted.

src/Abstractions/NexusMods.Abstractions.Games/AGame.cs

+1-3
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,8 @@
33
using NexusMods.Abstractions.Diagnostics.Emitters;
44
using NexusMods.Abstractions.GameLocators;
55
using NexusMods.Abstractions.GameLocators.GameCapabilities;
6-
using NexusMods.Abstractions.Games.UI;
76
using NexusMods.Abstractions.IO;
87
using NexusMods.Abstractions.Library.Installers;
9-
using NexusMods.Abstractions.Loadouts;
108
using NexusMods.Abstractions.Loadouts.Synchronizers;
119
using NexusMods.Abstractions.NexusWebApi.Types.V2;
1210
using NexusMods.MnemonicDB.Abstractions;
@@ -68,7 +66,7 @@ protected virtual ILoadoutSynchronizer MakeSynchronizer(IServiceProvider provide
6866
public virtual IDiagnosticEmitter[] DiagnosticEmitters { get; } = [];
6967

7068
/// <inheritdoc/>
71-
public virtual IObservableSortableItemProvider[] SortableItemProviders { get; } = [];
69+
public virtual ISortableItemProviderFactory[] SortableItemProviderFactories { get; } = [];
7270

7371
/// <inheritdoc />
7472
public virtual ILoadoutSynchronizer Synchronizer => _synchronizer.Value;

src/Abstractions/NexusMods.Abstractions.Games/IGame.cs

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using NexusMods.Abstractions.Diagnostics.Emitters;
22
using NexusMods.Abstractions.GameLocators;
3-
using NexusMods.Abstractions.Games.UI;
43
using NexusMods.Abstractions.IO;
54
using NexusMods.Abstractions.Library.Installers;
65
using NexusMods.Abstractions.Loadouts.Synchronizers;
@@ -37,10 +36,10 @@ public interface IGame : ILocatableGame
3736
public IDiagnosticEmitter[] DiagnosticEmitters { get; }
3837

3938
/// <summary>
40-
/// An array of all instances of <see cref="ISortableItemProvider"/> supported
39+
/// An array of all instances of <see cref="ISortableItemProviderFactory"/> supported
4140
/// by the game.
4241
/// </summary>
43-
public IObservableSortableItemProvider[] SortableItemProviders { get; }
42+
public ISortableItemProviderFactory[] SortableItemProviderFactories { get; }
4443

4544
/// <summary>
4645
/// The synchronizer for this game.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using NexusMods.Abstractions.Loadouts;
2+
using ObservableCollections;
3+
4+
namespace NexusMods.Abstractions.Games;
5+
6+
/// <summary>
7+
/// A loadout specific provider and manager of sortable items.
8+
/// </summary>
9+
public interface ILoadoutSortableItemProvider
10+
{
11+
/// <summary>
12+
/// The ISortableItemProviderFactory that created this provider
13+
/// </summary>
14+
public ISortableItemProviderFactory ParentFactory { get; }
15+
16+
/// <summary>
17+
/// The id of the loadout that the sortable items are associated with
18+
/// </summary>
19+
public LoadoutId LoadoutId { get; }
20+
21+
22+
public ObservableList<ISortableItem> SortableItems { get; }
23+
24+
/// <summary>
25+
/// Sets the relative position of a sortable item in the load order
26+
/// </summary>
27+
/// <param name="sortableItem">item to move</param>
28+
/// <param name="delta">positive or negative index delta</param>
29+
/// <returns></returns>
30+
Task SetRelativePosition(ISortableItem sortableItem, int delta);
31+
}

src/Abstractions/NexusMods.Abstractions.Games/IObservableSortableItemProvider.cs

-10
This file was deleted.

src/Abstractions/NexusMods.Abstractions.Games/ISortableItem.cs

+17-8
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,34 @@ namespace NexusMods.Abstractions.Games;
77
/// All items in the list will have a non-gaming sort index. If a item is moved the other items will
88
/// adjust to compensate for the positional change.
99
/// </summary>
10-
public interface ISortableItem
10+
public interface ISortableItem : IComparable<ISortableItem>
1111
{
1212
/// <summary>
13-
/// The provider that contains all the items that can be sorted
13+
/// Reference to the provider that manages this item
1414
/// </summary>
15-
public ISortableItemProvider Provider { get; }
15+
public ILoadoutSortableItemProvider SortableItemProvider { get; }
1616

1717
/// <summary>
1818
/// The index of the item in a sorted list of item as given by the provider
1919
/// </summary>
20-
public int SortIndex { get; }
20+
public int SortIndex { get; set; }
2121

2222
/// <summary>
23-
/// This is used as the unique identifier for the entity.
23+
/// Name of the item for display purposes
2424
/// </summary>
25-
public EntityId EntityId { get; }
25+
public string DisplayName { get; }
2626

2727
/// <summary>
28-
/// Moves the item relative to other items by the sepecified amount
28+
/// The name of the winning mod containing the item
2929
/// </summary>
30-
public Task SetRelativePosition(int delta);
30+
public string ModName { get; set; }
31+
32+
/// <summary>
33+
/// Represents whether the item is active in the load order or not
34+
/// An item is considered active if it is part of the load order and will be loaded by the game
35+
/// An item is considered inactive if it is for some reason not going to be loaded by the game,
36+
/// e.g. it is disabled in the load order, or parent mod is disabled.
37+
/// </summary>
38+
public bool IsActive { get; set; }
39+
3140
}

src/Abstractions/NexusMods.Abstractions.Games/ISortableItemProvider.cs

-15
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using NexusMods.Abstractions.Loadouts;
2+
3+
namespace NexusMods.Abstractions.Games;
4+
5+
6+
/// <summary>
7+
/// A factory for creating providers for sortable items for specific loadouts
8+
/// </summary>
9+
public interface ISortableItemProviderFactory
10+
{
11+
/// <summary>
12+
/// Returns a provider of sortable items for a specific loadout
13+
/// </summary>
14+
ILoadoutSortableItemProvider GetLoadoutSortableItemProvider(LoadoutId loadoutId);
15+
16+
17+
/// <summary>
18+
/// Returns id of the type of the loadout
19+
/// </summary>
20+
Guid StaticLoadOrderTypeId { get; }
21+
}
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
using NexusMods.Abstractions.UI;
1+
using NexusMods.Abstractions.Games;
22
using ReactiveUI;
33
using Unit = System.Reactive.Unit;
44

5-
namespace NexusMods.Abstractions.Games.UI;
5+
namespace NexusMods.Abstractions.UI;
66

7-
public interface IObservableSortableItemViewModel : IViewModelInterface
7+
public interface ISortableItemViewModel : IViewModelInterface
88
{
9+
public ISortableItem SortableItem { get; }
10+
911
public ReactiveCommand<Unit, Unit> MoveUp { get; }
1012

1113
public ReactiveCommand<Unit, Unit> MoveDown { get; }
@@ -14,13 +16,9 @@ public interface IObservableSortableItemViewModel : IViewModelInterface
1416

1517
public int SortIndex { get; }
1618

17-
public string GroupName { get; }
18-
19-
public string Name { get; }
2019

21-
public bool IsEnabled { get; }
20+
public string DisplayName { get; }
2221

23-
public ReactiveCommand<bool, Unit> SetEnabled { get; }
24-
25-
public string[] InCollections { get; }
22+
public string GroupName { get; }
23+
2624
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using JetBrains.Annotations;
2+
using NexusMods.Abstractions.Loadouts;
3+
using NexusMods.Abstractions.Loadouts.Attributes;
4+
using NexusMods.MnemonicDB.Abstractions.Attributes;
5+
using NexusMods.MnemonicDB.Abstractions.Models;
6+
7+
namespace NexusMods.Abstractions.Games;
8+
9+
/// <summary>
10+
/// Represents an item that is sorted in a Load Order
11+
/// This should not be used directly, but rather be extended by the game-specific implementation.
12+
///
13+
/// Each implementation should provide the game specific identifier used to map entries of the Load Order to items of the loadout.
14+
/// E.g. the plugin name for Skyrim plugins, or the module uuid for BG3 pak files.
15+
/// </summary>
16+
[PublicAPI]
17+
public partial class SortableItemModel : IModelDefinition
18+
{
19+
private const string Namespace = "NexusMods.Loadouts.SortableItemModel";
20+
21+
/// <summary>
22+
/// Name of the item for display purposes.
23+
/// </summary>
24+
public static readonly StringAttribute Name = new(Namespace, nameof(Name));
25+
26+
/// <summary>
27+
/// Reference to the Load Order that this item is part of.
28+
/// </summary>
29+
public static readonly ReferenceAttribute<LoadOrder> ParentLoadOrder = new(Namespace, nameof(ParentLoadOrder))
30+
{
31+
IsIndexed = true,
32+
};
33+
34+
/// <summary>
35+
/// The order in which this item should be loaded relative to other items in the Load Order.
36+
/// </summary>
37+
public static readonly Int32Attribute SortIndex = new(Namespace, nameof(SortIndex));
38+
39+
}

src/Abstractions/NexusMods.Abstractions.Games/NexusMods.Abstractions.Games.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,6 @@
2626
<ProjectReference Include="..\NexusMods.Abstractions.Loadouts\NexusMods.Abstractions.Loadouts.csproj" />
2727
<ProjectReference Include="..\NexusMods.Abstractions.NexusWebApi\NexusMods.Abstractions.NexusWebApi.csproj" />
2828
<ProjectReference Include="..\NexusMods.Abstractions.UI\NexusMods.Abstractions.UI.csproj" />
29+
<PackageReference Include="NexusMods.MnemonicDB.SourceGenerator" PrivateAssets="all" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
2930
</ItemGroup>
3031
</Project>

0 commit comments

Comments
 (0)