From 25769cb6db1038df0d915151b909dd93bf5d92a1 Mon Sep 17 00:00:00 2001
From: Stone_Red <56473591+Stone-Red-Code@users.noreply.github.com>
Date: Fri, 22 Dec 2023 18:05:44 +0100
Subject: [PATCH] Fully implement plugin manager and improve internal plugin
handling
---
src/DesktopMagic/App.xaml | 1 +
.../DataContexts/ModEntryDataContext.cs | 19 ---
.../DataContexts/PluginEntryDataContext.cs | 27 ++++
.../DataContexts/PluginManagerDataContext.cs | 21 ++-
src/DesktopMagic/DesktopMagic.csproj | 1 +
src/DesktopMagic/GlobalSuppressions.cs | 4 +-
src/DesktopMagic/MainWindow.xaml | 6 +-
src/DesktopMagic/MainWindow.xaml.cs | 126 ++++++++++--------
src/DesktopMagic/Plugins/ModEntry.xaml | 33 ++---
src/DesktopMagic/Plugins/PluginData.cs | 2 +-
src/DesktopMagic/Plugins/PluginManager.xaml | 77 ++++++-----
.../Plugins/PluginManager.xaml.cs | 118 +++++++++++++++-
src/DesktopMagic/Plugins/PluginMetadata.cs | 63 +++++++++
src/DesktopMagic/Plugins/PluginWindow.xaml.cs | 55 ++++----
src/DesktopMagic/Plugins/SettingElement.cs | 3 +-
src/DesktopMagic/Settings/Layout.cs | 4 +-
src/DesktopMagic/Settings/PluginSettings.cs | 4 +
17 files changed, 389 insertions(+), 175 deletions(-)
delete mode 100644 src/DesktopMagic/DataContexts/ModEntryDataContext.cs
create mode 100644 src/DesktopMagic/DataContexts/PluginEntryDataContext.cs
create mode 100644 src/DesktopMagic/Plugins/PluginMetadata.cs
diff --git a/src/DesktopMagic/App.xaml b/src/DesktopMagic/App.xaml
index cbf40f7..2931262 100644
--- a/src/DesktopMagic/App.xaml
+++ b/src/DesktopMagic/App.xaml
@@ -9,6 +9,7 @@
+
diff --git a/src/DesktopMagic/DataContexts/ModEntryDataContext.cs b/src/DesktopMagic/DataContexts/ModEntryDataContext.cs
deleted file mode 100644
index 14f57d2..0000000
--- a/src/DesktopMagic/DataContexts/ModEntryDataContext.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using Modio.NET.Models;
-
-using System;
-using System.Windows.Input;
-
-namespace DesktopMagic.DataContexts;
-internal class ModEntryDataContext(Mod mod, ICommand installCommand)
-{
- public string Name => mod.Name!;
-
- public string Description => mod.DescriptionPlaintext!;
-
- public string? Logo => mod.Logo?.Thumb320x180?.ToString();
-
- public DateTime FormattedDateAdded => DateTimeOffset.FromUnixTimeSeconds(mod.DateAdded).LocalDateTime;
- public DateTime FormattedDateUpdated => DateTimeOffset.FromUnixTimeSeconds(mod.DateUpdated).LocalDateTime;
-
- public ICommand InstallCommand => installCommand;
-}
diff --git a/src/DesktopMagic/DataContexts/PluginEntryDataContext.cs b/src/DesktopMagic/DataContexts/PluginEntryDataContext.cs
new file mode 100644
index 0000000..6e35dfa
--- /dev/null
+++ b/src/DesktopMagic/DataContexts/PluginEntryDataContext.cs
@@ -0,0 +1,27 @@
+using DesktopMagic.Plugins;
+
+using System;
+using System.Windows;
+using System.Windows.Input;
+
+namespace DesktopMagic.DataContexts;
+
+internal class PluginEntryDataContext(PluginMetadata pluginMetadata, ICommand command, bool installed = false)
+{
+ public string Name => pluginMetadata.Name;
+
+ public string? Description => pluginMetadata.Description;
+
+ public string? Logo => pluginMetadata.IconUri?.ToString();
+
+ public DateTime? FormattedDateAdded => pluginMetadata.Added;
+ public DateTime? FormattedDateUpdated => pluginMetadata.Updated;
+
+ public uint Id => pluginMetadata.Id;
+
+ public ICommand Command => command;
+
+ public Visibility InstallButtonVisibility => installed ? Visibility.Collapsed : Visibility.Visible;
+
+ public Visibility RemoveButtonVisibility => installed ? Visibility.Visible : Visibility.Collapsed;
+}
\ No newline at end of file
diff --git a/src/DesktopMagic/DataContexts/PluginManagerDataContext.cs b/src/DesktopMagic/DataContexts/PluginManagerDataContext.cs
index 67abb77..b253214 100644
--- a/src/DesktopMagic/DataContexts/PluginManagerDataContext.cs
+++ b/src/DesktopMagic/DataContexts/PluginManagerDataContext.cs
@@ -3,14 +3,31 @@ using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace DesktopMagic.DataContexts;
+
internal class PluginManagerDataContext : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
- public ObservableCollection Mods { get; } = [];
+ public ObservableCollection AllPlugins { get; } = [];
+ public ObservableCollection InstalledPlugins { get; } = [];
+
+ private bool _isLoading;
+
+ public bool IsLoading
+ {
+ get => _isLoading;
+ set
+ {
+ _isLoading = value;
+ OnPropertyChanged();
+ OnPropertyChanged(nameof(IsNotLoading));
+ }
+ }
+
+ public bool IsNotLoading => !IsLoading;
protected void OnPropertyChanged([CallerMemberName] string? name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
-}
+}
\ No newline at end of file
diff --git a/src/DesktopMagic/DesktopMagic.csproj b/src/DesktopMagic/DesktopMagic.csproj
index a73e875..363cd88 100644
--- a/src/DesktopMagic/DesktopMagic.csproj
+++ b/src/DesktopMagic/DesktopMagic.csproj
@@ -28,6 +28,7 @@
+
diff --git a/src/DesktopMagic/GlobalSuppressions.cs b/src/DesktopMagic/GlobalSuppressions.cs
index 2691b65..f210155 100644
--- a/src/DesktopMagic/GlobalSuppressions.cs
+++ b/src/DesktopMagic/GlobalSuppressions.cs
@@ -7,6 +7,4 @@ using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "Windows only application")]
[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "No need to")]
-[assembly: SuppressMessage("Minor Code Smell", "S1075:URIs should not be hardcoded", Justification = "")]
-[assembly: SuppressMessage("Minor Code Smell", "S3604:Member initializer values should not be redundant", Justification = "False postives")]
-[assembly: SuppressMessage("Critical Code Smell", "S2696:Instance members should not write to \"static\" fields", Justification = "", Scope = "member", Target = "~P:DesktopMagic.MainWindowDataContext.Settings")]
\ No newline at end of file
+[assembly: SuppressMessage("Minor Code Smell", "S1075:URIs should not be hardcoded", Justification = "")]
\ No newline at end of file
diff --git a/src/DesktopMagic/MainWindow.xaml b/src/DesktopMagic/MainWindow.xaml
index eb8c587..1099839 100644
--- a/src/DesktopMagic/MainWindow.xaml
+++ b/src/DesktopMagic/MainWindow.xaml
@@ -7,7 +7,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:DesktopMagic"
xmlns:dataContext="clr-namespace:DesktopMagic.DataContexts"
- d:DataContext="{d:DesignInstance Type=dataContext:PluginManagerDataContext}"
+ d:DataContext="{d:DesignInstance Type=dataContext:MainWindowDataContext}"
Closing="Window_Closing"
Closed="Window_Closed"
ShowInTaskbar="True"
@@ -48,7 +48,7 @@
-
+
@@ -56,7 +56,7 @@
-
+
diff --git a/src/DesktopMagic/MainWindow.xaml.cs b/src/DesktopMagic/MainWindow.xaml.cs
index 064bac4..2cf0032 100644
--- a/src/DesktopMagic/MainWindow.xaml.cs
+++ b/src/DesktopMagic/MainWindow.xaml.cs
@@ -5,6 +5,8 @@ using DesktopMagic.Helpers;
using DesktopMagic.Plugins;
using DesktopMagic.Settings;
+using Stone_Red_Utilities.StringExtentions;
+
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -26,12 +28,12 @@ namespace DesktopMagic
private readonly MainWindowDataContext mainWindowDataContext = new();
- private readonly Dictionary builtInPlugins = new()
+ private readonly Dictionary builtInPlugins = new()
{
- {"Music Visualizer", typeof(MusicVisualizerPlugin)},
- {"Time", typeof(TimePlugin)},
- {"Date", typeof(DatePlugin)},
- {"Cpu Usage", typeof(CpuMonitorPlugin)}
+ {new("Music Visualizer", 1), typeof(MusicVisualizerPlugin)},
+ {new("Time",2), typeof(TimePlugin)},
+ {new("Date",3), typeof(DatePlugin)},
+ {new("Cpu Usage", 4), typeof(CpuMonitorPlugin)}
};
private bool loaded = false;
@@ -76,7 +78,7 @@ namespace DesktopMagic
#region Load
- private readonly List pluginNames = [];
+ private readonly Dictionary plugins = [];
private void Window_Loaded(object sender, RoutedEventArgs e)
{
@@ -109,44 +111,41 @@ namespace DesktopMagic
private void LoadPlugins()
{
- pluginNames.Clear();
+ plugins.Clear();
- pluginNames.AddRange(builtInPlugins.Keys);
+ foreach (var buildInPlugin in builtInPlugins.Keys)
+ {
+ plugins.Add(buildInPlugin.Id, new(buildInPlugin, string.Empty));
+ }
string pluginsPath = App.ApplicationDataPath + "\\Plugins";
- foreach (string fileName in Directory.GetFiles(pluginsPath, "*.dll"))
- {
- string pluginName = fileName[(fileName.LastIndexOf('\\') + 1)..].Replace(fileName[fileName.LastIndexOf('.')..], "");
- try
- {
- _ = Directory.CreateDirectory(Path.Combine(pluginsPath, pluginName));
- File.Move(fileName, $"{pluginsPath}\\{pluginName}\\{pluginName}.dll");
- }
- catch (Exception ex)
- {
- App.Logger.Log(ex.Message, "Main", LogSeverity.Error);
- }
- }
-
foreach (string directory in Directory.GetDirectories(pluginsPath))
{
- foreach (string fileName in Directory.GetFiles(directory).Where(s => s.EndsWith(".dll", StringComparison.InvariantCulture)))
+ string? pluginDllPath = Directory.GetFiles(directory, "main.dll").FirstOrDefault();
+ string? pluginMetadataPath = Directory.GetFiles(directory, "metadata.json").FirstOrDefault();
+
+ if (pluginDllPath is null)
{
- string badChars = ",#-<>?!=()*,. ";
- string pluginName = fileName[(fileName.LastIndexOf('\\') + 1)..].Replace(fileName[fileName.LastIndexOf('.')..], "");
- string clearPluginName = pluginName;
-
- if (pluginName == directory[(directory.LastIndexOf('\\') + 1)..])
- {
- foreach (char c in badChars)
- {
- clearPluginName = clearPluginName.Replace(c, '_');
- }
-
- pluginNames.Add(pluginName);
- }
+ App.Logger.Log($"Plugin \"{directory}\" has no \"main.dll\"", "Main", LogSeverity.Error);
+ continue;
}
+
+ if (pluginMetadataPath is null)
+ {
+ App.Logger.Log($"Plugin \"{directory}\" has no \"metadata.json\"", "Main", LogSeverity.Warn);
+ continue;
+ }
+
+ PluginMetadata? pluginMetadata = JsonSerializer.Deserialize(File.ReadAllText(pluginMetadataPath));
+
+ if (pluginMetadata is null)
+ {
+ App.Logger.Log($"Plugin \"{directory}\" has no valid \"metadata.json\"", "Main", LogSeverity.Error);
+ continue;
+ }
+
+ plugins.Add(pluginMetadata.Id, new(pluginMetadata, directory));
}
}
@@ -167,31 +166,36 @@ namespace DesktopMagic
return;
}
- LoadPlugin(checkBox.Content.ToString() ?? string.Empty);
+ LoadPlugin(uint.Parse(checkBox.Tag.ToString()!));
}
- private void LoadPlugin(string pluginName)
+ private void LoadPlugin(uint pluginId)
{
- if (!Settings.CurrentLayout.Plugins.TryGetValue(pluginName, out PluginSettings? pluginSettings))
+ if (!plugins.TryGetValue(pluginId, out InternalPluginData? internalPluginData))
+ {
+ return;
+ }
+
+ if (!Settings.CurrentLayout.Plugins.TryGetValue(pluginId, out PluginSettings? pluginSettings))
{
pluginSettings = new PluginSettings();
- Settings.CurrentLayout.Plugins.Add(pluginName, pluginSettings);
+ Settings.CurrentLayout.Plugins.Add(pluginId, pluginSettings);
}
PluginWindow window;
- if (builtInPlugins.TryGetValue(pluginName, out Type? pluginType))
+ if (builtInPlugins.TryGetValue(internalPluginData.Metadata, out Type? pluginType))
{
- window = new PluginWindow((DesktopMagic.Api.Plugin)Activator.CreateInstance(pluginType)!, pluginName, pluginSettings)
+ window = new PluginWindow((Api.Plugin)Activator.CreateInstance(pluginType)!, internalPluginData.Metadata, pluginSettings)
{
- Title = pluginName
+ Title = internalPluginData.Metadata.Name
};
}
else
{
- window = new PluginWindow(pluginName, pluginSettings)
+ window = new PluginWindow(internalPluginData.Metadata, pluginSettings, internalPluginData.DirectoryPath)
{
- Title = pluginName
+ Title = internalPluginData.Metadata.Name
};
}
@@ -208,12 +212,12 @@ namespace DesktopMagic
{
Dispatcher.Invoke(() =>
{
- if (!optionsComboBox.Items.Contains(pluginName))
+ if (!optionsComboBox.Items.Contains(internalPluginData.Metadata))
{
- _ = optionsComboBox.Items.Add(pluginName);
+ _ = optionsComboBox.Items.Add(internalPluginData.Metadata);
}
optionsComboBox.SelectedIndex = -1;
- optionsComboBox.SelectedIndex = optionsComboBox.Items.IndexOf(pluginName);
+ optionsComboBox.SelectedIndex = optionsComboBox.Items.IndexOf(internalPluginData.Metadata);
window.PluginLoaded -= onPluginLoaded;
});
};
@@ -326,7 +330,7 @@ namespace DesktopMagic
return;
}
- bool success = Settings.CurrentLayout.Plugins.TryGetValue(optionsComboBox.SelectedItem.ToString()!, out Settings.PluginSettings? pluginSettings);
+ bool success = Settings.CurrentLayout.Plugins.TryGetValue(((PluginMetadata)optionsComboBox.SelectedItem).Id, out Settings.PluginSettings? pluginSettings);
if (!success || pluginSettings is null || pluginSettings.Settings.Count == 0)
{
_ = optionsPanel.Children.Add(new TextBlock() { Text = (string)FindResource("noOptions") });
@@ -561,19 +565,22 @@ namespace DesktopMagic
bool showWindow = true;
// Load plugins
- foreach (string pluginName in pluginNames)
+ foreach (uint pluginId in plugins.Keys)
{
// Add plugin to layout if it doesn't exist
- if (!Settings.CurrentLayout.Plugins.TryGetValue(pluginName, out PluginSettings? pluginSettings))
+ if (!Settings.CurrentLayout.Plugins.TryGetValue(pluginId, out PluginSettings? pluginSettings))
{
- Settings.CurrentLayout.Plugins.Add(pluginName, new PluginSettings());
+ Settings.CurrentLayout.Plugins.Add(pluginId, new PluginSettings());
continue;
}
+ InternalPluginData internalPluginData = plugins[pluginId];
+ pluginSettings.Name = internalPluginData.Metadata.Name;
+
if (pluginSettings.Enabled)
{
- LoadPlugin(pluginName);
+ LoadPlugin(pluginId);
}
if (showWindow && pluginSettings.Enabled)
@@ -583,11 +590,11 @@ namespace DesktopMagic
}
// Remove plugins that are not loaded anymore
- foreach (string pluginName in Settings.CurrentLayout.Plugins.Keys)
+ foreach (uint pluginId in Settings.CurrentLayout.Plugins.Keys)
{
- if (!pluginNames.Contains(pluginName))
+ if (!plugins.ContainsKey(pluginId))
{
- Settings.CurrentLayout.Plugins.Remove(pluginName);
+ Settings.CurrentLayout.Plugins.Remove(pluginId);
}
}
@@ -646,6 +653,7 @@ namespace DesktopMagic
PluginManager pluginManager = new PluginManager();
pluginManager.ShowDialog();
LoadPlugins();
+ LoadLayout(false);
}
private void SetLanguageDictionary()
@@ -664,4 +672,10 @@ namespace DesktopMagic
Resources.MergedDictionaries.Add(dict);
}
}
+
+ internal class InternalPluginData(PluginMetadata pluginMetadata, string directoryPath)
+ {
+ public PluginMetadata Metadata { get; set; } = pluginMetadata;
+ public string DirectoryPath { get; set; } = directoryPath;
+ }
}
\ No newline at end of file
diff --git a/src/DesktopMagic/Plugins/ModEntry.xaml b/src/DesktopMagic/Plugins/ModEntry.xaml
index 0a877b2..a48cbd9 100644
--- a/src/DesktopMagic/Plugins/ModEntry.xaml
+++ b/src/DesktopMagic/Plugins/ModEntry.xaml
@@ -1,41 +1,42 @@
-
-
+
+
-
-
+
+
-
+
-
-
-
-
+
+
+
+
+
-
-
+
+
-
+
\ No newline at end of file
diff --git a/src/DesktopMagic/Plugins/PluginData.cs b/src/DesktopMagic/Plugins/PluginData.cs
index 7299733..19162d3 100644
--- a/src/DesktopMagic/Plugins/PluginData.cs
+++ b/src/DesktopMagic/Plugins/PluginData.cs
@@ -15,7 +15,7 @@ internal class PluginData(PluginWindow window, PluginSettings pluginSettings) :
public Point WindowPosition => new Point((int)window.Left, (int)window.Top);
- public string PluginName => window.PluginName;
+ public string PluginName => window.PluginMetadata.Name;
public string PluginPath => window.PluginFolderPath;
diff --git a/src/DesktopMagic/Plugins/PluginManager.xaml b/src/DesktopMagic/Plugins/PluginManager.xaml
index 8aadf28..462fdde 100644
--- a/src/DesktopMagic/Plugins/PluginManager.xaml
+++ b/src/DesktopMagic/Plugins/PluginManager.xaml
@@ -4,54 +4,59 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
+ xmlns:busyIndicator="https://github.com/moh3ngolshani/BusyIndicator"
xmlns:local="clr-namespace:DesktopMagic.Plugins"
xmlns:dataContexts="clr-namespace:DesktopMagic.DataContexts"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type=dataContexts:PluginManagerDataContext}"
- Title="PluginManager"
+ Title="PluginManager"
Height="450"
Width="800"
MinHeight="520"
MinWidth="700"
+ WindowStyle="ToolWindow"
Background="{DynamicResource MaterialDesignPaper}"
FontFamily="{DynamicResource MaterialDesignFont}">
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
+
-
-
-
-
-
-
-
-
-
+
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/DesktopMagic/Plugins/PluginManager.xaml.cs b/src/DesktopMagic/Plugins/PluginManager.xaml.cs
index 9255621..61e713e 100644
--- a/src/DesktopMagic/Plugins/PluginManager.xaml.cs
+++ b/src/DesktopMagic/Plugins/PluginManager.xaml.cs
@@ -6,17 +6,28 @@ using Modio.NET.Models;
using System;
using System.Collections.Generic;
+using System.ComponentModel;
using System.Diagnostics;
using System.IO;
+using System.IO.Compression;
+using System.Linq;
+using System.Net.Http;
+using System.Text.Json;
+using System.Threading.Tasks;
using System.Windows;
+using File = System.IO.File;
+using Path = System.IO.Path;
+
namespace DesktopMagic.Plugins;
+
///
/// Interaction logic for PluginManager.xaml
///
public partial class PluginManager : Window
{
private readonly Client client = new Client(new Credentials("88e6ea774c3a502b06114e7fee0829ac"));
+ private readonly HttpClient httpClient = new();
private readonly PluginManagerDataContext pluginManagerDataContext = new();
private readonly string pluginsPath = Path.Combine(App.ApplicationDataPath, "Plugins");
@@ -28,19 +39,114 @@ public partial class PluginManager : Window
DataContext = pluginManagerDataContext;
}
+ public void Remove(string pluginPath, uint? id)
+ {
+ pluginManagerDataContext.IsLoading = true;
+
+ PluginEntryDataContext? pluginEntryDataContext = pluginManagerDataContext.InstalledPlugins.FirstOrDefault(p => p.Id == id);
+
+ if (pluginEntryDataContext is not null)
+ {
+ _ = pluginManagerDataContext.InstalledPlugins.Remove(pluginEntryDataContext);
+ }
+
+ if (Directory.Exists(pluginPath))
+ {
+ Directory.Delete(pluginPath, true);
+ }
+
+ pluginManagerDataContext.IsLoading = false;
+ }
+
protected override async void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
- IReadOnlyList mods = await client.Games[5665].Mods.Search().ToListAsync();
- foreach (Mod mod in mods)
+ HashSet pluginIds = [];
+
+ foreach (string pluginPath in Directory.GetDirectories(pluginsPath))
{
- pluginManagerDataContext.Mods.Add(new ModEntryDataContext(mod, new CommandHandler(() => Install(mod))));
+ string pluginMetadataPath = Path.Combine(pluginPath, "metadata.json");
+ if (!File.Exists(pluginMetadataPath))
+ {
+ continue;
+ }
+
+ PluginMetadata? pluginMetadata = JsonSerializer.Deserialize(File.ReadAllText(pluginMetadataPath));
+
+ if (pluginMetadata is not null)
+ {
+ pluginManagerDataContext.InstalledPlugins.Add(new PluginEntryDataContext(pluginMetadata, new CommandHandler(() => Remove(pluginPath, pluginMetadata.Id)), true));
+ _ = pluginIds.Add(pluginMetadata.Id);
+ }
+ }
+
+ IAsyncEnumerable mods = client.Games[5665].Mods.Search().ToEnumerableAsync();
+ await foreach (Mod mod in mods)
+ {
+ if (pluginIds.Contains(mod.Id))
+ {
+ continue;
+ }
+
+ pluginManagerDataContext.AllPlugins.Add(new PluginEntryDataContext(new(mod), new CommandHandler(async () => await Install(mod))));
}
}
- private void Install(Mod mod)
+ protected override void OnClosing(CancelEventArgs e)
{
- Debug.WriteLine(mod.Modfile?.Download?.BinaryUrl + " | " + pluginsPath);
+ e.Cancel = pluginManagerDataContext.IsLoading;
}
-}
+
+ private async Task Install(Mod mod)
+ {
+ pluginManagerDataContext.IsLoading = true;
+
+ Debug.WriteLine(mod.Modfile?.Download?.BinaryUrl + " | " + pluginsPath);
+
+ if (mod.Modfile?.Download?.BinaryUrl is null)
+ {
+ return;
+ }
+
+ string pluginGuid = Guid.NewGuid().ToString();
+ string pluginPath = Path.Combine(pluginsPath, pluginGuid);
+ string zipFilePath = Path.Combine(pluginsPath, pluginGuid + ".zip");
+
+ using (Stream fileStream = await httpClient.GetStreamAsync(mod.Modfile.Download.BinaryUrl))
+ {
+ using FileStream outputFileStream = new FileStream(zipFilePath, FileMode.Create);
+
+ await fileStream.CopyToAsync(outputFileStream);
+ }
+
+ using (ZipArchive zipArchive = ZipFile.OpenRead(zipFilePath))
+ {
+ zipArchive.ExtractToDirectory(pluginPath);
+ }
+
+ string pluginMetadataPath = Path.Combine(pluginPath, "metadata.json");
+ File.WriteAllText(pluginMetadataPath, JsonSerializer.Serialize(new PluginMetadata(mod)));
+
+ File.Delete(zipFilePath);
+
+ if (!File.Exists(Path.Combine(pluginPath, "main.dll")))
+ {
+ Remove(pluginPath, mod.Id);
+ pluginManagerDataContext.IsLoading = false;
+ _ = MessageBox.Show("The plugin you are trying to install does not contain a \"main.dll\" file. Please contact the plugin author.", "Plugin Manager", MessageBoxButton.OK, MessageBoxImage.Error);
+ return;
+ }
+
+ PluginEntryDataContext? pluginEntryDataContext = pluginManagerDataContext.AllPlugins.FirstOrDefault(p => p.Id == mod.Id);
+
+ if (pluginEntryDataContext is not null)
+ {
+ _ = pluginManagerDataContext.AllPlugins.Remove(pluginEntryDataContext);
+ }
+
+ pluginManagerDataContext.InstalledPlugins.Add(new PluginEntryDataContext(new PluginMetadata(mod), new CommandHandler(() => Remove(pluginPath, mod.Id)), true));
+
+ pluginManagerDataContext.IsLoading = false;
+ }
+}
\ No newline at end of file
diff --git a/src/DesktopMagic/Plugins/PluginMetadata.cs b/src/DesktopMagic/Plugins/PluginMetadata.cs
new file mode 100644
index 0000000..a476b82
--- /dev/null
+++ b/src/DesktopMagic/Plugins/PluginMetadata.cs
@@ -0,0 +1,63 @@
+using Modio.NET.Models;
+
+using System;
+using System.Text.Json.Serialization;
+
+namespace DesktopMagic.Plugins;
+
+public class PluginMetadata
+{
+ public string Name { get; set; }
+
+ public uint Id { get; set; }
+
+ public string? Author { get; set; }
+
+ public Uri? IconUri { get; set; }
+
+ public DateTime? Added { get; set; }
+
+ public DateTime? Updated { get; set; }
+
+ public string? Description { get; set; }
+
+ public string? Version { get; set; }
+
+ public PluginMetadata(Mod mod)
+ {
+ Name = mod.Name ?? mod.Id.ToString();
+ Id = mod.Id;
+ Author = mod.SubmittedBy?.Username;
+ IconUri = mod.Logo?.Thumb320x180;
+ Added = DateTimeOffset.FromUnixTimeSeconds(mod.DateAdded).DateTime;
+ Updated = DateTimeOffset.FromUnixTimeSeconds(mod.DateUpdated).DateTime;
+ Description = mod.DescriptionPlaintext;
+ Version = mod.Modfile?.Version;
+ }
+
+ public PluginMetadata(string name, uint id)
+ {
+ Name = name;
+ Id = id;
+ }
+
+ [JsonConstructor]
+ public PluginMetadata()
+ {
+ }
+
+ public override bool Equals(object? obj)
+ {
+ if (obj is not PluginMetadata other)
+ {
+ return false;
+ }
+
+ return Id == other.Id;
+ }
+
+ public override int GetHashCode()
+ {
+ return Id.GetHashCode();
+ }
+}
\ No newline at end of file
diff --git a/src/DesktopMagic/Plugins/PluginWindow.xaml.cs b/src/DesktopMagic/Plugins/PluginWindow.xaml.cs
index 0864cb6..93c1c63 100644
--- a/src/DesktopMagic/Plugins/PluginWindow.xaml.cs
+++ b/src/DesktopMagic/Plugins/PluginWindow.xaml.cs
@@ -34,10 +34,10 @@ public partial class PluginWindow : Window
private Plugin? pluginClassInstance;
public bool IsRunning { get; private set; } = true;
- public string PluginName { get; private set; }
+ public PluginMetadata PluginMetadata { get; private set; }
public string? PluginFolderPath { get; private set; }
- public PluginWindow(string pluginName, PluginSettings settings)
+ public PluginWindow(PluginMetadata pluginMetadata, PluginSettings settings, string pluginFolderPath)
{
InitializeComponent();
@@ -62,16 +62,18 @@ public partial class PluginWindow : Window
t.Elapsed += UpdateTimer_Elapsed;
t.Start();
- PluginName = pluginName;
+ PluginMetadata = pluginMetadata;
this.settings = settings;
Left = settings.Position.X;
Top = settings.Position.Y;
Width = settings.Size.X;
Height = settings.Size.Y;
+
+ PluginFolderPath = pluginFolderPath;
}
- public PluginWindow(Plugin pluginClassInstance, string pluginName, Settings.PluginSettings settings) : this(pluginName, settings)
+ public PluginWindow(Plugin pluginClassInstance, PluginMetadata pluginMetadata, PluginSettings settings) : this(pluginMetadata, settings, string.Empty)
{
this.pluginClassInstance = pluginClassInstance;
}
@@ -119,7 +121,7 @@ public partial class PluginWindow : Window
private void Window_ContentRendered(object? sender, EventArgs e)
{
- App.Logger.Log($"\"{PluginName}\" - Starting plugin thread", "Plugin");
+ App.Logger.Log($"\"{PluginMetadata}\" - Starting plugin thread", "Plugin");
pluginThread = new Thread(LoadPlugin);
pluginThread.Start();
}
@@ -165,20 +167,15 @@ public partial class PluginWindow : Window
private void LoadPlugin()
{
- App.Logger.Log($"\"{PluginName}\" - Loading plugin", "Plugin");
+ App.Logger.Log($"\"{PluginMetadata}\" - Loading plugin", "Plugin");
- if (pluginClassInstance is null)
+ if (pluginClassInstance is null && !File.Exists($"{PluginFolderPath}\\main.dll"))
{
- PluginFolderPath = $"{App.ApplicationDataPath}\\Plugins\\{PluginName}";
+ App.Logger.Log($"\"{PluginMetadata}\" - File \"main.dll\" does not exist", "Plugin", LogSeverity.Error);
+ _ = MessageBox.Show("File \"main.dll\" does not exist!", $"Error \"{PluginMetadata}\"", MessageBoxButton.OK, MessageBoxImage.Error);
- if (!File.Exists($"{PluginFolderPath}\\{PluginName}.dll"))
- {
- App.Logger.Log($"\"{PluginName}\" - File does not exist", "Plugin", LogSeverity.Error);
- _ = MessageBox.Show("File does not exist!", $"Error \"{PluginName}\"", MessageBoxButton.OK, MessageBoxImage.Error);
-
- Exit();
- return;
- }
+ Exit();
+ return;
}
try
@@ -187,8 +184,8 @@ public partial class PluginWindow : Window
}
catch (Exception ex)
{
- App.Logger.Log($"\"{PluginName}\" - {ex}", "Plugin", LogSeverity.Error);
- _ = MessageBox.Show("File execution error:\n" + ex, $"Error \"{PluginName}\"", MessageBoxButton.OK, MessageBoxImage.Error);
+ App.Logger.Log($"\"{PluginMetadata}\" - {ex}", "Plugin", LogSeverity.Error);
+ _ = MessageBox.Show("File execution error:\n" + ex, $"Error \"{PluginMetadata}\"", MessageBoxButton.OK, MessageBoxImage.Error);
Exit();
return;
}
@@ -200,14 +197,14 @@ public partial class PluginWindow : Window
object? instance = pluginClassInstance;
if (instance is null)
{
- byte[] assemblyBytes = File.ReadAllBytes($"{PluginFolderPath}\\{PluginName}.dll");
+ byte[] assemblyBytes = File.ReadAllBytes($"{PluginFolderPath}\\main.dll");
Assembly dll = Assembly.Load(assemblyBytes);
Type? instanceType = Array.Find(dll.GetTypes(), type => type.GetTypeInfo().BaseType == typeof(Plugin));
if (instanceType is null)
{
- App.Logger.Log($"\"{PluginName}\" - The \"Plugin\" class could not be found! It has to inherit from \"{typeof(Plugin).FullName}\"", "Plugin", LogSeverity.Error);
- _ = MessageBox.Show($"The \"Plugin\" class could not be found! It has to inherit from \"{typeof(Plugin).FullName}\"", $"Error \"{PluginName}\"", MessageBoxButton.OK, MessageBoxImage.Error);
+ App.Logger.Log($"\"{PluginMetadata}\" - The \"Plugin\" class could not be found! It has to inherit from \"{typeof(Plugin).FullName}\"", "Plugin", LogSeverity.Error);
+ _ = MessageBox.Show($"The \"Plugin\" class could not be found! It has to inherit from \"{typeof(Plugin).FullName}\"", $"Error \"{PluginMetadata}\"", MessageBoxButton.OK, MessageBoxImage.Error);
Exit();
return;
@@ -222,8 +219,8 @@ public partial class PluginWindow : Window
}
else
{
- App.Logger.Log($"\"{PluginName}\" - The \"Plugin\" class could not be found! It has to inherit from \"{typeof(Plugin).FullName}\"", "Plugin", LogSeverity.Error);
- _ = MessageBox.Show($"The \"Plugin\" class has to inherit from \"{typeof(Plugin).FullName}\"", $"Error \"{PluginName}\"", MessageBoxButton.OK, MessageBoxImage.Error);
+ App.Logger.Log($"\"{PluginMetadata}\" - The \"Plugin\" class could not be found! It has to inherit from \"{typeof(Plugin).FullName}\"", "Plugin", LogSeverity.Error);
+ _ = MessageBox.Show($"The \"Plugin\" class has to inherit from \"{typeof(Plugin).FullName}\"", $"Error \"{PluginMetadata}\"", MessageBoxButton.OK, MessageBoxImage.Error);
Exit();
return;
}
@@ -283,7 +280,7 @@ public partial class PluginWindow : Window
private void LoadOptions(object instance)
{
- App.Logger.Log($"\"{PluginName}\" - Loading plugin options", "Plugin");
+ App.Logger.Log($"\"{PluginMetadata}\" - Loading plugin options", "Plugin");
try
{
@@ -321,8 +318,8 @@ public partial class PluginWindow : Window
catch (Exception ex)
{
IsRunning = false;
- App.Logger.Log($"\"{PluginName}\" - {ex}", "Plugin", LogSeverity.Error);
- _ = MessageBox.Show("File execution error:\n" + ex, $"Error \"{PluginName}\"", MessageBoxButton.OK, MessageBoxImage.Error);
+ App.Logger.Log($"\"{PluginMetadata}\" - {ex}", "Plugin", LogSeverity.Error);
+ _ = MessageBox.Show("File execution error:\n" + ex, $"Error \"{PluginMetadata}\"", MessageBoxButton.OK, MessageBoxImage.Error);
Exit();
}
}
@@ -366,8 +363,8 @@ public partial class PluginWindow : Window
catch (Exception ex)
{
IsRunning = false;
- App.Logger.Log($"\"{PluginName}\" - {ex}", "Plugin", LogSeverity.Error);
- _ = MessageBox.Show("File execution error:\n" + ex, $"Error \"{PluginName}\"", MessageBoxButton.OK, MessageBoxImage.Error);
+ App.Logger.Log($"\"{PluginMetadata}\" - {ex}", "Plugin", LogSeverity.Error);
+ _ = MessageBox.Show("File execution error:\n" + ex, $"Error \"{PluginMetadata}\"", MessageBoxButton.OK, MessageBoxImage.Error);
Exit();
return;
}
@@ -380,7 +377,7 @@ public partial class PluginWindow : Window
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
- App.Logger.Log($"\"{PluginName}\" - Stopping plugin", "Plugin");
+ App.Logger.Log($"\"{PluginMetadata}\" - Stopping plugin", "Plugin");
pluginClassInstance?.Stop();
IsRunning = false;
}
diff --git a/src/DesktopMagic/Plugins/SettingElement.cs b/src/DesktopMagic/Plugins/SettingElement.cs
index 074f4e6..cbc45a6 100644
--- a/src/DesktopMagic/Plugins/SettingElement.cs
+++ b/src/DesktopMagic/Plugins/SettingElement.cs
@@ -44,8 +44,7 @@ public class SettingElement
[JsonConstructor]
private SettingElement()
+#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
{
}
-
-#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
}
\ No newline at end of file
diff --git a/src/DesktopMagic/Settings/Layout.cs b/src/DesktopMagic/Settings/Layout.cs
index 11ac819..03a42d9 100644
--- a/src/DesktopMagic/Settings/Layout.cs
+++ b/src/DesktopMagic/Settings/Layout.cs
@@ -13,7 +13,7 @@ internal class Layout(string name) : INotifyPropertyChanged
private string name = name;
private Theme theme = new Theme();
- private Dictionary plugins = [];
+ private Dictionary plugins = [];
public Theme Theme
{
@@ -25,7 +25,7 @@ internal class Layout(string name) : INotifyPropertyChanged
}
}
- public Dictionary Plugins
+ public Dictionary Plugins
{
get => plugins;
set
diff --git a/src/DesktopMagic/Settings/PluginSettings.cs b/src/DesktopMagic/Settings/PluginSettings.cs
index 085cc96..b9ae278 100644
--- a/src/DesktopMagic/Settings/PluginSettings.cs
+++ b/src/DesktopMagic/Settings/PluginSettings.cs
@@ -22,6 +22,10 @@ public class PluginSettings : INotifyPropertyChanged
[JsonIgnore]
public Theme Theme => theme is null ? MainWindowDataContext.GetSettings().CurrentLayout.Theme : theme;
+ // Only for internal use to show the name of the plugin in the main window
+ [JsonIgnore]
+ public string Name { get; set; } = string.Empty;
+
public List Settings
{
get => settings;