Skip to content

Commit

Permalink
Button to load plugin
Browse files Browse the repository at this point in the history
Detect broken GuildWars
Closes #437
Closes #407
  • Loading branch information
AlexMacocian committed Oct 28, 2023
1 parent 3b1d683 commit 33b430f
Show file tree
Hide file tree
Showing 8 changed files with 113 additions and 12 deletions.
2 changes: 1 addition & 1 deletion Daybreak/Daybreak.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<LangVersion>preview</LangVersion>
<ApplicationIcon>Daybreak.ico</ApplicationIcon>
<IncludePackageReferencesDuringMarkupCompilation>true</IncludePackageReferencesDuringMarkupCompilation>
<Version>0.9.8.131</Version>
<Version>0.9.8.132</Version>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<UserSecretsId>cfb2a489-db80-448d-a969-80270f314c46</UserSecretsId>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
Expand Down
3 changes: 3 additions & 0 deletions Daybreak/Services/Plugins/IPluginsService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@
using Daybreak.Services.Updater.PostUpdate;
using Slim;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Daybreak.Services.Plugins;

public interface IPluginsService
{
Task<bool> AddPlugin(string pathToPlugin);

IEnumerable<AvailablePlugin> GetCurrentlyLoadedPlugins();

void LoadPlugins(
Expand Down
32 changes: 32 additions & 0 deletions Daybreak/Services/Plugins/PluginsService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@
using System.Logging;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;

namespace Daybreak.Services.Plugins;

public sealed class PluginsService : IPluginsService
{
private const string DllExtension = ".dll";
private const string PluginsDirectory = "Plugins";

private static readonly object Lock = new();
Expand Down Expand Up @@ -180,6 +182,36 @@ public void LoadPlugins(
Monitor.Exit(Lock);
}

public async Task<bool> AddPlugin(string pathToPlugin)
{
var scopedLogger = this.logger.CreateScopedLogger(nameof(this.AddPlugin), pathToPlugin ?? string.Empty);
if (pathToPlugin!.IsNullOrWhiteSpace())
{
scopedLogger.LogError("Plugin path is null or empty");
return false;
}

var fullPath = Path.GetFullPath(pathToPlugin!);
if (!File.Exists(fullPath) ||
!Path.GetExtension(fullPath).Equals(DllExtension, StringComparison.OrdinalIgnoreCase))
{
scopedLogger.LogError("Plugin path is invalid. File must exist and must have .dll extension");
return false;
}

var destinationPath = Path.GetFullPath(Path.Combine(PluginsDirectory, Path.GetFileName(fullPath)));
if (File.Exists(destinationPath))
{
scopedLogger.LogError("Plugin already exists");
return true;
}

using var sourceStream = new FileStream(fullPath, FileMode.Open);
using var destinationStream = new FileStream(destinationPath, FileMode.CreateNew);
await sourceStream.CopyToAsync(destinationStream);
return true;
}

private static void LogLoadOperation(PluginLoadOperation result, ScopedLogger<PluginsService> scopedLogger)
{
_ = result switch
Expand Down
29 changes: 22 additions & 7 deletions Daybreak/Services/UMod/UModService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,16 @@ public Task OnGuildWarsCreated(Process process, CancellationToken cancellationTo

public async Task OnGuildwarsStarted(Process process, CancellationToken cancellationToken)
{
try
{
await this.uModClient.WaitForInitialize(cancellationToken);
}
catch(TimeoutException)
{
this.NotifyFaultyInstallation();
return;
}

foreach(var entry in this.uModOptions.Value.Mods.Where(e => e.Enabled && e.PathToFile is not null))
{
await this.uModClient.AddFile(entry.PathToFile!, cancellationToken);
Expand All @@ -108,13 +118,7 @@ public async Task OnGuildwarsStarted(Process process, CancellationToken cancella
throw;
}

/*
* Known issue where Guild Wars updater breaks the executable, which in turn breaks the integration with uMod.
* Prompt the user to manually reinstall Guild Wars.
*/
this.notificationService.NotifyInformation(
title: "uMod failed to start",
description: "uMod failed to start due to a known issue with Guild Wars updating process. Please manually re-install Guild Wars in order to restore uMod functionality");
this.NotifyFaultyInstallation();
return;
}

Expand Down Expand Up @@ -215,6 +219,17 @@ public void SaveMods(List<UModEntry> list)
this.uModOptions.UpdateOption();
}

private void NotifyFaultyInstallation()
{
/*
* Known issue where Guild Wars updater breaks the executable, which in turn breaks the integration with uMod.
* Prompt the user to manually reinstall Guild Wars.
*/
this.notificationService.NotifyInformation(
title: "uMod failed to start",
description: "uMod failed to start due to a known issue with Guild Wars updating process. Please manually re-install Guild Wars in order to restore uMod functionality");
}

private async Task<bool> SetupUModDll(UModInstallationStatus uModInstallationStatus)
{
if (File.Exists(this.uModOptions.Value.DllPath))
Expand Down
1 change: 1 addition & 0 deletions Daybreak/Services/UMod/Utilities/IUModClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public interface IUModClient : IDisposable
{
bool Ready { get; }
void Initialize(CancellationToken cancellationToken);
Task WaitForInitialize(CancellationToken cancellationToken);
void CloseConnection();
Task AddFile(string filePath, CancellationToken cancellationToken);
Task<bool> Send(CancellationToken cancellationToken);
Expand Down
20 changes: 17 additions & 3 deletions Daybreak/Services/UMod/Utilities/UModClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ internal sealed class UModClient : IUModClient
private const string EntryLoadMetricUnit = "ms";
private const int SMALL_PIPE_SIZE = 1 << 10;
private const int BIG_PIPE_SIZE = 1 << 24;
private const int MaxRetryCount = 10; //Equivalent of 5 seconds

private readonly Histogram<double> latencyMetric;
private readonly INotificationService notificationService;
Expand Down Expand Up @@ -113,14 +114,27 @@ public async Task AddFile(string filePath, CancellationToken cancellationToken)
}
}

public async Task<bool> Send(CancellationToken cancellationToken)
public async Task WaitForInitialize(CancellationToken cancellationToken)
{
var scopedLogger = this.logger.CreateScopedLogger(nameof(this.Send), string.Empty);
var scopedLogger = this.logger.CreateScopedLogger(nameof(this.WaitForInitialize), string.Empty);
var retryCount = 0;
while (!this.Ready)
{
scopedLogger.LogInformation("Pipes are not yet ready. Waiting 100ms");
await Task.Delay(100, cancellationToken);
await Task.Delay(500, cancellationToken);
retryCount++;
if (retryCount == MaxRetryCount)
{
scopedLogger.LogError($"Exceeded retry count. Throwing {nameof(TimeoutException)}");
throw new TimeoutException("Timed out waiting for uMod connection");
}
}
}

public async Task<bool> Send(CancellationToken cancellationToken)
{
var scopedLogger = this.logger.CreateScopedLogger(nameof(this.Send), string.Empty);
await this.WaitForInitialize(cancellationToken);

foreach(var loader in this.texturePackLoaders)
{
Expand Down
8 changes: 8 additions & 0 deletions Daybreak/Views/PluginsView.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@
Margin="0, 0, 5, 0"
Clicked="SaveButton_Clicked"
ToolTip="Save changes"/>
<buttons:NavigateFileButton
ToolTip="Load mods from disk"
Height="30"
Width="30"
Margin="0, 0, 45, 0"
VerticalAlignment="Center"
HorizontalAlignment="Right"
Clicked="LoadPluginFromDisk_Clicked" />
<ItemsControl
Grid.Row="1"
Background="Transparent"
Expand Down
30 changes: 29 additions & 1 deletion Daybreak/Views/PluginsView.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Daybreak.Models.Plugins;
using Daybreak.Services.Navigation;
using Daybreak.Services.Plugins;
using Microsoft.Win32;
using System;
using System.Collections.ObjectModel;
using System.Core.Extensions;
Expand Down Expand Up @@ -33,7 +34,7 @@ public PluginsView(

private void View_Loaded(object sender, RoutedEventArgs e)
{
this.AvailablePlugins.ClearAnd().AddRange(this.pluginsService.GetAvailablePlugins());
this.UpdatePlugins();
}

private void SaveButton_Clicked(object sender, EventArgs e)
Expand All @@ -51,6 +52,28 @@ private void SaveButton_Clicked(object sender, EventArgs e)
this.viewManager.ShowView<LauncherView>();
}

private async void LoadPluginFromDisk_Clicked(object sender, EventArgs e)
{
var filePicker = new OpenFileDialog
{
Filter = "Dll Files (*.dll)|*.dll",
Multiselect = true,
RestoreDirectory = true,
Title = "Please select dll files"
};
if (filePicker.ShowDialog() is false)
{
return;
}

foreach (var name in filePicker.FileNames)
{
await this.pluginsService.AddPlugin(name);
}

this.UpdatePlugins();
}

private void NavigateFileButton_Clicked(object sender, EventArgs e)
{
if (sender is not FrameworkElement element ||
Expand All @@ -61,4 +84,9 @@ private void NavigateFileButton_Clicked(object sender, EventArgs e)

Process.Start("explorer.exe", plugin.Path);
}

private void UpdatePlugins()
{
this.AvailablePlugins.ClearAnd().AddRange(this.pluginsService.GetAvailablePlugins());
}
}

0 comments on commit 33b430f

Please sign in to comment.