From 294005ed86982abe13b64b5c4747b746d43a9a1c Mon Sep 17 00:00:00 2001 From: LucHeart Date: Sun, 5 Jan 2025 00:35:39 +0100 Subject: [PATCH] Module downloader --- Desktop/ModuleManager/ModuleManager.cs | 48 ++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/Desktop/ModuleManager/ModuleManager.cs b/Desktop/ModuleManager/ModuleManager.cs index 102782e..e428c3f 100644 --- a/Desktop/ModuleManager/ModuleManager.cs +++ b/Desktop/ModuleManager/ModuleManager.cs @@ -1,4 +1,6 @@ using System.Collections.Concurrent; +using System.IO.Compression; +using System.Net.Mime; using System.Reflection; using System.Runtime.Loader; using ModuleBase; @@ -8,34 +10,66 @@ namespace OpenShock.Desktop.ModuleManager; public sealed class ModuleManager { private static readonly Type ModuleType = typeof(IModule); - + private readonly IServiceProvider _serviceProvider; private readonly ILogger _logger; - + private static string ModuleDirectory => Path.Combine(Constants.AppdataFolder, "modules"); + private static readonly HttpClient HttpClient = new() + { + Timeout = TimeSpan.FromMinutes(5) + }; + public ModuleManager(IServiceProvider serviceProvider, ILogger logger) { _serviceProvider = serviceProvider; _logger = logger; } - + public readonly ConcurrentDictionary Modules = new(); + private async Task DownloadModule(string moduleId, Uri moduleUrl) + { + using var downloadResponse = await HttpClient.GetAsync(moduleUrl, HttpCompletionOption.ResponseHeadersRead); + + if (!downloadResponse.IsSuccessStatusCode) + { + _logger.LogError("Failed to download module {ModuleId} from {ModuleUrl} with status code {StatusCode}", + moduleId, moduleUrl, downloadResponse.StatusCode); + return; + } + + if (downloadResponse.Content.Headers.ContentType?.MediaType != MediaTypeNames.Application.Zip) + { + _logger.LogError("Failed to download module {ModuleId} from {ModuleUrl} as it is not a ZIP file (Content-Type: {ContentType})", + moduleId, moduleUrl, downloadResponse.Content.Headers.ContentType?.MediaType); + return; + } + + var moduleFolderPath = Path.Combine(ModuleDirectory, moduleId); + + if (Directory.Exists(moduleFolderPath)) Directory.Delete(moduleFolderPath, true); + + Directory.CreateDirectory(moduleFolderPath); + ZipFile.ExtractToDirectory(await downloadResponse.Content.ReadAsStreamAsync(), moduleFolderPath, true); + } + private void LoadModule(string moduleFolderPath) { _logger.LogTrace("Searching for module in {Path}", moduleFolderPath); var moduleFile = Directory.GetFiles(moduleFolderPath, "*.dll", SearchOption.TopDirectoryOnly).FirstOrDefault(); - if(moduleFile == null) + if (moduleFile == null) { _logger.LogWarning("No DLLs found in root module folder!"); return; } + _logger.LogDebug("Attempting to load module: {Path}", moduleFile); var assemblyLoadContext = new ModuleAssemblyLoadContext(moduleFolderPath); var assembly = assemblyLoadContext.LoadFromAssemblyPath(moduleFile); - + var pluginTypes = assembly.GetTypes().Where(t => t.IsAssignableTo(ModuleType)).ToArray(); switch (pluginTypes.Length) { @@ -49,7 +83,7 @@ private void LoadModule(string moduleFolderPath) var module = (IModule?)ActivatorUtilities.CreateInstance(_serviceProvider, pluginTypes[0]); if (module is null) throw new Exception("Failed to instantiate module!"); - + var loadedModule = new LoadedModule { LoadContext = assemblyLoadContext, @@ -80,4 +114,4 @@ internal void LoadAll() } } } -} +} \ No newline at end of file