diff --git a/MelonModUpdater/Core.cs b/MelonModUpdater/Core.cs index b092001..1c7e8b6 100644 --- a/MelonModUpdater/Core.cs +++ b/MelonModUpdater/Core.cs @@ -141,29 +141,6 @@ internal static Task CopyTo(Stream input, Stream output) return Task.Factory.StartNew(() => true); } - /// - /// Transform function (or action) to a task
- /// Made to work with net35 - ///
- /// The action that u want to convert into a task - /// A task of the provided action - internal static Task ToTask(Action action) - { - TaskCompletionSource taskCompletionSource = new - TaskCompletionSource(); - string result = null; - try - { - Task.Factory.StartNew(() => action()); - } - catch (Exception ex) - { - result = ex.Message; - } - taskCompletionSource.SetResult(result); - return taskCompletionSource.Task; - } - /// /// Create's an empty task, so you can return null with tasks
/// Made to work with net35 @@ -272,7 +249,6 @@ internal Task GetModData(string downloadLink) packageName = split[split.Length - 1]; namespaceName = split[split.Length - 2]; } - HttpClient request = new HttpClient(); Task response = request.GetAsync($"https://thunderstore.io/api/experimental/package/{namespaceName}/{packageName}/"); response.Wait(); @@ -288,10 +264,18 @@ internal Task GetModData(string downloadLink) response.Dispose(); body.Dispose(); + List files = new List(); + + FileData fileData = new FileData(); + fileData.FileName = packageName; + fileData.URL = (string)_data["latest"]["download_url"]; + + files.Add(fileData); + return Task.Factory.StartNew(() => new ModData() { LatestVersion = ModVersion.GetFromString((string)_data["latest"]["version_number"]), - DownloadFiles = new Dictionary() { { (string)_data["latest"]["download_url"], "" } }, + DownloadFiles = files, }); } else @@ -368,12 +352,11 @@ internal Task GetModData(string downloadLink) { var data = JSON.Load(body.Result); string version = (string)data["tag_name"]; - Dictionary downloadURLs = new Dictionary(); + List downloadURLs = new List(); foreach (var file in data["assets"] as ProxyArray) { - LoggerInstance.Msg("File: " + (string)file["browser_download_url"]); - downloadURLs.Add((string)file["browser_download_url"], (string)file["content_type"]); + downloadURLs.Add(new FileData() { URL = (string)file["browser_download_url"], ContentType = (string)file["content_type"], FileName = Path.GetFileNameWithoutExtension((string)file["browser_download_url"]) }); } client.Dispose(); @@ -470,10 +453,16 @@ internal Task GetModDataFromInfo(string name, string author) response.Dispose(); body.Dispose(); + List files = new List(); + + FileData fileData = new FileData(); + fileData.FileName = name; + fileData.URL = (string)_data["latest"]["download_url"]; + return Task.Factory.StartNew(() => new ModData() { LatestVersion = ModVersion.GetFromString((string)_data["latest"]["version_number"]), - DownloadFiles = new Dictionary() { { (string)_data["latest"]["download_url"], "" } }, + DownloadFiles = files, }); } else @@ -505,6 +494,13 @@ internal Task GetModDataFromInfo(string name, string author) return CreateEmptyTask(); } + /// + /// Get path to save a file from contentType & name provided + /// + /// Content Type (Example: application/zip) + /// Name of the file, without extension + /// A path to temporary directory with file name and extension according to contentType + internal string GetPathFromContentType(string contentType, string name) { if (contentType == "application/zip") @@ -538,6 +534,22 @@ internal static FileType GetFileType(string path) return FileType.Other; } + internal void ReplaceAllFiles(string path, string directory) + { + foreach (string file in Directory.GetFiles(path)) + { + string _path = Path.Combine(directory, Path.GetFileName(file)); + if (!File.Exists(_path)) File.Move(file, _path); + else File.Replace(file, _path, Path.Combine(backupFolderPath, $"{Path.GetFileName(path)}-{DateTimeOffset.Now.ToUnixTimeSeconds()}.{Path.GetExtension(file)}")); + } + foreach (string dir in Directory.GetDirectories(path)) + { + string _path = Path.Combine(directory, Path.GetDirectoryName(dir)); + if (!Directory.Exists(_path)) Directory.CreateDirectory(_path); + ReplaceAllFiles(dir, _path); + } + } + /// /// Get value from a custom attribute /// @@ -562,7 +574,10 @@ internal static MelonInfoAttribute GetMelonInfo(string path) AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly(path); foreach (var attr in assembly.CustomAttributes) { - if (attr.AttributeType.Name == nameof(MelonInfoAttribute)) +#pragma warning disable CS0618 // Type or member is obsolete + if (attr.AttributeType.Name == nameof(MelonInfoAttribute) + || attr.AttributeType.Name == nameof(MelonModInfoAttribute) + || attr.AttributeType.Name == nameof(MelonPluginInfoAttribute)) { var _type = Get(attr, 0); Type type = _type.BaseType.Name == "MelonMod" ? typeof(MelonMod) : _type.BaseType.Name == "MelonPlugin" ? typeof(MelonPlugin) : null; @@ -575,6 +590,7 @@ internal static MelonInfoAttribute GetMelonInfo(string path) return new MelonInfoAttribute(type: type, name: Name, version: Version, author: Author, downloadLink: DownloadLink); } +#pragma warning restore CS0618 // Type or member is obsolete } assembly.Dispose(); return null; @@ -678,7 +694,6 @@ internal void CheckDirectory(string directory, bool automatic = true) FileType fileType = GetFileType(path); if (fileType != FileType.Other) { - LoggerInstance.Msg("File Type"); var melonAssemblyInfo = GetMelonInfo(path); string assemblyName = (string)melonAssemblyInfo.Name.Clone(); if (melonAssemblyInfo != null) @@ -689,7 +704,7 @@ internal void CheckDirectory(string directory, bool automatic = true) if (!IsCompatible(loaderVer, BuildInfo.Version)) { string installString = loaderVer.IsMinimum ? $"{loaderVer.SemVer} or later" : $"{loaderVer.SemVer} specifically"; - LoggerInstance.Warning($"{assemblyName} is not compatible with the current version of MelonLoader ({BuildInfo.Version}), for it to work you need to install {installString}"); + LoggerInstance.Warning($"{assemblyName} {melonAssemblyInfo.Version} is not compatible with the current version of MelonLoader ({BuildInfo.Version}), for it to work you need to install {installString}"); continue; } } @@ -719,21 +734,22 @@ internal void CheckDirectory(string directory, bool automatic = true) foreach (var retFile in data.Result.DownloadFiles) { var httpClient = new HttpClient(); - var response = httpClient.GetAsync(retFile.Key, HttpCompletionOption.ResponseHeadersRead); + var response = httpClient.GetAsync(retFile.URL, HttpCompletionOption.ResponseHeadersRead); response.Wait(); FileStream downloadedFile = null; string pathToSave = ""; + string name = !string.IsNullOrEmpty(retFile.FileName) ? retFile.FileName : melonAssemblyInfo.Name; try { response.Result.EnsureSuccessStatusCode(); string _contentType = response.Result.Content.Headers.ContentType.MediaType; - if (!string.IsNullOrEmpty(retFile.Value)) + if (!string.IsNullOrEmpty(retFile.ContentType)) { - pathToSave = GetPathFromContentType(retFile.Value, melonAssemblyInfo.Name); + pathToSave = GetPathFromContentType(retFile.ContentType, name); } else if (_contentType != null) { - pathToSave = GetPathFromContentType(_contentType, melonAssemblyInfo.Name); + pathToSave = GetPathFromContentType(_contentType, name); } var ms = response.Result.Content.ReadAsStreamAsync(); @@ -762,7 +778,7 @@ internal void CheckDirectory(string directory, bool automatic = true) if (Path.GetExtension(pathToSave) == ".zip") { LoggerInstance.Msg("Downloaded file is a ZIP file, extracting files..."); - string extractPath = Path.Combine(tempFilesPath, melonAssemblyInfo.Name.Replace(" ", "-")); + string extractPath = Path.Combine(tempFilesPath, name.Replace(" ", "-")); try { Task unzipTask = UnzipFromStream(File.OpenRead(pathToSave), extractPath); @@ -786,90 +802,106 @@ internal void CheckDirectory(string directory, bool automatic = true) { if (Directory.Exists(extPath)) { - foreach (var fPath in Directory.GetFiles(extPath, "*.dll")) + string dirName = Path.GetDirectoryName(extPath); + if (dirName != "MelonLoader" && dirName != "UserData") { - FileType _fileType = GetFileType(fPath); - if (_fileType == FileType.MelonMod) + foreach (var fPath in Directory.GetFiles(extPath, "*.dll")) { - try + FileType _fileType = GetFileType(fPath); + if (_fileType == FileType.MelonMod) { - LoggerInstance.Msg("Installing mod file " + Path.GetFileName(fPath)); - var _loaderVer = GetLoaderVersionRequired(fPath); - if (_loaderVer != null) + try { - if (!IsCompatible(_loaderVer, BuildInfo.Version)) + LoggerInstance.Msg("Installing mod file " + Path.GetFileName(fPath)); + var _loaderVer = GetLoaderVersionRequired(fPath); + if (_loaderVer != null) { - string installString = _loaderVer.IsMinimum ? $"{_loaderVer.SemVer} or later" : $"{_loaderVer.SemVer} specifically"; - LoggerInstance.Warning($"{Path.GetFileName(fPath)} ({GetMelonInfo(fPath).Version}), a newly downloaded mod, is not compatible with the current version of MelonLoader ({BuildInfo.Version}), for it to work you need to install {installString}."); - LoggerInstance.Warning($"Not installing {Path.GetFileName(fPath)}"); - continue; + if (!IsCompatible(_loaderVer, BuildInfo.Version)) + { + string installString = _loaderVer.IsMinimum ? $"{_loaderVer.SemVer} or later" : $"{_loaderVer.SemVer} specifically"; + LoggerInstance.Warning($"{Path.GetFileName(fPath)} ({GetMelonInfo(fPath).Version}), a newly downloaded mod, is not compatible with the current version of MelonLoader ({BuildInfo.Version}), for it to work you need to install {installString}."); + LoggerInstance.Warning($"Not installing {Path.GetFileName(fPath)}"); + continue; + } } +#pragma warning disable CS0618 // Type or member is obsolete + string _path = Path.Combine(Path.Combine(MelonUtils.BaseDirectory, "Mods"), Path.GetFileName(fPath)); +#pragma warning restore CS0618 // Type or member is obsolete + if (!File.Exists(_path)) File.Move(fPath, _path); + else File.Replace(fPath, _path, Path.Combine(backupFolderPath, $"{Path.GetFileName(_path)}-{DateTimeOffset.Now.ToUnixTimeSeconds()}.dll")); + success += 1; + LoggerInstance.Msg("Successfully installed mod file " + Path.GetFileName(fPath)); } - string _path = Path.Combine(Path.Combine(MelonUtils.BaseDirectory, "Mods"), Path.GetFileName(fPath)); - if (!File.Exists(_path)) File.Move(fPath, _path); - else File.Replace(fPath, _path, Path.Combine(backupFolderPath, $"{Path.GetFileName(_path)}-{DateTimeOffset.Now.ToUnixTimeSeconds()}.dll")); - success += 1; - LoggerInstance.Msg("Successfully installed mod file " + Path.GetFileName(fPath)); - } - catch (Exception ex) - { - LoggerInstance.Error($"An unexpected error occurred while installing content\n{ex.Message}\n{ex.StackTrace}"); - failed += 1; - } - } - else if (_fileType == FileType.MelonPlugin) - { - try - { - LoggerInstance.Msg("Installing plugin file " + Path.GetFileName(fPath)); - var _loaderVer = GetLoaderVersionRequired(fPath); - if (_loaderVer != null) + catch (Exception ex) { - if (!IsCompatible(_loaderVer, BuildInfo.Version)) - { - string installString = _loaderVer.IsMinimum ? $"{_loaderVer.SemVer} or later" : $"{_loaderVer.SemVer} specifically"; - LoggerInstance.Warning($"{Path.GetFileName(fPath)} ({GetMelonInfo(fPath).Version}), a newly downloaded plugin, is not compatible with the current version of MelonLoader ({BuildInfo.Version}), for it to work you need to install {installString}."); - LoggerInstance.Warning($"Not installing {Path.GetFileName(fPath)}"); - continue; - } + LoggerInstance.Error($"An unexpected error occurred while installing content\n{ex.Message}\n{ex.StackTrace}"); + failed += 1; } - string pluginPath = Path.Combine(Path.Combine(MelonUtils.BaseDirectory, "Plugins"), fileName); - string _path = Path.Combine(Path.Combine(MelonUtils.BaseDirectory, "Plugins"), Path.GetFileName(fPath)); - if (!File.Exists(_path)) File.Move(fPath, _path); - else File.Replace(fPath, _path, Path.Combine(backupFolderPath, $"{Path.GetFileName(_path)}-{DateTimeOffset.Now.ToUnixTimeSeconds()}.dll")); - //var melonAssembly = MelonAssembly.LoadMelonAssembly(pluginPath); - LoggerInstance.Warning("WARNING: The plugin will only work after game restart"); - LoggerInstance.Msg("Successfully installed plugin file " + Path.GetFileName(fPath)); - success += 1; } - catch (Exception ex) - { - LoggerInstance.Error($"An unexpected error occurred while installing content\n{ex.Message}\n{ex.StackTrace}"); - failed += 1; - } - } - else - { - if (Path.GetDirectoryName(extPath) == "UserLibs") + else if (_fileType == FileType.MelonPlugin) { try { - LoggerInstance.Msg("Installing new library " + Path.GetFileName(fPath)); - string _path = Path.Combine(Path.Combine(MelonUtils.BaseDirectory, "UserLibs"), Path.GetFileName(fPath)); + LoggerInstance.Msg("Installing plugin file " + Path.GetFileName(fPath)); + var _loaderVer = GetLoaderVersionRequired(fPath); + if (_loaderVer != null) + { + if (!IsCompatible(_loaderVer, BuildInfo.Version)) + { + string installString = _loaderVer.IsMinimum ? $"{_loaderVer.SemVer} or later" : $"{_loaderVer.SemVer} specifically"; + LoggerInstance.Warning($"{Path.GetFileName(fPath)} ({GetMelonInfo(fPath).Version}), a newly downloaded plugin, is not compatible with the current version of MelonLoader ({BuildInfo.Version}), for it to work you need to install {installString}."); + LoggerInstance.Warning($"Not installing {Path.GetFileName(fPath)}"); + continue; + } + } +#pragma warning disable CS0618 // Type or member is obsolete + string pluginPath = Path.Combine(Path.Combine(MelonUtils.BaseDirectory, "Plugins"), fileName); + string _path = Path.Combine(Path.Combine(MelonUtils.BaseDirectory, "Plugins"), Path.GetFileName(fPath)); +#pragma warning restore CS0618 // Type or member is obsolete if (!File.Exists(_path)) File.Move(fPath, _path); else File.Replace(fPath, _path, Path.Combine(backupFolderPath, $"{Path.GetFileName(_path)}-{DateTimeOffset.Now.ToUnixTimeSeconds()}.dll")); + //var melonAssembly = MelonAssembly.LoadMelonAssembly(pluginPath); + LoggerInstance.Warning("WARNING: The plugin will only work after game restart"); + LoggerInstance.Msg("Successfully installed plugin file " + Path.GetFileName(fPath)); + success += 1; } catch (Exception ex) { - LoggerInstance.Msg($"An unexpected error occurred while installing library\n{ex.Message}\n{ex.StackTrace}"); + LoggerInstance.Error($"An unexpected error occurred while installing content\n{ex.Message}\n{ex.StackTrace}"); + failed += 1; } } else { - LoggerInstance.Msg($"Not extracting {Path.GetFileName(extPath)}, because it does not have the Melon Info Attribute"); + if (Path.GetDirectoryName(extPath) == "UserLibs") + { + try + { + LoggerInstance.Msg("Installing new library " + Path.GetFileName(fPath)); +#pragma warning disable CS0618 // Type or member is obsolete + string _path = Path.Combine(Path.Combine(MelonUtils.BaseDirectory, "UserLibs"), Path.GetFileName(fPath)); +#pragma warning restore CS0618 // Type or member is obsolete + if (!File.Exists(_path)) File.Move(fPath, _path); + else File.Replace(fPath, _path, Path.Combine(backupFolderPath, $"{Path.GetFileName(_path)}-{DateTimeOffset.Now.ToUnixTimeSeconds()}.dll")); + } + catch (Exception ex) + { + LoggerInstance.Msg($"An unexpected error occurred while installing library\n{ex.Message}\n{ex.StackTrace}"); + } + } + else + { + LoggerInstance.Msg($"Not extracting {Path.GetFileName(extPath)}, because it does not have the Melon Info Attribute"); + } } } } + else + { +#pragma warning disable CS0618 // Type or member is obsolete + ReplaceAllFiles(extPath, dirName == "MelonLoader" ? MelonUtils.MelonLoaderDirectory : dirName == "UserData" ? MelonUtils.UserDataDirectory : string.Empty); +#pragma warning restore CS0618 // Type or member is obsolete + } } else if (Path.GetExtension(extPath) == ".dll") { @@ -890,7 +922,9 @@ internal void CheckDirectory(string directory, bool automatic = true) continue; } } +#pragma warning disable CS0618 // Type or member is obsolete string _path = Path.Combine(Path.Combine(MelonUtils.BaseDirectory, "Mods"), Path.GetFileName(extPath)); +#pragma warning restore CS0618 // Type or member is obsolete if (!File.Exists(_path)) File.Move(extPath, _path); else File.Replace(extPath, _path, Path.Combine(backupFolderPath, $"{Path.GetFileName(path)}-{DateTimeOffset.Now.ToUnixTimeSeconds()}.dll")); success += 1; @@ -919,8 +953,10 @@ internal void CheckDirectory(string directory, bool automatic = true) continue; } } +#pragma warning disable CS0618 // Type or member is obsolete string pluginPath = Path.Combine(Path.Combine(MelonUtils.BaseDirectory, "Plugins"), fileName); string _path = Path.Combine(Path.Combine(MelonUtils.BaseDirectory, "Plugins"), Path.GetFileName(extPath)); +#pragma warning restore CS0618 // Type or member is obsolete if (!File.Exists(_path)) File.Move(extPath, _path); else File.Replace(extPath, _path, Path.Combine(backupFolderPath, $"{Path.GetFileName(path)}-{DateTimeOffset.Now.ToUnixTimeSeconds()}.dll")); //var melonAssembly = MelonAssembly.LoadMelonAssembly(pluginPath); @@ -964,7 +1000,9 @@ internal void CheckDirectory(string directory, bool automatic = true) continue; } } +#pragma warning disable CS0618 // Type or member is obsolete string _path = Path.Combine(Path.Combine(MelonUtils.BaseDirectory, "Mods"), Path.GetFileName(pathToSave)); +#pragma warning restore CS0618 // Type or member is obsolete if (!File.Exists(_path)) File.Move(pathToSave, _path); else File.Replace(pathToSave, _path, Path.Combine(backupFolderPath, $"{Path.GetFileName(path)}-{DateTimeOffset.Now.ToUnixTimeSeconds()}.dll")); success += 1; @@ -993,8 +1031,10 @@ internal void CheckDirectory(string directory, bool automatic = true) continue; } } +#pragma warning disable CS0618 // Type or member is obsolete string pluginPath = Path.Combine(Path.Combine(MelonUtils.BaseDirectory, "Plugins"), fileName); string _path = Path.Combine(Path.Combine(MelonUtils.BaseDirectory, "Plugins"), Path.GetFileName(pathToSave)); +#pragma warning restore CS0618 // Type or member is obsolete if (!File.Exists(_path)) File.Move(pathToSave, _path); else File.Replace(pathToSave, _path, Path.Combine(backupFolderPath, $"{Path.GetFileName(path)}-{DateTimeOffset.Now.ToUnixTimeSeconds()}.dll")); //var melonAssembly = MelonAssembly.LoadMelonAssembly(pluginPath); @@ -1057,7 +1097,9 @@ internal void CheckDirectory(string directory, bool automatic = true) public override void OnPreInitialization() { LoggerInstance.Msg("Creating folders in UserData"); +#pragma warning disable CS0618 // Type or member is obsolete DirectoryInfo mainDir = Directory.CreateDirectory(Path.Combine(Path.Combine(MelonUtils.BaseDirectory, "UserData"), "MelonAutoUpdater")); +#pragma warning restore CS0618 // Type or member is obsolete DirectoryInfo tempDir = mainDir.CreateSubdirectory("TemporaryFiles"); DirectoryInfo backupDir = mainDir.CreateSubdirectory("Backups"); @@ -1078,11 +1120,13 @@ public override void OnPreInitialization() SetupPreferences(); LoggerInstance.Msg("Checking plugins..."); +#pragma warning disable CS0618 // Type or member is obsolete CheckDirectory(Path.Combine(MelonUtils.BaseDirectory, "Plugins"), false); LoggerInstance.Msg("Done checking plugins"); LoggerInstance.Msg("Checking mods..."); CheckDirectory(Path.Combine(MelonUtils.BaseDirectory, "Mods")); +#pragma warning restore CS0618 // Type or member is obsolete LoggerInstance.Msg("Done checking mods"); } } diff --git a/MelonModUpdater/ModData.cs b/MelonModUpdater/ModData.cs index fd6540b..cc92e36 100644 --- a/MelonModUpdater/ModData.cs +++ b/MelonModUpdater/ModData.cs @@ -13,7 +13,7 @@ public class ModData /// /// The URLs & to download the latest version of a mod & Content Type if provided /// - public Dictionary DownloadFiles { get; internal set; } + public List DownloadFiles { get; internal set; } } public class ModVersion @@ -81,4 +81,22 @@ public enum FileType MelonPlugin = 2, Other = 3 } + + public class FileData + { + /// + /// URL to the file + /// + public string URL { get; internal set; } + + /// + /// Content Type returned by API + /// + public string ContentType { get; internal set; } + + /// + /// File Name, if provided with response + /// + public string FileName { get; internal set; } + } } \ No newline at end of file