Skip to content

Commit

Permalink
Fix nxm downloads not opening the App if app is closed (#1644)
Browse files Browse the repository at this point in the history
* Fix DI missing <LoggingSettings> for slim process

* Fix NXM downloads not starting if app is closed:
- The DL was being skipped due to login check
- LoginManger wouldn't have data untill DB was initialized
- Had to make the LoginManger into a HostedService and await in StartAsync on the Repository.
- This initialized the LoginData from DB, but CliServer StartAsync started listening before that.
- Changed CliServer to require explicit StartCliServerAsync call in Program.cs
- CliServer is only started after all the other HostedServices have initialized.
- Removed hack workaround where CliServer was waiting for GameRegistry to initialize in StartAsync.

* Test fixes

* More fixes

* Fix cli tests

* Fix Networking WebApi tests

* Fix more tests

* Fix all remaining tests
  • Loading branch information
Al12rs authored Jun 18, 2024
1 parent 680498d commit 8e721da
Show file tree
Hide file tree
Showing 19 changed files with 67 additions and 35 deletions.
32 changes: 30 additions & 2 deletions src/Networking/NexusMods.Networking.NexusWebApi/LoginManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Reactive.Linq;
using DynamicData.Binding;
using JetBrains.Annotations;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using NexusMods.Abstractions.MnemonicDB.Attributes;
using NexusMods.Abstractions.NexusWebApi;
Expand All @@ -19,14 +20,15 @@ namespace NexusMods.Networking.NexusWebApi;
/// Component for handling login and logout from the Nexus Mods
/// </summary>
[PublicAPI]
public sealed class LoginManager : IDisposable, ILoginManager
public sealed class LoginManager : IDisposable, ILoginManager, IHostedService
{
private readonly ILogger<LoginManager> _logger;
private readonly OAuth _oauth;
private readonly IProtocolRegistration _protocolRegistration;
private readonly NexusApiClient _nexusApiClient;
private readonly IAuthenticatingMessageFactory _msgFactory;

private Task? _startupTask = null;

private CompositeDisposable _subscriptions = new CompositeDisposable();


Expand Down Expand Up @@ -173,4 +175,30 @@ public void Dispose()
_verifySemaphore.Dispose();
_subscriptions.Dispose();
}

/// <inheritdoc />
public async Task StartAsync(CancellationToken cancellationToken)
{
lock (this)
{
_startupTask ??= Startup(cancellationToken);
}
await _startupTask;
}

private async Task Startup(CancellationToken cancellationToken)
{
await ((IHostedService)_jwtTokenRepository).StartAsync(cancellationToken);
var userInfo = await Verify(cancellationToken);
if (userInfo is not null)
{
UserInfo = userInfo;
}
}

/// <inheritdoc />
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public static IServiceCollection AddNexusWebApi(this IServiceCollection collecti

return collection
.AddAllSingleton<ILoginManager, LoginManager>()
.AddHostedService(s => (LoginManager)s.GetRequiredService<ILoginManager>())
.AddAllSingleton<INexusApiClient, NexusApiClient>()
.AddNexusApiVerbs();
}
Expand Down
4 changes: 4 additions & 0 deletions src/NexusMods.App/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ public static int Main(string[] args)

// Okay to do wait here, as we are in the main process thread.
host.StartAsync().Wait(timeout: TimeSpan.FromMinutes(5));

// Start the CLI server if we are the main process.
var cliServer = services.GetService<CliServer>();
cliServer?.StartCliServerAsync().Wait(timeout: TimeSpan.FromSeconds(5));

_logger = services.GetRequiredService<ILogger<Program>>();
LogMessages.RuntimeInformation(_logger, RuntimeInformation.OSDescription, RuntimeInformation.FrameworkDescription);
Expand Down
3 changes: 2 additions & 1 deletion src/NexusMods.App/Services.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ public static IServiceCollection AddApp(this IServiceCollection services,
services.AddFileSystem()
.AddCrossPlatform()
.AddDefaultRenderers()
.AddSettingsManager();
.AddSettingsManager()
.AddSettings<LoggingSettings>();

if (!startupMode.IsAvaloniaDesigner)
services.AddSingleProcess(Mode.Client);
Expand Down
29 changes: 16 additions & 13 deletions src/NexusMods.SingleProcess/CliServer.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
using System.Net;
using System.Net.Sockets;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using NexusMods.Abstractions.GameLocators;
using NexusMods.Abstractions.Settings;
using NexusMods.ProxyConsole;
using NexusMods.SingleProcess.Exceptions;
Expand Down Expand Up @@ -49,6 +47,19 @@ public CliServer(

_settings = settingsManager.Get<CliSettings>();
}

/// <summary>
/// Starts the CLI server, listening for incoming connections.
/// This method needs to be called explicitly to start the server.
/// </summary>
public async Task StartCliServerAsync()
{
if (!_started)
{
_started = true;
await StartTcpListenerAsync();
}
}

private Task StartTcpListenerAsync()
{
Expand Down Expand Up @@ -135,19 +146,11 @@ private void CleanClosedConnections()
_runningClients.Remove(task);
}
}

/// <inheritdoc />
public async Task StartAsync(CancellationToken cancellationToken)
public Task StartAsync(CancellationToken cancellationToken)
{
// Bit of a hack, but works for now till we get better startup logic with DI
var registry = _serviceProvider.GetRequiredService<IGameRegistry>();
await ((IHostedService)registry).StartAsync(cancellationToken);

if (!_started && _settings.StartCliBackend)
{
_started = true;
await StartTcpListenerAsync();
}
return Task.CompletedTask;
}

/// <inheritdoc />
Expand Down
1 change: 1 addition & 0 deletions src/NexusMods.SingleProcess/Services.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public static IServiceCollection AddSingleProcess(this IServiceCollection servic
switch (mode)
{
case Mode.Main:
services.AddSingleton<CliServer>();
services.AddHostedService<CliServer>();
break;
case Mode.Client:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ public void ConfigureServices(IServiceCollection services)
.AddSingleton<CommandLineConfigurator>()
.AddBethesdaGameStudios()
.AddGames()
.AddActivityMonitor()
.AddSerializationAbstractions()
.AddInstallerTypes()
.AddGenericGameSupport()
Expand All @@ -39,7 +38,6 @@ public void ConfigureServices(IServiceCollection services)
.AddCLI()
.AddSingleton<IGuidedInstaller, NullGuidedInstaller>()
.AddLogging(builder => builder.AddXunitOutput())
.AddCrossPlatform()
.Validate();
}
}
2 changes: 0 additions & 2 deletions tests/Games/NexusMods.Games.BladeAndSorcery.Tests/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,8 @@ public void ConfigureServices(IServiceCollection container)
.AddGames()
.AddFileStoreAbstractions()
.AddLoadoutAbstractions()
.AddActivityMonitor()
.AddSerializationAbstractions()
.AddInstallerTypes()
.AddCrossPlatform()
.Validate();
}
}
2 changes: 0 additions & 2 deletions tests/Games/NexusMods.Games.DarkestDungeon.Tests/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,10 @@ public void ConfigureServices(IServiceCollection container)
.AddDarkestDungeon()
.AddLogging(builder => builder.AddXUnit())
.AddGames()
.AddActivityMonitor()
.AddFileStoreAbstractions()
.AddLoadoutAbstractions()
.AddSerializationAbstractions()
.AddInstallerTypes()
.AddCrossPlatform()
.Validate();
}
}
3 changes: 0 additions & 3 deletions tests/Games/NexusMods.Games.FOMOD.Tests/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using NexusMods.Abstractions.Installers;
using NexusMods.Abstractions.Loadouts;
using NexusMods.Abstractions.Serialization;
using NexusMods.Activities;
using NexusMods.App.BuildInfo;
using NexusMods.CrossPlatform;
using NexusMods.Games.BethesdaGameStudios;
Expand All @@ -23,7 +22,6 @@ public class Startup
public void ConfigureServices(IServiceCollection container)
{
container
.AddCrossPlatform()
.AddLoadoutAbstractions()
.AddDefaultServicesForTesting()
.AddBethesdaGameStudios()
Expand All @@ -34,7 +32,6 @@ public void ConfigureServices(IServiceCollection container)
.AddSingleton<ICoreDelegates, MockDelegates>()
.AddLogging(builder => builder.AddXunitOutput().SetMinimumLevel(LogLevel.Debug))
.AddGames()
.AddActivityMonitor()
.AddSerializationAbstractions()
.AddInstallerTypes()
.Validate();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,10 @@ public void ConfigureServices(IServiceCollection services)
.AddMountAndBladeBannerlord()
.AddLogging(builder => builder.AddXunitOutput())
.AddGames()
.AddActivityMonitor()
.AddSerializationAbstractions()
.AddFileStoreAbstractions()
.AddLoadoutAbstractions()
.AddInstallerTypes()
.AddCrossPlatform()
.Validate();
}
}
Expand Down
2 changes: 0 additions & 2 deletions tests/Games/NexusMods.Games.RedEngine.Tests/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,10 @@ public void ConfigureServices(IServiceCollection container)
.AddRedEngineGames()
.AddLogging(builder => builder.AddXUnit())
.AddGames()
.AddActivityMonitor()
.AddSerializationAbstractions()
.AddLoadoutAbstractions()
.AddFileStoreAbstractions()
.AddInstallerTypes()
.AddCrossPlatform()
.Validate();
}
}
Expand Down
2 changes: 0 additions & 2 deletions tests/Games/NexusMods.Games.Sifu.Tests/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,10 @@ public void ConfigureServices(IServiceCollection container)
.AddSifu()
.AddLogging(builder => builder.AddXUnit())
.AddGames()
.AddActivityMonitor()
.AddFileStoreAbstractions()
.AddLoadoutAbstractions()
.AddSerializationAbstractions()
.AddInstallerTypes()
.AddCrossPlatform()
.Validate();
}
}
2 changes: 0 additions & 2 deletions tests/Games/NexusMods.Games.StardewValley.Tests/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,10 @@ public void ConfigureServices(IServiceCollection container)
.AddStardewValley()
.AddLogging(builder => builder.AddXUnit())
.AddGames()
.AddActivityMonitor()
.AddFileStoreAbstractions()
.AddLoadoutAbstractions()
.AddSerializationAbstractions()
.AddInstallerTypes()
.AddCrossPlatform()
.Validate();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NexusMods.Abstractions.Activities;
using NexusMods.Abstractions.HttpDownloader;
using NexusMods.Abstractions.Loadouts.Synchronizers;
using NexusMods.Abstractions.Settings;
using NexusMods.Activities;
using NexusMods.CrossPlatform;
using NexusMods.DataModel;
using NexusMods.FileExtractor;
Expand Down Expand Up @@ -52,6 +54,9 @@ public static IServiceCollection AddDefaultServicesForTesting(this IServiceColle
.AddSingleton<HttpClient>()
.AddSingleton<TestModDownloader>()
.AddNexusWebApi(true)
.AddCrossPlatform()
.AddActivityMonitor()
.AddSettings<LoggingSettings>()
.AddHttpDownloader()
.AddDataModel()
.AddLoadoutsSynchronizers()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<ProjectReference Include="..\..\..\src\ArchiveManagement\NexusMods.FileExtractor\NexusMods.FileExtractor.csproj" />
<ProjectReference Include="..\..\..\src\Networking\NexusMods.Networking.HttpDownloader\NexusMods.Networking.HttpDownloader.csproj" />
<ProjectReference Include="..\..\..\src\Networking\NexusMods.Networking.NexusWebApi\NexusMods.Networking.NexusWebApi.csproj" />
<ProjectReference Include="..\..\..\src\NexusMods.Activities\NexusMods.Activities.csproj" />
<ProjectReference Include="..\..\..\src\NexusMods.DataModel\NexusMods.DataModel.csproj" />
<ProjectReference Include="..\..\..\src\NexusMods.Settings\NexusMods.Settings.csproj" />
<ProjectReference Include="..\..\NexusMods.StandardGameLocators.TestHelpers\NexusMods.StandardGameLocators.TestHelpers.csproj" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
using NexusMods.Abstractions.GuidedInstallers;
using NexusMods.Abstractions.Serialization;
using NexusMods.Abstractions.Serialization.ExpressionGenerator;
using NexusMods.Abstractions.Settings;
using NexusMods.Activities;
using NexusMods.App.BuildInfo;
using NexusMods.CrossPlatform;
using NexusMods.Extensions.DependencyInjection;
using NexusMods.Games.BethesdaGameStudios;
using NexusMods.Games.BethesdaGameStudios.SkyrimSpecialEdition;
Expand All @@ -26,7 +28,6 @@ public void ConfigureServices(IServiceCollection services)
.AddDefaultServicesForTesting()
.AddUniversalGameLocator<Cyberpunk2077>(new Version("1.61"))
.AddUniversalGameLocator<SkyrimSpecialEdition>(new Version("1.6.659.0"))
.AddActivityMonitor()
.AddStubbedGameLocators()
.AddBethesdaGameStudios()
.AddGenericGameSupport()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NexusMods.Abstractions.Loadouts;
using NexusMods.Abstractions.Settings;
using NexusMods.Activities;
using NexusMods.App.BuildInfo;
using NexusMods.CrossPlatform;
using NexusMods.CrossPlatform.Process;
using NexusMods.DataModel;
using NexusMods.Networking.HttpDownloader;
Expand All @@ -23,10 +25,11 @@ public void ConfigureServices(IServiceCollection services)
.AddSingleton<HttpClient>()
.AddHttpDownloader()
.AddSingleton<TemporaryFileManager>()
.AddSingleton<IProcessFactory, ProcessFactory>()
.AddSingleton<LocalHttpServer>()
.AddNexusWebApi(true)
.AddActivityMonitor()
.AddCrossPlatform()
.AddSettings<LoggingSettings>()
.AddLoadoutAbstractions()
.AddDataModel() // this is required because we're also using NMA integration
.AddLogging(builder => builder.AddXunitOutput()
Expand Down
1 change: 1 addition & 0 deletions tests/NexusMods.CLI.Tests/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public void ConfigureServices(IServiceCollection services)
.AddGames()
.AddInstallerTypes()
.AddCrossPlatform()
.AddSettings<LoggingSettings>()
.AddLogging(builder => builder.AddXunitOutput().SetMinimumLevel(LogLevel.Trace))
.AddSingleton<LocalHttpServer>()
.AddLogging(builder => builder.AddXUnit())
Expand Down

0 comments on commit 8e721da

Please sign in to comment.