Skip to content

Commit e9df8f3

Browse files
authored
Merge branch 'Nexus-Mods:main' into add-spanish-language
2 parents 81d4082 + 97c4913 commit e9df8f3

File tree

58 files changed

+3572
-867
lines changed

Some content is hidden

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

58 files changed

+3572
-867
lines changed

src/NexusMods.Abstractions.GameLocators/GamePath.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,16 @@ public GamePath(LocationId locationId, RelativePath path)
5252

5353
/// <summary/>
5454
public static bool operator !=(GamePath a, GamePath b) => a.LocationId != b.LocationId || a.Path != b.Path;
55+
56+
/// <summary>
57+
/// Joins the current path with a relative path.
58+
/// </summary>
59+
public static GamePath operator /(GamePath a, RelativePath b) => new(a.LocationId, a.Path / b);
60+
61+
/// <summary>
62+
/// Joins the current path with a string.
63+
/// </summary>
64+
public static GamePath operator /(GamePath a, string b) => new(a.LocationId, a.Path / b);
5565

5666
/// <inheritdoc />
5767
public override bool Equals(object? obj) => obj is GamePath other && Equals(other);

src/NexusMods.Abstractions.Games/AGame.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ protected AGame(IServiceProvider provider)
3636
_gameLocators = provider.GetServices<IGameLocator>();
3737
// In a Lazy so we don't get a circular dependency
3838
_synchronizer = new Lazy<ILoadoutSynchronizer>(() => MakeSynchronizer(provider));
39-
_sortOrderManager = new Lazy<ISortOrderManager>(() => MakeSortOrderManager(provider));
39+
_sortOrderManager = new Lazy<ISortOrderManager>(() => MakeSortOrderManager(provider, this));
4040
_fs = provider.GetRequiredService<IFileSystem>();
4141
}
4242

@@ -48,10 +48,10 @@ protected virtual ILoadoutSynchronizer MakeSynchronizer(IServiceProvider provide
4848
return new DefaultSynchronizer(provider);
4949
}
5050

51-
private ISortOrderManager MakeSortOrderManager(IServiceProvider provider)
51+
private ISortOrderManager MakeSortOrderManager(IServiceProvider provider, IGame game)
5252
{
5353
var manager = new SortOrderManager(provider);
54-
manager.RegisterSortOrderVarieties(GetSortOrderVarieties());
54+
manager.RegisterSortOrderVarieties(GetSortOrderVarieties(), game);
5555
return manager;
5656
}
5757

@@ -87,6 +87,8 @@ private ISortOrderManager MakeSortOrderManager(IServiceProvider provider)
8787

8888
/// <inheritdoc />
8989
public virtual ILoadoutSynchronizer Synchronizer => _synchronizer.Value;
90+
91+
public virtual ISortOrderManager SortOrderManager => _sortOrderManager.Value;
9092

9193
/// <inheritdoc />
9294
public GameInstallation InstallationFromLocatorResult(GameLocatorResult metadata, EntityId dbId, IGameLocator locator)

src/NexusMods.Abstractions.Games/IGame.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ public interface IGame : ILocatableGame
5050
/// </summary>
5151
public ILoadoutSynchronizer Synchronizer { get; }
5252

53+
/// <summary>
54+
/// The sort order manager for this game.
55+
/// </summary>
56+
public ISortOrderManager SortOrderManager { get; }
57+
5358
/// <summary>
5459
/// Constructs a <see cref="GameInstallation"/> from the given <see cref="GameLocatorResult"/>, and a unique DB ID,
5560
/// also marks the installation was sourced from the given <see cref="IGameLocator"/>.

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,9 @@
3838
<DependentUpon>ISortOrderManager.cs</DependentUpon>
3939
</Compile>
4040
</ItemGroup>
41+
42+
<ItemGroup>
43+
<AdditionalFiles Include="**\*.sql" />
44+
<UpToDateCheckInput Include="**\*.sql"/>
45+
</ItemGroup>
4146
</Project>

src/NexusMods.Abstractions.Games/Services.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ public static class Services
1313
public static IServiceCollection AddGames(this IServiceCollection services)
1414
{
1515
return services
16-
.AddSortOrderItemModel();
16+
.AddSortOrderItemModel()
17+
.AddSortOrderQueriesSql();
1718
}
1819
}

src/NexusMods.Abstractions.Games/SortOrder/ASortOrderVariety.cs

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -23,23 +23,17 @@ public abstract class ASortOrderVariety<TKey, TSortableItem, TItemLoadoutData, T
2323
where TItemLoadoutData : ISortItemLoadoutData<TKey>
2424
where TSortedEntry : ISortItemData<TKey>
2525
{
26-
private readonly ILogger _logger;
26+
private readonly ILogger<ASortOrderVariety<TKey, TSortableItem, TItemLoadoutData, TSortedEntry>> _logger;
2727

2828
/// <summary>
2929
/// Database connection
3030
/// </summary>
3131
protected readonly IConnection Connection;
32-
33-
/// <summary>
34-
/// The game sort order manager that this variety belongs to.
35-
/// </summary>
36-
protected readonly ISortOrderManager Manager;
37-
38-
protected ASortOrderVariety(IServiceProvider serviceProvider, ISortOrderManager manager)
32+
33+
protected ASortOrderVariety(IServiceProvider serviceProvider)
3934
{
4035
Connection = serviceProvider.GetRequiredService<IConnection>();
41-
_logger = serviceProvider.GetRequiredService<ILogger>();
42-
Manager = manager;
36+
_logger = serviceProvider.GetRequiredService<ILogger<ASortOrderVariety<TKey, TSortableItem, TItemLoadoutData, TSortedEntry>>>();
4337
}
4438

4539
#region public members
@@ -195,19 +189,20 @@ public async ValueTask MoveItemDelta(SortOrderId sortOrderId, TKey sourceItem, i
195189
}
196190

197191
/// <inheritdoc />
198-
public virtual async ValueTask ReconcileSortOrder(SortOrderId sortOrderId, IDb? db = null, CancellationToken token = default)
192+
public virtual async ValueTask ReconcileSortOrder(SortOrderId sortOrderId, IDb? referenceDb = null, CancellationToken token = default)
199193
{
200194
// If this is passed a specific database, don't retry the reconciliation using the most recent database.
201-
var noRetry = db is not null;
195+
var noRetry = referenceDb is not null;
202196
var retryCount = 0;
203-
var dbToUse = db ?? Connection.Db;
204197

205198
while (retryCount <= 3)
206199
{
207-
var reconciledItems = ReconcileSortOrderCore(sortOrderId, dbToUse);
200+
var refDb = referenceDb ?? Connection.Db;
208201

209-
var succeded = await TryPersistSortOrder(sortOrderId, reconciledItems.Select(tuple => tuple.SortedEntry).ToArray(), dbToUse, token);
210-
if (succeded) return;
202+
var reconciledItems = ReconcileSortOrderCore(sortOrderId, refDb);
203+
204+
var succeeded = await TryPersistSortOrder(sortOrderId, reconciledItems.Select(tuple => tuple.SortedEntry).ToArray(), refDb, token);
205+
if (succeeded) return;
211206

212207
if (noRetry)
213208
{
@@ -222,6 +217,30 @@ public virtual async ValueTask ReconcileSortOrder(SortOrderId sortOrderId, IDb?
222217
_logger.LogError("Reconciliation of sort order {SortOrderId} failed after 4 attempts", sortOrderId);
223218
}
224219

220+
/// <inheritdoc />
221+
public virtual async ValueTask DeleteSortOrder(SortOrderId sortOrderId, CancellationToken token = default)
222+
{
223+
using var tx = Connection.BeginTransaction();
224+
225+
// Delete the items
226+
foreach (var item in Connection.Db.Datoms(SortOrderItem.ParentSortOrder, sortOrderId))
227+
{
228+
tx.Delete(item.E, recursive: false);
229+
}
230+
231+
// Delete the sort order
232+
tx.Delete(sortOrderId, recursive: false);
233+
234+
try
235+
{
236+
await tx.Commit();
237+
}
238+
catch (Exception ex)
239+
{
240+
_logger.LogError(ex, "Failed to delete sort order {SortOrderId}", sortOrderId);
241+
}
242+
}
243+
225244

226245
#endregion public members
227246

@@ -285,17 +304,20 @@ protected abstract void PersistSortOrderCore(
285304
CancellationToken token = default);
286305

287306

288-
protected virtual IReadOnlyList<(TSortedEntry SortedEntry, TItemLoadoutData ItemLoadoutData)> ReconcileSortOrderCore(SortOrderId sortOrderId, IDb dbToUse)
307+
protected virtual IReadOnlyList<(TSortedEntry SortedEntry, TItemLoadoutData ItemLoadoutData)> ReconcileSortOrderCore(SortOrderId sortOrderId, IDb loadoutRevisionDb)
289308
{
290-
var sortOrder = SortOrder.Load(dbToUse, sortOrderId);
309+
// Get the most recent sort order data
310+
var sortOrder = SortOrder.Load(loadoutRevisionDb.Connection.Db, sortOrderId);
291311

292312
var collectionGroupId = sortOrder.ParentEntity.IsT1 ?
293313
sortOrder.ParentEntity.AsT1 :
294314
Optional<CollectionGroupId>.None;
295315

296-
var loadoutData = RetrieveLoadoutData(sortOrder.LoadoutId, collectionGroupId, dbToUse);
316+
// Get the loadout data from the revision db
317+
var loadoutData = RetrieveLoadoutData(sortOrder.LoadoutId, collectionGroupId, loadoutRevisionDb);
297318

298-
var currentSortOrder = RetrieveSortOrder(sortOrderId, dbToUse);
319+
// Get the most recent sort order data
320+
var currentSortOrder = RetrieveSortOrder(sortOrderId, loadoutRevisionDb.Connection.Db);
299321

300322
var reconciledItems = Reconcile(currentSortOrder, loadoutData);
301323
return reconciledItems;
@@ -304,6 +326,7 @@ protected abstract void PersistSortOrderCore(
304326
/// <summary>
305327
/// Returns a collection of loadout-specific TItemLoadoutData for each relevant item found in the provided loadout/collection.
306328
/// These loadout data items are unsorted and do not have a sort index.
329+
/// This already filters out duplicates, currently selecting the most recently added enabled item for each key.
307330
/// </summary>
308331
protected abstract IReadOnlyList<TItemLoadoutData> RetrieveLoadoutData(
309332
LoadoutId loadoutId,

src/NexusMods.Abstractions.Games/SortOrder/ISortItemData.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ namespace NexusMods.Abstractions.Games;
33
/// <summary>
44
/// Represents an entry in a stored sort order.
55
/// </summary>
6-
public interface ISortItemData
6+
public interface ISortItemData : IComparable<ISortItemData>
77
{
88
public ISortItemKey Key { get;}
99

@@ -28,7 +28,7 @@ public interface ISortItemData<out TKey> : ISortItemData
2828
/// This class is used to represent an entry in a stored sort order.
2929
/// </summary>
3030
/// <typeparam name="TKey"></typeparam>
31-
public class SortItemData<TKey> : ISortItemData<TKey>
31+
public class SortItemData<TKey> : ISortItemData<TKey>, IComparable<SortItemData<TKey>>
3232
where TKey : IEquatable<TKey>, ISortItemKey
3333
{
3434
public SortItemData(TKey key, int sortIndex)
@@ -40,4 +40,18 @@ public SortItemData(TKey key, int sortIndex)
4040
public TKey Key { get; }
4141

4242
public int SortIndex { get; set; }
43+
44+
public int CompareTo(SortItemData<TKey>? other)
45+
{
46+
if (ReferenceEquals(this, other)) return 0;
47+
if (other is null) return 1;
48+
return SortIndex.CompareTo(other.SortIndex);
49+
}
50+
51+
public int CompareTo(ISortItemData? other)
52+
{
53+
if (ReferenceEquals(this, other)) return 0;
54+
if (other is null) return 1;
55+
return SortIndex.CompareTo(other.SortIndex);
56+
}
4357
}

src/NexusMods.Abstractions.Games/SortOrder/ISortOrderManager.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Collections.Immutable;
12
using DynamicData.Kernel;
23
using NexusMods.Abstractions.Loadouts;
34

@@ -21,12 +22,12 @@ public interface ISortOrderManager
2122
/// One instance of ISortOrderVariety for each variety.
2223
/// </summary>
2324
/// <returns></returns>
24-
public ReadOnlySpan<ISortOrderVariety> GetSortOrderVarieties();
25+
public IReadOnlyList<ISortOrderVariety> GetSortOrderVarieties();
2526

2627
/// <summary>
2728
/// Sets the sort order varieties for the current game.
2829
/// Should only be called once during initialization of the game.
2930
/// </summary>
3031
/// <param name="sortOrderVarieties"></param>
31-
public void RegisterSortOrderVarieties(ISortOrderVariety[] sortOrderVarieties);
32+
public void RegisterSortOrderVarieties(ISortOrderVariety[] sortOrderVarieties, IGame game);
3233
}

src/NexusMods.Abstractions.Games/SortOrder/ISortOrderVariety.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,11 @@ public ValueTask MoveItems(
100100
[Pure]
101101
public ValueTask ReconcileSortOrder(SortOrderId sortOrderId, IDb? db = null, CancellationToken token = default);
102102

103-
103+
/// <summary>
104+
/// Deletes the SortOrder and all child SortOrderItems.
105+
/// </summary>
106+
[Pure]
107+
public ValueTask DeleteSortOrder(SortOrderId sortOrderId, CancellationToken token = default);
104108

105109
/// <summary>
106110
/// Static metadata for the sort order type that can be accessed by derived classes for reuse

0 commit comments

Comments
 (0)