Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into start-adding-v2-types
Browse files Browse the repository at this point in the history
  • Loading branch information
Sewer56 committed Sep 30, 2024
2 parents 2c96668 + b7ab993 commit 22bc785
Show file tree
Hide file tree
Showing 15 changed files with 241 additions and 83 deletions.
3 changes: 2 additions & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<PackageVersion Include="AvaloniaEdit.TextMate" Version="11.1.0" />
<PackageVersion Include="FlatSharp.Compiler" Version="7.6.0" />
<PackageVersion Include="FlatSharp.Runtime" Version="7.6.0" />
<PackageVersion Include="GameFinder.Launcher.Heroic" Version="4.3.0" />
<PackageVersion Include="LinqGen" Version="0.3.1" />
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="8.8.0" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="8.0.0" />
Expand Down Expand Up @@ -120,7 +121,7 @@
<PackageVersion Include="BitFaster.Caching" Version="2.5.0" />
<PackageVersion Include="CliWrap" Version="3.6.6" />
<PackageVersion Include="DynamicData" Version="9.0.4" />
<PackageVersion Include="GameFinder" Version="4.2.4" />
<PackageVersion Include="GameFinder" Version="4.3.0" />
<PackageVersion Include="Humanizer" Version="2.14.1" />
<PackageVersion Include="ini-parser-netstandard" Version="2.5.2" />
<PackageVersion Include="Mutagen.Bethesda.Skyrim" Version="0.44.0" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ public record GOGLocatorResultMetadata : IGameLocatorResultMetadata
/// </summary>
public required long Id { get; init; }
}

[PublicAPI]
public record HeroicGOGLocatorResultMetadata : GOGLocatorResultMetadata;
23 changes: 20 additions & 3 deletions src/Abstractions/NexusMods.Abstractions.Games/RunGameTool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Microsoft.Extensions.Logging;
using NexusMods.Abstractions.GameLocators;
using NexusMods.Abstractions.Games.DTO;
using NexusMods.Abstractions.Games.Stores.GOG;
using NexusMods.Abstractions.Games.Stores.Steam;
using NexusMods.Abstractions.Loadouts;
using NexusMods.CrossPlatform.Process;
Expand Down Expand Up @@ -60,10 +61,18 @@ public async Task Execute(Loadout.ReadOnly loadout, CancellationToken cancellati
var program = await GetGamePath(loadout);
var primaryFile = _game.GetPrimaryFile(loadout.InstallationInstance.Store).CombineChecked(loadout.InstallationInstance);

if (OSInformation.Shared.IsLinux && program.Equals(primaryFile) && loadout.InstallationInstance.LocatorResultMetadata is SteamLocatorResultMetadata steamLocatorResultMetadata)
if (OSInformation.Shared.IsLinux && program.Equals(primaryFile))
{
await RunThroughSteam(steamLocatorResultMetadata.AppId, cancellationToken);
return;
var locator = loadout.InstallationInstance.LocatorResultMetadata;
switch (locator)
{
case SteamLocatorResultMetadata steamLocatorResultMetadata:
await RunThroughSteam(steamLocatorResultMetadata.AppId, cancellationToken);
return;
case HeroicGOGLocatorResultMetadata heroicGOGLocatorResultMetadata:
await RunThroughHeroic("gog", heroicGOGLocatorResultMetadata.Id, cancellationToken);
return;
}
}

var names = new HashSet<string>
Expand Down Expand Up @@ -177,6 +186,14 @@ private async Task RunThroughSteam(uint appId, CancellationToken cancellationTok
await reaper.WaitForExitAsync(cancellationToken);
}

private async Task RunThroughHeroic(string type, long appId, CancellationToken cancellationToken)
{
Debug.Assert(OSInformation.Shared.IsLinux);

// TODO: track process
await _osInterop.OpenUrl(new Uri($"heroic://launch/{type}/{appId.ToString(CultureInfo.InvariantCulture)}"), fireAndForget: true, cancellationToken: cancellationToken);
}

private async ValueTask<Process?> WaitForProcessToStart(
string processName,
TimeSpan timeout,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ public readonly struct GraphQLResolver(ITransaction Tx, ReadOnlyModel Model)
/// <summary>
/// Create a new resolver using the given primary key attribute and value.
/// </summary>
public static GraphQLResolver Create<THighLevel, TLowLevel>(IDb referenceDb, ITransaction tx, ScalarAttribute<THighLevel, TLowLevel> primaryKeyAttribute, THighLevel primaryKeyValue) where THighLevel : notnull
public static GraphQLResolver Create<THighLevel, TLowLevel>(IDb db, ITransaction tx, ScalarAttribute<THighLevel, TLowLevel> primaryKeyAttribute, THighLevel primaryKeyValue) where THighLevel : notnull
{
var existing = referenceDb.Datoms(primaryKeyAttribute, primaryKeyValue);
var existing = db.Datoms(primaryKeyAttribute, primaryKeyValue);
var exists = existing.Count > 0;
var id = existing.Count == 0 ? tx.TempId() : existing[0].E;
if (!exists)
tx.Add(id, primaryKeyAttribute, primaryKeyValue);
return new GraphQLResolver(tx, new ReadOnlyModel(referenceDb, id));
return new GraphQLResolver(tx, new ReadOnlyModel(db, id));
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using NexusMods.Abstractions.Games.DTO;
using NexusMods.Abstractions.NexusModsLibrary;
using NexusMods.Abstractions.NexusModsLibrary.Models;
using NexusMods.Abstractions.NexusWebApi.Types;
using NexusMods.MnemonicDB.Abstractions;
using NexusMods.Paths;

namespace NexusMods.Networking.NexusWebApi.Extensions;

/// <summary>
/// Extensions to GraphQL fragments.
/// </summary>
public static class FragmentExtensions
{
/// <summary>
/// Resolves the IUserFragment to an entity in the database, inserting or updating as necessary.
/// </summary>
public static async Task<EntityId> Resolve(this IUserFragment userFragment, IDb db, ITransaction tx, HttpClient client, CancellationToken token)
{
var userResolver = GraphQLResolver.Create(db, tx, User.NexusId, (ulong)userFragment.MemberId);
userResolver.Add(User.Name, userFragment.Name);
userResolver.Add(User.Avatar, new Uri(userFragment.Avatar));

var avatarImage = await DownloadImage(client, userFragment.Avatar, token);
userResolver.Add(User.AvatarImage,avatarImage);
return userResolver.Id;
}

/// <summary>
/// Resolves the IModFragment to an entity in the database, inserting or updating as necessary.
/// </summary>
public static EntityId Resolve(this IModFileFragment modFileFragment, IDb db, ITransaction tx, EntityId modEId)
{
var nexusFileResolver = GraphQLResolver.Create(db, tx, (NexusModsFileMetadata.FileId, FileId.From((ulong)modFileFragment.FileId)), (NexusModsFileMetadata.ModPageId, modEId));

Check failure on line 34 in src/Networking/NexusMods.Networking.NexusWebApi/Extensions/FragmentExtensions.cs

View workflow job for this annotation

GitHub Actions / build

The name 'FileId' does not exist in the current context

Check failure on line 34 in src/Networking/NexusMods.Networking.NexusWebApi/Extensions/FragmentExtensions.cs

View workflow job for this annotation

GitHub Actions / build-and-test / Build and Test (ubuntu-latest)

The name 'FileId' does not exist in the current context

Check failure on line 34 in src/Networking/NexusMods.Networking.NexusWebApi/Extensions/FragmentExtensions.cs

View workflow job for this annotation

GitHub Actions / build-and-test / Build and Test (ubuntu-latest)

The name 'FileId' does not exist in the current context

Check failure on line 34 in src/Networking/NexusMods.Networking.NexusWebApi/Extensions/FragmentExtensions.cs

View workflow job for this annotation

GitHub Actions / build-and-test / Build and Test (macos-latest)

The name 'FileId' does not exist in the current context

Check failure on line 34 in src/Networking/NexusMods.Networking.NexusWebApi/Extensions/FragmentExtensions.cs

View workflow job for this annotation

GitHub Actions / build-and-test / Build and Test (macos-latest)

The name 'FileId' does not exist in the current context
nexusFileResolver.Add(NexusModsFileMetadata.ModPageId, modEId);
nexusFileResolver.Add(NexusModsFileMetadata.Name, modFileFragment.Name);
nexusFileResolver.Add(NexusModsFileMetadata.Version, modFileFragment.Version);
if (ulong.TryParse(modFileFragment.SizeInBytes, out var size))
nexusFileResolver.Add(NexusModsFileMetadata.Size, Size.From(size));
return nexusFileResolver.Id;
}

/// <summary>
/// Resolves the IModFragment to an entity in the database, inserting or updating as necessary.
/// </summary>
public static EntityId Resolve(this IModFragment modFragment, IDb db, ITransaction tx)
{
var nexusModResolver = GraphQLResolver.Create(db, tx, NexusModsModPageMetadata.ModId,
ModId.From((ulong)modFragment.ModId));

Check failure on line 49 in src/Networking/NexusMods.Networking.NexusWebApi/Extensions/FragmentExtensions.cs

View workflow job for this annotation

GitHub Actions / build

The name 'ModId' does not exist in the current context

Check failure on line 49 in src/Networking/NexusMods.Networking.NexusWebApi/Extensions/FragmentExtensions.cs

View workflow job for this annotation

GitHub Actions / build-and-test / Build and Test (ubuntu-latest)

The name 'ModId' does not exist in the current context

Check failure on line 49 in src/Networking/NexusMods.Networking.NexusWebApi/Extensions/FragmentExtensions.cs

View workflow job for this annotation

GitHub Actions / build-and-test / Build and Test (ubuntu-latest)

The name 'ModId' does not exist in the current context

Check failure on line 49 in src/Networking/NexusMods.Networking.NexusWebApi/Extensions/FragmentExtensions.cs

View workflow job for this annotation

GitHub Actions / build-and-test / Build and Test (macos-latest)

The name 'ModId' does not exist in the current context

Check failure on line 49 in src/Networking/NexusMods.Networking.NexusWebApi/Extensions/FragmentExtensions.cs

View workflow job for this annotation

GitHub Actions / build-and-test / Build and Test (macos-latest)

The name 'ModId' does not exist in the current context

nexusModResolver.Add(NexusModsModPageMetadata.Name, modFragment.Name);
nexusModResolver.Add(NexusModsModPageMetadata.GameDomain, GameDomain.From(modFragment.Game.DomainName));

if (Uri.TryCreate(modFragment.PictureUrl, UriKind.Absolute, out var fullSizedPictureUri))
nexusModResolver.Add(NexusModsModPageMetadata.FullSizedPictureUri, fullSizedPictureUri);

if (Uri.TryCreate(modFragment.ThumbnailUrl, UriKind.Absolute, out var thumbnailUri))
nexusModResolver.Add(NexusModsModPageMetadata.ThumbnailUri, thumbnailUri);
return nexusModResolver.Id;
}

private static async Task<byte[]> DownloadImage(HttpClient client, string? uri, CancellationToken token)
{
if (uri is null) return [];
if (!Uri.TryCreate(uri, UriKind.Absolute, out var imageUri)) return [];

return await client.GetByteArrayAsync(imageUri, token);
}

}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#import { UserFragment, ModFragment, ModFileFragment } from './CommonFragments.graphql';

# Pulls all the information we need about a collection revision.
query CollectionRevisionInfo($slug: String!, $revisionNumber: Int!, $viewAdultContent: Boolean!)
Expand All @@ -20,18 +21,9 @@ query CollectionRevisionInfo($slug: String!, $revisionNumber: Int!, $viewAdultCo
gameId,
fileId,
file {
name,
modId,
fileId,
version,
sizeInBytes,
...ModFileFragment
mod {
name
game {
domainName
}
thumbnailUrl
pictureUrl
...ModFragment
}
}
updatePolicy,
Expand All @@ -53,11 +45,8 @@ query CollectionRevisionInfo($slug: String!, $revisionNumber: Int!, $viewAdultCo
id
}
user {
name
avatar
memberId
...UserFragment
}

}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@

fragment UserFragment on User {
name
avatar
memberId
}

fragment ModFileFragment on ModFile {
name,
modId,
fileId,
version,
sizeInBytes
}

fragment ModFragment on Mod {
modId
name
game {
domainName
}
thumbnailUrl
pictureUrl
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#include { ModFragment } from './CommonFragments.graphql'

query ModInfo($gameDomain: String!, $modId: Int!)
{
legacyModsByDomain(ids: [{gameDomain: $gameDomain, modId: $modId}])
{
nodes
{
...ModFragment
}
}
}
53 changes: 16 additions & 37 deletions src/Networking/NexusMods.Networking.NexusWebApi/NexusModsLibrary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using NexusMods.Extensions.BCL;
using NexusMods.MnemonicDB.Abstractions;
using NexusMods.Networking.HttpDownloader;
using NexusMods.Networking.NexusWebApi.Extensions;
using NexusMods.Paths;
using User = NexusMods.Abstractions.NexusModsLibrary.Models.User;

Expand Down Expand Up @@ -49,28 +50,15 @@ public NexusModsLibrary(IServiceProvider serviceProvider)

using var tx = _connection.BeginTransaction();

var modInfo = await _apiClient.ModInfoAsync(gameDomain.ToString(), modId, cancellationToken);

var newModPage = new NexusModsModPageMetadata.New(tx)
{
Name = modInfo.Data.Name,
ModId = modId,
GameDomain = gameDomain,
};

if (Uri.TryCreate(modInfo.Data.PictureUrl, UriKind.Absolute, out var fullSizedPictureUri))
var modInfo = await _gqlClient.ModInfo.ExecuteAsync(gameDomain.ToString(), (int)modId.Value, cancellationToken);
EntityId first = default;
foreach (var node in modInfo.Data!.LegacyModsByDomain.Nodes)
{
newModPage.FullSizedPictureUri = fullSizedPictureUri;

var thumbnailUrl = modInfo.Data.PictureUrl.Replace("/images/", "/images/thumbnails/", StringComparison.OrdinalIgnoreCase);
if (Uri.TryCreate(thumbnailUrl, UriKind.Absolute, out var thumbnailUri))
{
newModPage.ThumbnailUri = thumbnailUri;
}
first = node.Resolve(_connection.Db, tx);
}

var txResults = await tx.Commit();
return txResults.Remap(newModPage);
return NexusModsModPageMetadata.Load(txResults.Db, txResults[first]);
}

/// <summary>
Expand All @@ -89,7 +77,6 @@ public NexusModsLibrary(IServiceProvider serviceProvider)
var db = _connection.Db;
var collectionInfo = info.Data!.CollectionRevision.Collection;
var collectionTileImage = DownloadImage(collectionInfo.TileImage?.ThumbnailUrl, token);
var avatarImage = DownloadImage(collectionInfo.User.Avatar, token);
var collectionBackgroundImage = DownloadImage(collectionInfo.HeaderImage?.Url, token);

// Remap the collection info
Expand All @@ -100,13 +87,8 @@ public NexusModsLibrary(IServiceProvider serviceProvider)
collectionResolver.Add(CollectionMetadata.TileImage, await collectionTileImage);
collectionResolver.Add(CollectionMetadata.BackgroundImage, await collectionBackgroundImage);

// Remap the user info
var userResolver = GraphQLResolver.Create(db, tx, User.NexusId, (ulong)collectionInfo.User.MemberId);
userResolver.Add(User.Name, collectionInfo.User.Name);
userResolver.Add(User.Avatar, new Uri(collectionInfo.User.Avatar));
userResolver.Add(User.AvatarImage, await avatarImage);

collectionResolver.Add(CollectionMetadata.Author, userResolver.Id);
var user = await collectionInfo.User.Resolve(db, tx, _httpClient, token);
collectionResolver.Add(CollectionMetadata.Author, user);

// Remap the revision info
var revisionInfo = info.Data!.CollectionRevision;
Expand All @@ -123,37 +105,34 @@ public NexusModsLibrary(IServiceProvider serviceProvider)
{
var fileInfo = file.File!;
var modInfo = fileInfo.Mod;
var nexusModResolver = GraphQLResolver.Create(db, tx, NexusModsModPageMetadata.ModId, ModId.From((uint)fileInfo.ModId));
var nexusModResolver = GraphQLResolver.Create(db, tx, NexusModsModPageMetadata.ModId, ModId.From((ulong)fileInfo.ModId));

Check failure on line 108 in src/Networking/NexusMods.Networking.NexusWebApi/NexusModsLibrary.cs

View workflow job for this annotation

GitHub Actions / build

Argument 1: cannot convert from 'ulong' to 'uint'

Check failure on line 108 in src/Networking/NexusMods.Networking.NexusWebApi/NexusModsLibrary.cs

View workflow job for this annotation

GitHub Actions / build-and-test / Build and Test (ubuntu-latest)

Argument 1: cannot convert from 'ulong' to 'uint'

Check failure on line 108 in src/Networking/NexusMods.Networking.NexusWebApi/NexusModsLibrary.cs

View workflow job for this annotation

GitHub Actions / build-and-test / Build and Test (ubuntu-latest)

Argument 1: cannot convert from 'ulong' to 'uint'

Check failure on line 108 in src/Networking/NexusMods.Networking.NexusWebApi/NexusModsLibrary.cs

View workflow job for this annotation

GitHub Actions / build-and-test / Build and Test (macos-latest)

Argument 1: cannot convert from 'ulong' to 'uint'

Check failure on line 108 in src/Networking/NexusMods.Networking.NexusWebApi/NexusModsLibrary.cs

View workflow job for this annotation

GitHub Actions / build-and-test / Build and Test (macos-latest)

Argument 1: cannot convert from 'ulong' to 'uint'
nexusModResolver.Add(NexusModsModPageMetadata.Name, modInfo.Name);
nexusModResolver.Add(NexusModsModPageMetadata.GameDomain, GameDomain.From(modInfo.Game.DomainName));

if (Uri.TryCreate(modInfo.PictureUrl, UriKind.Absolute, out var fullSizedPictureUri))
nexusModResolver.Add(NexusModsModPageMetadata.FullSizedPictureUri, fullSizedPictureUri);
var modEId = fileInfo.Mod.Resolve(db, tx);
var modfile = fileInfo.Resolve(db, tx, modEId);

if (Uri.TryCreate(modInfo.ThumbnailUrl, UriKind.Absolute, out var thumbnailUri))
nexusModResolver.Add(NexusModsModPageMetadata.ThumbnailUri, thumbnailUri);


var nexusFileResolver = GraphQLResolver.Create(db, tx, (NexusModsFileMetadata.FileId, FileId.From((uint)fileInfo.FileId)), (NexusModsFileMetadata.ModPageId, nexusModResolver.Id));
var nexusFileResolver = GraphQLResolver.Create(db, tx, (NexusModsFileMetadata.FileId, FileId.From((ulong)fileInfo.FileId)), (NexusModsFileMetadata.ModPageId, nexusModResolver.Id));

Check failure on line 119 in src/Networking/NexusMods.Networking.NexusWebApi/NexusModsLibrary.cs

View workflow job for this annotation

GitHub Actions / build

Argument 1: cannot convert from 'ulong' to 'uint'

Check failure on line 119 in src/Networking/NexusMods.Networking.NexusWebApi/NexusModsLibrary.cs

View workflow job for this annotation

GitHub Actions / build-and-test / Build and Test (ubuntu-latest)

Argument 1: cannot convert from 'ulong' to 'uint'

Check failure on line 119 in src/Networking/NexusMods.Networking.NexusWebApi/NexusModsLibrary.cs

View workflow job for this annotation

GitHub Actions / build-and-test / Build and Test (ubuntu-latest)

Argument 1: cannot convert from 'ulong' to 'uint'

Check failure on line 119 in src/Networking/NexusMods.Networking.NexusWebApi/NexusModsLibrary.cs

View workflow job for this annotation

GitHub Actions / build-and-test / Build and Test (macos-latest)

Argument 1: cannot convert from 'ulong' to 'uint'

Check failure on line 119 in src/Networking/NexusMods.Networking.NexusWebApi/NexusModsLibrary.cs

View workflow job for this annotation

GitHub Actions / build-and-test / Build and Test (macos-latest)

Argument 1: cannot convert from 'ulong' to 'uint'
nexusFileResolver.Add(NexusModsFileMetadata.ModPageId, nexusModResolver.Id);
nexusFileResolver.Add(NexusModsFileMetadata.Name, fileInfo.Name);
nexusFileResolver.Add(NexusModsFileMetadata.Version, fileInfo.Version);
nexusFileResolver.Add(NexusModsFileMetadata.Size, Size.FromLong(long.Parse(fileInfo.SizeInBytes!)));

var revisionFileResolver = GraphQLResolver.Create(db, tx, CollectionRevisionModFile.FileId, ulong.Parse(file.Id));
revisionFileResolver.Add(CollectionRevisionModFile.CollectionRevision, revisionResolver.Id);
revisionFileResolver.Add(CollectionRevisionModFile.NexusModFile, nexusFileResolver.Id);
revisionFileResolver.Add(CollectionRevisionModFile.NexusModFile, modfile);
revisionFileResolver.Add(CollectionRevisionModFile.IsOptional, file.Optional);
}

var txResults = await tx.Commit();
return CollectionRevisionMetadata.Load(txResults.Db, txResults[revisionResolver.Id]);
}

/// <summary>
/// Load an image from a URI
/// </summary>
public async Task<byte[]> DownloadImage(string? uri, CancellationToken token)

private async Task<byte[]> DownloadImage(string? uri, CancellationToken token)
{
if (uri is null) return [];
if (!Uri.TryCreate(uri, UriKind.Absolute, out var imageUri)) return [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@
<Border x:Name="CategoryTextBorder">
<TextBlock x:Name="CategoryText" />
</Border>
<panels:FlexPanel x:Name="SummaryTextFlexPanel">
<Border x:Name="SummaryTextFlexPanel">
<TextBlock x:Name="SummaryText" />
</panels:FlexPanel>
</Border>
</panels:FlexPanel>

</panels:FlexPanel>
Expand Down
Loading

0 comments on commit 22bc785

Please sign in to comment.