Skip to content

Commit

Permalink
Merge pull request #1650 from erri120/feat/1648-name
Browse files Browse the repository at this point in the history
Use mod name from SMAPI API
  • Loading branch information
erri120 authored Jun 19, 2024
2 parents 08585a6 + 91262cb commit ecda5ec
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 39 deletions.
9 changes: 6 additions & 3 deletions src/Games/NexusMods.Games.StardewValley/Diagnostics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,14 @@ internal static partial class Diagnostics
.WithId(new DiagnosticId(Source, number: 1))
.WithTitle("Missing required dependency")
.WithSeverity(DiagnosticSeverity.Warning)
.WithSummary("Mod {Mod} is missing required dependency '{MissingDependency}'")
.WithDetails("You can download the latest version at {NexusModsDependencyUri}.")
.WithSummary("Mod {Mod} is missing required dependency '{MissingDependencyModName}'")
.WithDetails("""
You can download the latest version of '{MissingDependencyModName}' (`{MissingDependencyModId}`) at {NexusModsDependencyUri}.
""")
.WithMessageData(messageBuilder => messageBuilder
.AddDataReference<ModReference>("Mod")
.AddValue<string>("MissingDependency")
.AddValue<string>("MissingDependencyModName")
.AddValue<string>("MissingDependencyModId")
.AddValue<NamedLink>("NexusModsDependencyUri")
)
.Finish();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System.Collections.Immutable;
using System.Runtime.CompilerServices;
using DynamicData.Kernel;
using Metsys.Bson;
using Microsoft.Extensions.Logging;
using NexusMods.Abstractions.Diagnostics;
using NexusMods.Abstractions.Diagnostics.Emitters;
Expand Down Expand Up @@ -146,7 +148,7 @@ private async Task<IEnumerable<Diagnostic>> DiagnoseMissingDependencies(

cancellationToken.ThrowIfCancellationRequested();

var missingDependencyUris = await _smapiWebApi.GetModPageUrls(
var apiMods = await _smapiWebApi.GetModDetails(
os: _os,
gameVersion,
smapiVersion,
Expand All @@ -159,12 +161,17 @@ private async Task<IEnumerable<Diagnostic>> DiagnoseMissingDependencies(
return missingDependencies.Select(missingDependency =>
{
var mod = ModForId(loadout, modId);
var modDetails = apiMods.GetValueOrDefault(missingDependency);
// TODO: diagnostic even if the API doesn't return anything
if (modDetails?.Name is null) return null;

return Diagnostics.CreateMissingRequiredDependency(
Mod: mod.ToReference(loadout),
MissingDependency: missingDependency,
NexusModsDependencyUri: missingDependencyUris.GetValueOrDefault(missingDependency, Helpers.NexusModsLink)
MissingDependencyModId: modDetails.UniqueId,
MissingDependencyModName: modDetails.Name,
NexusModsDependencyUri: modDetails.NexusModsLink.ValueOr(() => Helpers.NexusModsLink)
);
});
}).Where(x => x is not null).Select(x => x!);
});
}

Expand Down Expand Up @@ -220,7 +227,7 @@ private async Task<IEnumerable<Diagnostic>> DiagnoseOutdatedDependencies(

cancellationToken.ThrowIfCancellationRequested();

var missingDependencyUris = await _smapiWebApi.GetModPageUrls(
var apiMods = await _smapiWebApi.GetModDetails(
os: _os,
gameVersion,
smapiVersion,
Expand All @@ -232,7 +239,7 @@ private async Task<IEnumerable<Diagnostic>> DiagnoseOutdatedDependencies(
Dependency: ModForId(loadout, tuple.DependencyModId).ToReference(loadout),
MinimumVersion: tuple.MinimumVersion.ToString(),
CurrentVersion: tuple.CurrentVersion.ToString(),
NexusModsDependencyUri: missingDependencyUris.GetValueOrDefault(tuple.DependencyId, Helpers.NexusModsLink)
NexusModsDependencyUri: apiMods.GetLink(tuple.DependencyId, defaultValue: Helpers.NexusModsLink)
));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Runtime.CompilerServices;
using DynamicData.Kernel;
using Microsoft.Extensions.Logging;
using NexusMods.Abstractions.Diagnostics;
using NexusMods.Abstractions.Diagnostics.Emitters;
Expand Down Expand Up @@ -92,8 +93,8 @@ public async IAsyncEnumerable<Diagnostic> Diagnose(Loadout.Model loadout, [Enume
list.Add((mod, manifest, versionedFields));
}

var modPageUrls = await _smapiWebApi.GetModPageUrls(
_os,
var apiMods = await _smapiWebApi.GetModDetails(
os: _os,
gameVersion,
smapiVersion,
smapiIDs: list.Select(tuple => tuple.Item2.UniqueID).ToArray()
Expand All @@ -116,7 +117,7 @@ public async IAsyncEnumerable<Diagnostic> Diagnose(Loadout.Model loadout, [Enume
yield return Diagnostics.CreateModCompatabilityAssumeBroken(
Mod: mod.ToReference(loadout),
ReasonPhrase: reasonPhrase ?? "it's no longer compatible",
ModLink: modPageUrls.GetValueOrDefault(manifest.UniqueID, DefaultWikiLink),
ModLink:apiMods.GetLink(manifest.UniqueID, defaultValue: DefaultWikiLink),
ModVersion: manifest.Version.ToString()
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Runtime.CompilerServices;
using DynamicData.Kernel;
using JetBrains.Annotations;
using Microsoft.Extensions.Logging;
using NexusMods.Abstractions.Diagnostics;
Expand Down Expand Up @@ -49,11 +50,11 @@ public async IAsyncEnumerable<Diagnostic> Diagnose(Loadout.Model loadout, [Enume
.GetAllManifestsAsync(_logger, _fileStore, loadout, onlyEnabledMods: true, cancellationToken)
.ToArrayAsync(cancellationToken);

var modPageUrls = await _smapiWebApi.GetModPageUrls(
_os,
var apiMods = await _smapiWebApi.GetModDetails(
os: _os,
gameVersion,
smapiVersion,
smapiMods.Select(tuple => tuple.Item2.UniqueID).ToArray()
smapiIDs: smapiMods.Select(tuple => tuple.Item2.UniqueID).ToArray()
);

foreach (var tuple in smapiMods)
Expand All @@ -70,7 +71,7 @@ public async IAsyncEnumerable<Diagnostic> Diagnose(Loadout.Model loadout, [Enume
ModName: mod.Name,
MinimumAPIVersion: minimumApiVersion.ToString(),
CurrentSMAPIVersion: smapiVersion.ToString(),
NexusModsLink: modPageUrls.GetValueOrDefault(manifest.UniqueID, Helpers.NexusModsLink),
NexusModsLink: apiMods.GetLink(manifest.UniqueID, defaultValue: Helpers.NexusModsLink),
SMAPINexusModsLink: Helpers.SMAPILink
);
}
Expand All @@ -82,7 +83,7 @@ public async IAsyncEnumerable<Diagnostic> Diagnose(Loadout.Model loadout, [Enume
ModName: mod.Name,
MinimumGameVersion: minimumGameVersion.ToString(),
CurrentGameVersion: gameVersion.ToString(),
NexusModsLink: modPageUrls.GetValueOrDefault(manifest.UniqueID, Helpers.NexusModsLink)
NexusModsLink: apiMods.GetLink(manifest.UniqueID, defaultValue: Helpers.NexusModsLink)
);
}
}
Expand Down
26 changes: 24 additions & 2 deletions src/Games/NexusMods.Games.StardewValley/WebAPI/ISMAPIWebApi.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using DynamicData.Kernel;
using JetBrains.Annotations;
using NexusMods.Abstractions.Diagnostics.Values;
using NexusMods.Paths;
Expand All @@ -12,12 +13,33 @@ namespace NexusMods.Games.StardewValley.WebAPI;
public interface ISMAPIWebApi : IDisposable
{
/// <summary>
/// Gets all mod page urls of the given mods.
/// Fetches details for mods using their IDs.
/// </summary>
public Task<IReadOnlyDictionary<string, NamedLink>> GetModPageUrls(
public Task<IReadOnlyDictionary<string, SMAPIWebApiMod>> GetModDetails(
IOSInformation os,
ISemanticVersion gameVersion,
ISemanticVersion smapiVersion,
string[] smapiIDs
);
}

public record SMAPIWebApiMod
{
public required string UniqueId { get; init; }

public required string? Name { get; init; }

public required Optional<NamedLink> NexusModsLink { get; init; }
}

public static class SMAPIWebApiExtensions
{
public static NamedLink GetLink(this IReadOnlyDictionary<string, SMAPIWebApiMod> mods, string id, NamedLink defaultValue)
{
var mod = mods.GetValueOrDefault(id);
if (mod is null) return defaultValue;

var link = mod.NexusModsLink;
return link.HasValue ? link.Value : defaultValue;
}
}
46 changes: 27 additions & 19 deletions src/Games/NexusMods.Games.StardewValley/WebAPI/SMAPIWebApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ internal sealed class SMAPIWebApi : ISMAPIWebApi
private bool _isDisposed;
private WebApiClient? _client;

private ImmutableDictionary<string, NamedLink> _knownModPageUrls = ImmutableDictionary<string, NamedLink>.Empty.WithComparers(StringComparer.OrdinalIgnoreCase);
private ImmutableDictionary<string, SMAPIWebApiMod> _cache = ImmutableDictionary<string, SMAPIWebApiMod>.Empty.WithComparers(StringComparer.OrdinalIgnoreCase);

public SMAPIWebApi(ILogger<SMAPIWebApi> logger)
{
_logger = logger;
}

public async Task<IReadOnlyDictionary<string, NamedLink>> GetModPageUrls(
public async Task<IReadOnlyDictionary<string, SMAPIWebApiMod>> GetModDetails(
IOSInformation os,
ISemanticVersion gameVersion,
ISemanticVersion smapiVersion,
Expand All @@ -45,7 +45,7 @@ public async Task<IReadOnlyDictionary<string, NamedLink>> GetModPageUrls(
);

var mods = smapiIDs
.Where(id => !_knownModPageUrls.ContainsKey(id))
.Where(id => !_cache.ContainsKey(id))
.Select(id => new ModSearchEntryModel(
id: id,
installedVersion: null,
Expand Down Expand Up @@ -76,36 +76,44 @@ public async Task<IReadOnlyDictionary<string, NamedLink>> GetModPageUrls(
if (apiResult is not null)
{
var tmp = apiResult
.Select<KeyValuePair<string, ModEntryModel>, ValueTuple<string?, Optional<NamedLink>>>(kv =>
.Select(kv =>
{
var (id, model) = kv;
var metadata = model.Metadata;

var nexusId = metadata?.NexusID;
if (nexusId is null) return (null, Optional<NamedLink>.None);

var uri = NexusModsUrlBuilder.CreateDiagnosticUri(StardewValley.GameDomain.Value, nexusId.Value.ToString());
return (id, uri.WithName("Nexus Mods"));
var nexusModsLink = Optional<NamedLink>.None;

if (nexusId is not null)
{
var uri = NexusModsUrlBuilder.CreateDiagnosticUri(StardewValley.GameDomain.Value, nexusId.Value.ToString());
nexusModsLink = uri.WithName("Nexus Mods");
}

return new SMAPIWebApiMod
{
UniqueId = id,
Name = metadata?.Name,
NexusModsLink = nexusModsLink,
};
})
.Where(kv => kv.Item1 is not null)
.Select(tuple => new KeyValuePair<string, NamedLink>(tuple.Item1!, tuple.Item2.Value))
.ToDictionary();
.ToDictionary(x => x.UniqueId, x => x, StringComparer.OrdinalIgnoreCase);

ImmutableDictionary<string, NamedLink> initial, updated;
ImmutableDictionary<string, SMAPIWebApiMod> initial, updated;
do
{
initial = _knownModPageUrls;
updated = _knownModPageUrls.SetItems(tmp);
} while (initial != Interlocked.CompareExchange(ref _knownModPageUrls, updated, initial));
initial = _cache;
updated = _cache.SetItems(tmp);
} while (initial != Interlocked.CompareExchange(ref _cache, updated, initial));
}
}

return smapiIDs
.Distinct(StringComparer.OrdinalIgnoreCase)
.Select(id => (Id: id, Link: _knownModPageUrls.GetValueOrDefault(id)))
.Where(tuple => tuple.Link != default(NamedLink))
.Select(tuple => new KeyValuePair<string, NamedLink>(tuple.Id, tuple.Link))
.ToDictionary(StringComparer.OrdinalIgnoreCase);
.Select(id => _cache.GetValueOrDefault(id))
.Where(mod => mod is not null)
.Select(mod => mod!)
.ToDictionary(mod => mod.UniqueId, mod => mod, StringComparer.OrdinalIgnoreCase);
}

private static Platform ToPlatform(IOSInformation os)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ public void Format(IDiagnosticWriter writer, ref DiagnosticWriterState state, Mo
{
// TODO: custom markdown control
var mod = conn.Db.Get(value.DataId);
writer.Write(ref state, mod?.Name ?? "MISSING MOD");
writer.Write(ref state, $"'{mod.Name}'");
}
}

0 comments on commit ecda5ec

Please sign in to comment.