From a84945e1d52771e3be76fc82f71860cf4f90dcf5 Mon Sep 17 00:00:00 2001 From: Alex Macocian Date: Thu, 9 Nov 2023 22:59:03 +0100 Subject: [PATCH] Update UMod startup action Minor mod fixes --- .../Configuration/ProjectConfiguration.cs | 3 +- Daybreak/Daybreak.csproj | 2 +- Daybreak/Models/ApplicationLauncherContext.cs | 8 +++ .../ApplicationLauncher.cs | 46 ++++++++++++-- Daybreak/Services/DSOAL/DSOALService.cs | 13 ++-- .../Services/DirectSong/DirectSongService.cs | 13 ++-- Daybreak/Services/GWCA/GWCAInjector.cs | 15 ++--- Daybreak/Services/Mods/IModService.cs | 12 ++-- Daybreak/Services/ReShade/ReShadeService.cs | 17 ++--- .../Services/Screens/GuildwarsScreenPlacer.cs | 11 ++-- .../Startup/Actions/StartupActionBase.cs | 5 +- .../Startup/Actions/UpdateUModAction.cs | 21 +++++++ .../Services/Startup/StartupActionManager.cs | 5 +- Daybreak/Services/Toolbox/ToolboxService.cs | 11 ++-- Daybreak/Services/UMod/IUModService.cs | 3 + Daybreak/Services/UMod/UModService.cs | 63 +++++++++++++++++-- 16 files changed, 187 insertions(+), 61 deletions(-) create mode 100644 Daybreak/Models/ApplicationLauncherContext.cs create mode 100644 Daybreak/Services/Startup/Actions/UpdateUModAction.cs diff --git a/Daybreak/Configuration/ProjectConfiguration.cs b/Daybreak/Configuration/ProjectConfiguration.cs index d3cd9f57..7e203bb8 100644 --- a/Daybreak/Configuration/ProjectConfiguration.cs +++ b/Daybreak/Configuration/ProjectConfiguration.cs @@ -332,6 +332,7 @@ public override void RegisterStartupActions(IStartupActionProducer startupAction startupActionProducer.RegisterAction(); startupActionProducer.RegisterAction(); + startupActionProducer.RegisterAction(); } public override void RegisterPostUpdateActions(IPostUpdateActionProducer postUpdateActionProducer) @@ -438,11 +439,11 @@ public override void RegisterNotificationHandlers(INotificationHandlerProducer n public override void RegisterMods(IModsManager modsManager) { + modsManager.RegisterMod(); modsManager.RegisterMod(); modsManager.RegisterMod(); modsManager.RegisterMod(); modsManager.RegisterMod(); - modsManager.RegisterMod(); modsManager.RegisterMod(); modsManager.RegisterMod(singleton: true); } diff --git a/Daybreak/Daybreak.csproj b/Daybreak/Daybreak.csproj index 9beb5665..923c55af 100644 --- a/Daybreak/Daybreak.csproj +++ b/Daybreak/Daybreak.csproj @@ -13,7 +13,7 @@ preview Daybreak.ico true - 0.9.8.141 + 0.9.8.142 true cfb2a489-db80-448d-a969-80270f314c46 True diff --git a/Daybreak/Models/ApplicationLauncherContext.cs b/Daybreak/Models/ApplicationLauncherContext.cs new file mode 100644 index 00000000..c3a9ce7a --- /dev/null +++ b/Daybreak/Models/ApplicationLauncherContext.cs @@ -0,0 +1,8 @@ +using System.Diagnostics; + +namespace Daybreak.Models; +public readonly struct ApplicationLauncherContext +{ + public string ExecutablePath { get; init; } + public Process Process { get; init; } +} diff --git a/Daybreak/Services/ApplicationLauncher/ApplicationLauncher.cs b/Daybreak/Services/ApplicationLauncher/ApplicationLauncher.cs index de2608e6..9a91d5fe 100644 --- a/Daybreak/Services/ApplicationLauncher/ApplicationLauncher.cs +++ b/Daybreak/Services/ApplicationLauncher/ApplicationLauncher.cs @@ -1,5 +1,6 @@ using Daybreak.Configuration.Options; using Daybreak.Exceptions; +using Daybreak.Models; using Daybreak.Models.LaunchConfigurations; using Daybreak.Services.Mods; using Daybreak.Services.Mutex; @@ -204,12 +205,13 @@ public void RestartDaybreakAsNormalUser() } }; + var applicationLauncherContext = new ApplicationLauncherContext { Process = process, ExecutablePath = executable }; foreach(var mod in disabledmods) { using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(this.launcherOptions.Value.ModStartupTimeout)); try { - await mod.OnGuildWarsStartingDisabled(process, cts.Token); + await mod.OnGuildWarsStartingDisabled(applicationLauncherContext, cts.Token); } catch (TaskCanceledException) { @@ -233,7 +235,7 @@ public void RestartDaybreakAsNormalUser() using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(this.launcherOptions.Value.ModStartupTimeout)); try { - await mod.OnGuildWarsStarting(process, cts.Token); + await mod.OnGuildWarsStarting(applicationLauncherContext, cts.Token); } catch (TaskCanceledException) { @@ -253,14 +255,39 @@ public void RestartDaybreakAsNormalUser() } var pId = LaunchClient(executable, string.Join(" ", args), this.privilegeManager.AdminPrivileges, out var clientHandle); - process = Process.GetProcessById(pId); + do + { + process = Process.GetProcessById(pId); + } while (process is null); + + while (true) + { + // We need to verify that the process is actually started. This needs to be in a try/catch block because process.Id throws when the process is invalid + try + { + if (process.Id != 0) + { + break; + } + } + catch + { + } + } + + while (!process.Responding) + { + await Task.Delay(1000, CancellationToken.None); + } + // Reset launch context with the launched process + applicationLauncherContext = new ApplicationLauncherContext { ExecutablePath = executable, Process = process }; foreach (var mod in mods) { using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(this.launcherOptions.Value.ModStartupTimeout)); try { - await mod.OnGuildWarsCreated(process, cts.Token); + await mod.OnGuildWarsCreated(applicationLauncherContext, cts.Token); } catch (TaskCanceledException) { @@ -331,7 +358,7 @@ public void RestartDaybreakAsNormalUser() using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(this.launcherOptions.Value.ModStartupTimeout)); try { - await mod.OnGuildWarsStarted(process, cts.Token); + await mod.OnGuildWarsStarted(applicationLauncherContext, cts.Token); } catch (TaskCanceledException) { @@ -457,7 +484,14 @@ private bool SetRegistryGuildwarsPath(string path) return Process.GetProcessesByName(ProcessName) .Where(p => { - return launchConfigurationWithCredentials.FirstOrDefault(l => l.ExecutablePath == p.MainModule?.FileName) is not null; + try + { + return launchConfigurationWithCredentials.FirstOrDefault(l => l.ExecutablePath == p.MainModule?.FileName) is not null; + } + catch + { + return false; + } }) .Select(p => { diff --git a/Daybreak/Services/DSOAL/DSOALService.cs b/Daybreak/Services/DSOAL/DSOALService.cs index 88498561..72f48f42 100644 --- a/Daybreak/Services/DSOAL/DSOALService.cs +++ b/Daybreak/Services/DSOAL/DSOALService.cs @@ -1,4 +1,5 @@ using Daybreak.Configuration.Options; +using Daybreak.Models; using Daybreak.Models.Progress; using Daybreak.Services.Downloads; using Daybreak.Services.Notifications; @@ -120,19 +121,19 @@ public IEnumerable GetCustomArguments() } } - public Task OnGuildWarsCreated(Process process, CancellationToken cancellationToken) + public Task OnGuildWarsCreated(ApplicationLauncherContext applicationLauncherContext, CancellationToken cancellationToken) { return Task.CompletedTask; } - public Task OnGuildWarsStarted(Process process, CancellationToken cancellationToken) + public Task OnGuildWarsStarted(ApplicationLauncherContext applicationLauncherContext, CancellationToken cancellationToken) { return Task.CompletedTask; } - public Task OnGuildWarsStarting(Process process, CancellationToken cancellationToken) + public Task OnGuildWarsStarting(ApplicationLauncherContext applicationLauncherContext, CancellationToken cancellationToken) { - var guildwarsDirectory = new FileInfo(process.StartInfo.FileName).Directory!.FullName; + var guildwarsDirectory = new FileInfo(applicationLauncherContext.ExecutablePath).Directory!.FullName; if (this.IsInstalled) { this.EnsureSymbolicLinkExists(); @@ -147,9 +148,9 @@ public Task OnGuildWarsStarting(Process process, CancellationToken cancellationT return Task.CompletedTask; } - public Task OnGuildWarsStartingDisabled(Process process, CancellationToken cancellationToken) + public Task OnGuildWarsStartingDisabled(ApplicationLauncherContext applicationLauncherContext, CancellationToken cancellationToken) { - var guildwarsDirectory = new FileInfo(process.StartInfo.FileName).Directory!.FullName; + var guildwarsDirectory = new FileInfo(applicationLauncherContext.ExecutablePath).Directory!.FullName; EnsureFileDoesNotExistInGuildwarsDirectory(DsoundDll, guildwarsDirectory); EnsureFileDoesNotExistInGuildwarsDirectory(DSOALAldrvDll, guildwarsDirectory); EnsureFileDoesNotExistInGuildwarsDirectory(AlsoftIni, guildwarsDirectory); diff --git a/Daybreak/Services/DirectSong/DirectSongService.cs b/Daybreak/Services/DirectSong/DirectSongService.cs index 5cc54dfd..97afb404 100644 --- a/Daybreak/Services/DirectSong/DirectSongService.cs +++ b/Daybreak/Services/DirectSong/DirectSongService.cs @@ -1,4 +1,5 @@ using Daybreak.Configuration.Options; +using Daybreak.Models; using Daybreak.Models.Progress; using Daybreak.Services.Downloads; using Daybreak.Services.Notifications; @@ -68,18 +69,18 @@ public DirectSongService( public IEnumerable GetCustomArguments() => Array.Empty(); - public Task OnGuildWarsCreated(Process process, CancellationToken cancellationToken) => Task.CompletedTask; + public Task OnGuildWarsCreated(ApplicationLauncherContext applicationLauncherContext, CancellationToken cancellationToken) => Task.CompletedTask; - public Task OnGuildWarsStarted(Process process, CancellationToken cancellationToken) => Task.CompletedTask; + public Task OnGuildWarsStarted(ApplicationLauncherContext applicationLauncherContext, CancellationToken cancellationToken) => Task.CompletedTask; - public Task OnGuildWarsStarting(Process process, CancellationToken cancellationToken) + public Task OnGuildWarsStarting(ApplicationLauncherContext applicationLauncherContext, CancellationToken cancellationToken) { if (!this.IsInstalled) { return Task.CompletedTask; } - var gwPath = Path.GetFullPath(process.StartInfo.FileName); + var gwPath = Path.GetFullPath(applicationLauncherContext.ExecutablePath); var gwDirectory = Path.GetDirectoryName(gwPath)!; var wmCorePath = Path.Combine(gwDirectory, WMVCOREDll); var dsGuildWarsDll = Path.Combine(gwDirectory, DsGuildwarsDll); @@ -99,9 +100,9 @@ public Task OnGuildWarsStarting(Process process, CancellationToken cancellationT return Task.CompletedTask; } - public Task OnGuildWarsStartingDisabled(Process process, CancellationToken cancellationToken) + public Task OnGuildWarsStartingDisabled(ApplicationLauncherContext applicationLauncherContext, CancellationToken cancellationToken) { - var gwPath = Path.GetFullPath(process.StartInfo.FileName); + var gwPath = Path.GetFullPath(applicationLauncherContext.ExecutablePath); var gwDirectory = Path.GetDirectoryName(gwPath)!; var wmCorePath = Path.Combine(gwDirectory, WMVCOREDll); var dsGuildWarsDll = Path.Combine(gwDirectory, DsGuildwarsDll); diff --git a/Daybreak/Services/GWCA/GWCAInjector.cs b/Daybreak/Services/GWCA/GWCAInjector.cs index 54d4113b..da6e99f4 100644 --- a/Daybreak/Services/GWCA/GWCAInjector.cs +++ b/Daybreak/Services/GWCA/GWCAInjector.cs @@ -1,4 +1,5 @@ -using Daybreak.Models.GWCA; +using Daybreak.Models; +using Daybreak.Models.GWCA; using Daybreak.Services.Injection; using Daybreak.Services.Notifications; using System.Collections.Generic; @@ -35,19 +36,19 @@ public GWCAInjector( public IEnumerable GetCustomArguments() => Enumerable.Empty(); - public Task OnGuildWarsStarting(Process process, CancellationToken cancellationToken) + public Task OnGuildWarsStarting(ApplicationLauncherContext applicationLauncherContext, CancellationToken cancellationToken) { return Task.CompletedTask; } - public Task OnGuildWarsStartingDisabled(Process process, CancellationToken cancellationToken) + public Task OnGuildWarsStartingDisabled(ApplicationLauncherContext applicationLauncherContext, CancellationToken cancellationToken) { return Task.CompletedTask; } - public async Task OnGuildWarsCreated(Process process, CancellationToken cancellationToken) + public async Task OnGuildWarsCreated(ApplicationLauncherContext applicationLauncherContext, CancellationToken cancellationToken) { - if (!await this.injector.Inject(process, ModulePath, cancellationToken)) + if (!await this.injector.Inject(applicationLauncherContext.Process, ModulePath, cancellationToken)) { this.notificationService.NotifyError( title: "Unable to inject GWCA into Guild Wars process", @@ -55,12 +56,12 @@ public async Task OnGuildWarsCreated(Process process, CancellationToken cancella } } - public async Task OnGuildWarsStarted(Process process, CancellationToken cancellationToken) + public async Task OnGuildWarsStarted(ApplicationLauncherContext applicationLauncherContext, CancellationToken cancellationToken) { ConnectionContext? connectionContext = default; for(var i = 0; i < MaxRetries; i++) { - if (await this.gwcaClient.Connect(process, cancellationToken) is ConnectionContext newContext) + if (await this.gwcaClient.Connect(applicationLauncherContext.Process, cancellationToken) is ConnectionContext newContext) { connectionContext = newContext; break; diff --git a/Daybreak/Services/Mods/IModService.cs b/Daybreak/Services/Mods/IModService.cs index 4018d147..5cb432b1 100644 --- a/Daybreak/Services/Mods/IModService.cs +++ b/Daybreak/Services/Mods/IModService.cs @@ -1,5 +1,5 @@ -using System.Collections.Generic; -using System.Diagnostics; +using Daybreak.Models; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -15,20 +15,20 @@ public interface IModService /// Called before starting the guild wars process. /// Do mod preparation here. /// - Task OnGuildWarsStarting(Process process, CancellationToken cancellationToken); + Task OnGuildWarsStarting(ApplicationLauncherContext applicationLauncherContext, CancellationToken cancellationToken); /// /// Called when the process is created in suspended state. /// Do dll injection here. /// - Task OnGuildWarsCreated(Process process, CancellationToken cancellationToken); + Task OnGuildWarsCreated(ApplicationLauncherContext applicationLauncherContext, CancellationToken cancellationToken); /// /// Called after the process has been resumed. /// Do clean-up/integration with the guild wars process here. /// - Task OnGuildWarsStarted(Process process, CancellationToken cancellationToken); + Task OnGuildWarsStarted(ApplicationLauncherContext applicationLauncherContext, CancellationToken cancellationToken); /// /// Called before starting the guild wars process, when the mod is disabled. /// Use this method to clean up the GuildWars folder of any residual mod files. /// - Task OnGuildWarsStartingDisabled(Process process, CancellationToken cancellationToken); + Task OnGuildWarsStartingDisabled(ApplicationLauncherContext applicationLauncherContext, CancellationToken cancellationToken); } diff --git a/Daybreak/Services/ReShade/ReShadeService.cs b/Daybreak/Services/ReShade/ReShadeService.cs index 03b126af..1fe62fad 100644 --- a/Daybreak/Services/ReShade/ReShadeService.cs +++ b/Daybreak/Services/ReShade/ReShadeService.cs @@ -1,4 +1,5 @@ using Daybreak.Configuration.Options; +using Daybreak.Models; using Daybreak.Models.Progress; using Daybreak.Models.ReShade; using Daybreak.Services.Downloads; @@ -120,10 +121,10 @@ public void OnClosing() public IEnumerable GetCustomArguments() => Enumerable.Empty(); - public async Task OnGuildWarsCreated(Process process, CancellationToken cancellationToken) + public async Task OnGuildWarsCreated(ApplicationLauncherContext applicationLauncherContext, CancellationToken cancellationToken) { - var scopedLogger = this.logger.CreateScopedLogger(nameof(this.OnGuildWarsCreated), process?.MainModule?.FileName ?? string.Empty); - if (await this.processInjector.Inject(process!, ReShadeDllPath, cancellationToken)) + var scopedLogger = this.logger.CreateScopedLogger(nameof(this.OnGuildWarsCreated), applicationLauncherContext.ExecutablePath ?? string.Empty); + if (await this.processInjector.Inject(applicationLauncherContext.Process, ReShadeDllPath, cancellationToken)) { scopedLogger.LogInformation("Injected ReShade dll"); this.notificationService.NotifyInformation( @@ -139,11 +140,11 @@ public async Task OnGuildWarsCreated(Process process, CancellationToken cancella } } - public Task OnGuildWarsStarted(Process process, CancellationToken cancellationToken) => Task.CompletedTask; + public Task OnGuildWarsStarted(ApplicationLauncherContext applicationLauncherContext, CancellationToken cancellationToken) => Task.CompletedTask; - public Task OnGuildWarsStarting(Process process, CancellationToken cancellationToken) + public Task OnGuildWarsStarting(ApplicationLauncherContext applicationLauncherContext, CancellationToken cancellationToken) { - var destinationDirectory = Path.GetFullPath(new FileInfo(process.StartInfo.FileName).DirectoryName!); + var destinationDirectory = Path.GetFullPath(new FileInfo(applicationLauncherContext.ExecutablePath).DirectoryName!); EnsureFileExistsInGuildwarsDirectory(ReShadeLog, destinationDirectory); EnsureFileExistsInGuildwarsDirectory(ReShadePreset, destinationDirectory); EnsureFileExistsInGuildwarsDirectory(ConfigIni, destinationDirectory); @@ -152,9 +153,9 @@ public Task OnGuildWarsStarting(Process process, CancellationToken cancellationT return Task.CompletedTask; } - public Task OnGuildWarsStartingDisabled(Process process, CancellationToken cancellationToken) + public Task OnGuildWarsStartingDisabled(ApplicationLauncherContext applicationLauncherContext, CancellationToken cancellationToken) { - var destinationDirectory = Path.GetFullPath(new FileInfo(process.StartInfo.FileName).DirectoryName!); + var destinationDirectory = Path.GetFullPath(new FileInfo(applicationLauncherContext.ExecutablePath).DirectoryName!); var destination = Path.Combine(Path.GetFullPath(destinationDirectory), PresetsFolder); if (Directory.Exists(destination)) { diff --git a/Daybreak/Services/Screens/GuildwarsScreenPlacer.cs b/Daybreak/Services/Screens/GuildwarsScreenPlacer.cs index 7113926a..1dc16d9c 100644 --- a/Daybreak/Services/Screens/GuildwarsScreenPlacer.cs +++ b/Daybreak/Services/Screens/GuildwarsScreenPlacer.cs @@ -1,11 +1,10 @@ using Daybreak.Configuration.Options; +using Daybreak.Models; using Daybreak.Services.Scanner; using Microsoft.Extensions.Logging; -using System; using System.Collections.Generic; using System.Configuration; using System.Core.Extensions; -using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -46,7 +45,7 @@ public bool IsEnabled public bool IsInstalled => true; - public async Task OnGuildWarsStarted(Process process, CancellationToken cancellationToken) + public async Task OnGuildWarsStarted(ApplicationLauncherContext applicationLauncherContext, CancellationToken cancellationToken) { var screen = this.screenManager.Screens.Skip(this.liveOptions.Value.DesiredGuildwarsScreen).FirstOrDefault(); if (screen is null) @@ -72,9 +71,9 @@ public async Task OnGuildWarsStarted(Process process, CancellationToken cancella public IEnumerable GetCustomArguments() => Enumerable.Empty(); - public Task OnGuildWarsStarting(Process process, CancellationToken cancellationToken) => Task.CompletedTask; + public Task OnGuildWarsStarting(ApplicationLauncherContext applicationLauncherContext, CancellationToken cancellationToken) => Task.CompletedTask; - public Task OnGuildWarsStartingDisabled(Process process, CancellationToken cancellationToken) => Task.CompletedTask; + public Task OnGuildWarsStartingDisabled(ApplicationLauncherContext applicationLauncherContext, CancellationToken cancellationToken) => Task.CompletedTask; - public Task OnGuildWarsCreated(Process process, CancellationToken cancellationToken) => Task.CompletedTask; + public Task OnGuildWarsCreated(ApplicationLauncherContext applicationLauncherContext, CancellationToken cancellationToken) => Task.CompletedTask; } diff --git a/Daybreak/Services/Startup/Actions/StartupActionBase.cs b/Daybreak/Services/Startup/Actions/StartupActionBase.cs index 8cbe0be0..4c24080e 100644 --- a/Daybreak/Services/Startup/Actions/StartupActionBase.cs +++ b/Daybreak/Services/Startup/Actions/StartupActionBase.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System.Threading; +using System.Threading.Tasks; namespace Daybreak.Services.Startup.Actions; @@ -9,7 +10,7 @@ public virtual void ExecuteOnStartup() } - public virtual Task ExecuteOnStartupAsync() + public virtual Task ExecuteOnStartupAsync(CancellationToken cancellationToken) { return Task.CompletedTask; } diff --git a/Daybreak/Services/Startup/Actions/UpdateUModAction.cs b/Daybreak/Services/Startup/Actions/UpdateUModAction.cs new file mode 100644 index 00000000..8010a84f --- /dev/null +++ b/Daybreak/Services/Startup/Actions/UpdateUModAction.cs @@ -0,0 +1,21 @@ +using Daybreak.Services.UMod; +using System.Core.Extensions; +using System.Threading; +using System.Threading.Tasks; + +namespace Daybreak.Services.Startup.Actions; +internal sealed class UpdateUModAction : StartupActionBase +{ + private readonly IUModService uModService; + + public UpdateUModAction( + IUModService uModService) + { + this.uModService = uModService.ThrowIfNull(); + } + + public override async Task ExecuteOnStartupAsync(CancellationToken cancellationToken) + { + await this.uModService.CheckAndUpdateUMod(cancellationToken); + } +} diff --git a/Daybreak/Services/Startup/StartupActionManager.cs b/Daybreak/Services/Startup/StartupActionManager.cs index aa948401..a53713b8 100644 --- a/Daybreak/Services/Startup/StartupActionManager.cs +++ b/Daybreak/Services/Startup/StartupActionManager.cs @@ -1,7 +1,9 @@ using Daybreak.Services.Startup.Actions; using Slim; +using System; using System.Collections.Generic; using System.Core.Extensions; +using System.Threading; using System.Threading.Tasks; using System.Windows.Extensions.Services; @@ -24,10 +26,11 @@ public void OnClosing() public void OnStartup() { var asyncTasks = new List(); + var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5)); foreach(var action in this.serviceManager.GetServicesOfType()) { action.ExecuteOnStartup(); - asyncTasks.Add(action.ExecuteOnStartupAsync()); + asyncTasks.Add(Task.Run(() => action.ExecuteOnStartupAsync(cts.Token))); } Task.WaitAll(asyncTasks.ToArray()); diff --git a/Daybreak/Services/Toolbox/ToolboxService.cs b/Daybreak/Services/Toolbox/ToolboxService.cs index e2584c67..d139e90f 100644 --- a/Daybreak/Services/Toolbox/ToolboxService.cs +++ b/Daybreak/Services/Toolbox/ToolboxService.cs @@ -1,5 +1,6 @@ using Daybreak.Configuration.Options; using Daybreak.Exceptions; +using Daybreak.Models; using Daybreak.Models.Progress; using Daybreak.Services.Injection; using Daybreak.Services.Notifications; @@ -63,13 +64,13 @@ public ToolboxService( this.logger = logger.ThrowIfNull(); } - public Task OnGuildWarsStarting(Process process, CancellationToken cancellationToken) => Task.CompletedTask; + public Task OnGuildWarsStarting(ApplicationLauncherContext applicationLauncherContext, CancellationToken cancellationToken) => Task.CompletedTask; - public Task OnGuildWarsStartingDisabled(Process process, CancellationToken cancellationToken) => Task.CompletedTask; + public Task OnGuildWarsStartingDisabled(ApplicationLauncherContext applicationLauncherContext, CancellationToken cancellationToken) => Task.CompletedTask; - public async Task OnGuildWarsCreated(Process process, CancellationToken cancellationToken) + public async Task OnGuildWarsCreated(ApplicationLauncherContext applicationLauncherContext, CancellationToken cancellationToken) { - await this.LaunchToolbox(process, cancellationToken); + await this.LaunchToolbox(applicationLauncherContext.Process, cancellationToken); /* * Toolbox startup conflicts with Daybreak GWCA integration. Wait some time @@ -78,7 +79,7 @@ public async Task OnGuildWarsCreated(Process process, CancellationToken cancella await Task.Delay(TimeSpan.FromSeconds(this.toolboxOptions.Value.StartupDelay), cancellationToken); } - public Task OnGuildWarsStarted(Process process, CancellationToken cancellationToken) => Task.CompletedTask; + public Task OnGuildWarsStarted(ApplicationLauncherContext applicationLauncherContext, CancellationToken cancellationToken) => Task.CompletedTask; public IEnumerable GetCustomArguments() { diff --git a/Daybreak/Services/UMod/IUModService.cs b/Daybreak/Services/UMod/IUModService.cs index 83a57842..2a6ade09 100644 --- a/Daybreak/Services/UMod/IUModService.cs +++ b/Daybreak/Services/UMod/IUModService.cs @@ -2,6 +2,7 @@ using Daybreak.Models.UMod; using Daybreak.Services.Mods; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; namespace Daybreak.Services.UMod; @@ -11,6 +12,8 @@ public interface IUModService : IModService Task SetupUMod(UModInstallationStatus uModInstallationStatus); + Task CheckAndUpdateUMod(CancellationToken cancellationToken); + bool AddMod(string pathToTpf, bool? imported = default); bool RemoveMod(string pathToTpf); diff --git a/Daybreak/Services/UMod/UModService.cs b/Daybreak/Services/UMod/UModService.cs index d59b68c8..f49d7363 100644 --- a/Daybreak/Services/UMod/UModService.cs +++ b/Daybreak/Services/UMod/UModService.cs @@ -1,4 +1,5 @@ using Daybreak.Configuration.Options; +using Daybreak.Models; using Daybreak.Models.Github; using Daybreak.Models.Progress; using Daybreak.Models.UMod; @@ -85,24 +86,24 @@ public IEnumerable GetCustomArguments() return Enumerable.Empty(); } - public Task OnGuildWarsStarting(Process process, CancellationToken cancellationToken) + public Task OnGuildWarsStarting(ApplicationLauncherContext applicationLauncherContext, CancellationToken cancellationToken) { return Task.CompletedTask; } - public Task OnGuildWarsStartingDisabled(Process process, CancellationToken cancellationToken) => Task.CompletedTask; + public Task OnGuildWarsStartingDisabled(ApplicationLauncherContext applicationLauncherContext, CancellationToken cancellationToken) => Task.CompletedTask; - public Task OnGuildWarsCreated(Process process, CancellationToken cancellationToken) + public Task OnGuildWarsCreated(ApplicationLauncherContext applicationLauncherContext, CancellationToken cancellationToken) { - return this.processInjector.Inject(process, Path.Combine(Path.GetFullPath(UModDirectory), UModDll), cancellationToken); + return this.processInjector.Inject(applicationLauncherContext.Process, Path.Combine(Path.GetFullPath(UModDirectory), UModDll), cancellationToken); } - public async Task OnGuildWarsStarted(Process process, CancellationToken cancellationToken) + public async Task OnGuildWarsStarted(ApplicationLauncherContext applicationLauncherContext, CancellationToken cancellationToken) { UModConnectionContext? context; try { - context = await this.uModClient2.Initialize(process, cancellationToken); + context = await this.uModClient2.Initialize(applicationLauncherContext.Process, cancellationToken); } catch(TimeoutException) { @@ -208,6 +209,56 @@ public void SaveMods(List list) this.uModOptions.UpdateOption(); } + public async Task CheckAndUpdateUMod(CancellationToken cancellationToken) + { + var scopedLogger = this.logger.CreateScopedLogger(nameof(this.CheckAndUpdateUMod), string.Empty); + var existingUMod = Path.Combine(Path.GetFullPath(UModDirectory), UModDll); + if (!this.IsInstalled) + { + scopedLogger.LogInformation("UMod is not installed"); + return; + } + + scopedLogger.LogInformation("Retrieving version list"); + var getListResponse = await this.httpClient.GetAsync(ReleasesUrl, cancellationToken); + if (!getListResponse.IsSuccessStatusCode) + { + scopedLogger.LogError($"Received non success status code [{getListResponse.StatusCode}]"); + return; + } + + var responseString = await getListResponse.Content.ReadAsStringAsync(); + var releasesList = responseString.Deserialize>(); + var latestRelease = releasesList? + .Select(t => t.Ref?.Replace("refs/tags/", "")) + .OfType() + .LastOrDefault(); + + if (!Daybreak.Models.Versioning.Version.TryParse(latestRelease ?? string.Empty, out var latestVersion)) + { + scopedLogger.LogError($"Unable to parse latest version {latestRelease}"); + return; + } + + var currentRelease = FileVersionInfo.GetVersionInfo(existingUMod).FileVersion; + if (!Daybreak.Models.Versioning.Version.TryParse(currentRelease ?? string.Empty, out var currentVersion)) + { + scopedLogger.LogError($"Unable to parse current version {currentRelease}"); + return; + } + + if (currentVersion.CompareTo(latestVersion) >= 0) + { + scopedLogger.LogError($"UMod is up to date"); + return; + } + + await this.DownloadLatestDll(new UModInstallationStatus(), cancellationToken); + this.notificationService.NotifyInformation( + title: "UMod updated", + description: $"UMod has been updated to version {latestRelease}"); + } + private async void LoadModsAsync(UModConnectionContext context) { foreach (var entry in this.uModOptions.Value.Mods.Where(e => e.Enabled && e.PathToFile is not null))