From 2c780afea80ac717f429514fd6556865f92c2323 Mon Sep 17 00:00:00 2001 From: Timerix Date: Sun, 29 Dec 2024 12:15:51 +0500 Subject: [PATCH] added support for modpack defined in version descriptor --- Mlaumcherb.Client.Avalonia/GameVersion.cs | 19 ++-- .../зримое/MainWindow.axaml.cs | 2 +- .../классы/Буржуазия/GameVersionCatalog.cs | 2 +- .../классы/Буржуазия/GameVersionDescriptor.cs | 3 + .../классы/Пролетариат/FilesManifest.cs | 30 ++++++ .../классы/Пролетариат/GameVersionProps.cs | 15 ++- .../классы/Пролетариат/MyModpack.cs | 19 ++++ .../сеть/NetworkHelper.cs | 24 ++++- .../TaskFactories/AssetsDownloadTaskFactory.cs | 34 ++---- .../TaskFactories/JavaDownloadTaskFactory.cs | 16 +-- .../TaskFactories/LibrariesDownloadTaskFactory.cs | 17 +-- .../TaskFactories/ModpackDownloadTaskFactory.cs | 102 ++++++++++++++++++ .../VersionJarDownloadTaskFactory.cs | 27 ++--- .../холопы/HashHelper.cs | 32 ++++++ .../холопы/PathHelper.cs | 6 ++ 15 files changed, 256 insertions(+), 92 deletions(-) create mode 100644 Mlaumcherb.Client.Avalonia/классы/Пролетариат/FilesManifest.cs create mode 100644 Mlaumcherb.Client.Avalonia/классы/Пролетариат/MyModpack.cs create mode 100644 Mlaumcherb.Client.Avalonia/сеть/TaskFactories/ModpackDownloadTaskFactory.cs create mode 100644 Mlaumcherb.Client.Avalonia/холопы/HashHelper.cs diff --git a/Mlaumcherb.Client.Avalonia/GameVersion.cs b/Mlaumcherb.Client.Avalonia/GameVersion.cs index e684d4b..eded8b7 100644 --- a/Mlaumcherb.Client.Avalonia/GameVersion.cs +++ b/Mlaumcherb.Client.Avalonia/GameVersion.cs @@ -36,7 +36,7 @@ public class GameVersion foreach (IOPath subdir in Directory.GetDirectories(GetVersionsDir())) { string name = subdir.LastName().ToString(); - var d = new GameVersionProps(name, null); + var d = new GameVersionProps(name); if(!File.Exists(d.LocalDescriptorPath)) throw new Exception("Can't find version descriptor file in directory '{subdir}'. Rename it as directory name."); propsList.Add(d); @@ -47,14 +47,15 @@ public class GameVersion return Task.FromResult(propsList); } - public static async Task CreateFromPropsAsync(GameVersionProps props) + public static async Task CreateFromPropsAsync(GameVersionProps props, bool checkHash) { + //TODO: refresh version descriptor from server if (!File.Exists(props.LocalDescriptorPath)) { - if (props.RemoteDescriptorUrl is null) + if (props.RemoteProps is null) throw new NullReferenceException("can't download game version descriptor '" + props.Name + "', because RemoteDescriptorUrl is null"); - await NetworkHelper.DownloadFile(props.RemoteDescriptorUrl, props.LocalDescriptorPath); + await NetworkHelper.DownloadFile(props.RemoteProps.url, props.LocalDescriptorPath); } return new GameVersion(props); @@ -104,17 +105,17 @@ public class GameVersion [ new AssetsDownloadTaskFactory(_descriptor), new LibrariesDownloadTaskFactory(_descriptor, _libraries), - new VersionJarDownloadTaskFactory(_descriptor), ]; if (LauncherApp.Config.download_java) { taskFactories.Add(new JavaDownloadTaskFactory(_descriptor)); } - //TODO: modpack - /*if (modpack != null) + if (_descriptor.modpack != null) { - taskFactories.Add(new ModpackDownloadTaskFactory(modpack)); - }*/ + taskFactories.Add(new ModpackDownloadTaskFactory(_descriptor)); + } + // has to be downloaded last because it is used by GameVersionProps to check if version is installed + taskFactories.Add(new VersionJarDownloadTaskFactory(_descriptor)); var networkTasks = new List(); for (int i = 0; i < taskFactories.Count; i++) diff --git a/Mlaumcherb.Client.Avalonia/зримое/MainWindow.axaml.cs b/Mlaumcherb.Client.Avalonia/зримое/MainWindow.axaml.cs index 502c78d..cc1f343 100644 --- a/Mlaumcherb.Client.Avalonia/зримое/MainWindow.axaml.cs +++ b/Mlaumcherb.Client.Avalonia/зримое/MainWindow.axaml.cs @@ -134,7 +134,7 @@ public partial class MainWindow : Window if (selectedVersion == null) return; - var v = await GameVersion.CreateFromPropsAsync(selectedVersion); + var v = await GameVersion.CreateFromPropsAsync(selectedVersion, CheckGameFiles); await v.UpdateFiles(CheckGameFiles, nt => { Dispatcher.UIThread.Invoke(() => diff --git a/Mlaumcherb.Client.Avalonia/классы/Буржуазия/GameVersionCatalog.cs b/Mlaumcherb.Client.Avalonia/классы/Буржуазия/GameVersionCatalog.cs index 5c7869b..6a221db 100644 --- a/Mlaumcherb.Client.Avalonia/классы/Буржуазия/GameVersionCatalog.cs +++ b/Mlaumcherb.Client.Avalonia/классы/Буржуазия/GameVersionCatalog.cs @@ -26,7 +26,7 @@ public class GameVersionCatalog _ => p.OtherTypeAllowed }; if(match) - _versionPropsList.Add(new GameVersionProps(r.id, r.url)); + _versionPropsList.Add(new GameVersionProps(r)); } return _versionPropsList; } diff --git a/Mlaumcherb.Client.Avalonia/классы/Буржуазия/GameVersionDescriptor.cs b/Mlaumcherb.Client.Avalonia/классы/Буржуазия/GameVersionDescriptor.cs index c261e4a..90fde89 100644 --- a/Mlaumcherb.Client.Avalonia/классы/Буржуазия/GameVersionDescriptor.cs +++ b/Mlaumcherb.Client.Avalonia/классы/Буржуазия/GameVersionDescriptor.cs @@ -17,6 +17,9 @@ public class GameVersionDescriptor public JavaVersion javaVersion { get; set; } = new() { component = "jre-legacy", majorVersion = 8 }; public string? minecraftArguments { get; set; } public ArgumentsNew? arguments { get; set; } + + // my additions + public MyModpackRemoteProps? modpack { get; set; } } public class Artifact diff --git a/Mlaumcherb.Client.Avalonia/классы/Пролетариат/FilesManifest.cs b/Mlaumcherb.Client.Avalonia/классы/Пролетариат/FilesManifest.cs new file mode 100644 index 0000000..cffeddb --- /dev/null +++ b/Mlaumcherb.Client.Avalonia/классы/Пролетариат/FilesManifest.cs @@ -0,0 +1,30 @@ +using Mlaumcherb.Client.Avalonia.холопы; + +namespace Mlaumcherb.Client.Avalonia.классы; + +public class ModpackFilesManifest +{ + public class FileProps + { + [JsonRequired] public string sha1 { get; set; } + public bool allow_edit { get; set; } = false; + } + + /// relative_path, hash + [JsonRequired] public Dictionary files { get; set; } = new(); + + public bool CheckFiles(IOPath basedir, bool checkHashes, HashSet unmatchedFilesLocalPaths) + { + foreach (var p in files) + { + var localPath = new IOPath(p.Key); + var realPath = Path.Concat(basedir, localPath); + if (!HashHelper.CheckFileSHA1(realPath, p.Value.sha1, checkHashes && !p.Value.allow_edit)) + { + unmatchedFilesLocalPaths.Add(localPath); + } + } + + return unmatchedFilesLocalPaths.Count == 0; + } +} \ No newline at end of file diff --git a/Mlaumcherb.Client.Avalonia/классы/Пролетариат/GameVersionProps.cs b/Mlaumcherb.Client.Avalonia/классы/Пролетариат/GameVersionProps.cs index c360d5f..0c7cbb3 100644 --- a/Mlaumcherb.Client.Avalonia/классы/Пролетариат/GameVersionProps.cs +++ b/Mlaumcherb.Client.Avalonia/классы/Пролетариат/GameVersionProps.cs @@ -7,7 +7,7 @@ public class GameVersionProps : IComparable, IEquatable, IEquatable? StatusChanged; - public GameVersionProps(string name, string? url) + private GameVersionProps(string name, RemoteVersionDescriptorProps? remoteProps) { + Name = name; + RemoteProps = remoteProps; LocalDescriptorPath = PathHelper.GetVersionDescriptorPath(name); - RemoteDescriptorUrl = url; IsDownloaded = File.Exists(PathHelper.GetVersionJarFilePath(name)); } + + public GameVersionProps(string name) + : this(name, null) + { } + + public GameVersionProps(RemoteVersionDescriptorProps remoteProps) + : this(remoteProps.id, remoteProps) + { } public void DeleteFiles() { diff --git a/Mlaumcherb.Client.Avalonia/классы/Пролетариат/MyModpack.cs b/Mlaumcherb.Client.Avalonia/классы/Пролетариат/MyModpack.cs new file mode 100644 index 0000000..4ed7e54 --- /dev/null +++ b/Mlaumcherb.Client.Avalonia/классы/Пролетариат/MyModpack.cs @@ -0,0 +1,19 @@ +namespace Mlaumcherb.Client.Avalonia.классы; + +public class MyModpackRemoteProps +{ + [JsonRequired] public int format_version { get; set; } + [JsonRequired] public Artifact artifact { get; set; } = null!; +} + +public class MyModpackV1 +{ + + // 1 + [JsonRequired] public int format_version { get; set; } + [JsonRequired] public string name { get; set; } + // zip archive with all files + [JsonRequired] public Artifact zip { get; set; } = null!; + // ModpackFilesManifest + [JsonRequired] public Artifact manifest { get; set; } = null!; +} \ No newline at end of file diff --git a/Mlaumcherb.Client.Avalonia/сеть/NetworkHelper.cs b/Mlaumcherb.Client.Avalonia/сеть/NetworkHelper.cs index 32803a0..1ff5762 100644 --- a/Mlaumcherb.Client.Avalonia/сеть/NetworkHelper.cs +++ b/Mlaumcherb.Client.Avalonia/сеть/NetworkHelper.cs @@ -1,6 +1,5 @@ using System.Net.Http; -using Mlaumcherb.Client.Avalonia.зримое; -using Mlaumcherb.Client.Avalonia.классы; +using Mlaumcherb.Client.Avalonia.холопы; namespace Mlaumcherb.Client.Avalonia.сеть; @@ -41,4 +40,25 @@ public static class NetworkHelper ?? throw new Exception($"can't deserialize {typeof(T).Name}"); return result; } + + + public static async Task ReadOrDownload(IOPath filePath, string url, + string? sha1, bool checkHashes) + { + if (HashHelper.CheckFileSHA1(filePath, sha1, checkHashes)) + return File.ReadAllText(filePath); + + string txt = await NetworkHelper.GetString(url); + File.WriteAllText(filePath, txt); + return txt; + } + + public static async Task ReadOrDownloadAndDeserialize(IOPath filePath, string url, + string? sha1, bool checkHashes) + { + string text = await ReadOrDownload(filePath, url, sha1, checkHashes); + var result = JsonConvert.DeserializeObject(text) + ?? throw new Exception($"can't deserialize {typeof(T).Name}"); + return result; + } } \ No newline at end of file diff --git a/Mlaumcherb.Client.Avalonia/сеть/TaskFactories/AssetsDownloadTaskFactory.cs b/Mlaumcherb.Client.Avalonia/сеть/TaskFactories/AssetsDownloadTaskFactory.cs index aa512d8..1b5469d 100644 --- a/Mlaumcherb.Client.Avalonia/сеть/TaskFactories/AssetsDownloadTaskFactory.cs +++ b/Mlaumcherb.Client.Avalonia/сеть/TaskFactories/AssetsDownloadTaskFactory.cs @@ -1,7 +1,5 @@ using System.Net; using System.Net.Http; -using System.Security.Cryptography; -using DTLib.Extensions; using Mlaumcherb.Client.Avalonia.зримое; using Mlaumcherb.Client.Avalonia.классы; using Mlaumcherb.Client.Avalonia.холопы; @@ -13,14 +11,12 @@ public class AssetsDownloadTaskFactory : INetworkTaskFactory { private const string ASSET_SERVER_URL = "https://resources.download.minecraft.net/"; private GameVersionDescriptor _descriptor; - private SHA1 _hasher; private IOPath _indexFilePath; List _assetsToDownload = new(); public AssetsDownloadTaskFactory(GameVersionDescriptor descriptor) { _descriptor = descriptor; - _hasher = SHA1.Create(); _indexFilePath = PathHelper.GetAssetIndexFilePath(_descriptor.assetIndex.id); } @@ -37,22 +33,11 @@ public class AssetsDownloadTaskFactory : INetworkTaskFactory } private async Task CheckFilesAsync(bool checkHashes) { - string assetIndexHash = ""; - if(checkHashes) - { - await using var fs = File.OpenRead(_indexFilePath); - assetIndexHash = _hasher.ComputeHash(fs).HashToString(); - } - if(!File.Exists(_indexFilePath) || (checkHashes && assetIndexHash != _descriptor.assetIndex.sha1)) - { - LauncherApp.Logger.LogInfo(nameof(NetworkHelper), $"started downloading asset index to '{_indexFilePath}'"); - await DownloadFile(_descriptor.assetIndex.url, _indexFilePath); - LauncherApp.Logger.LogInfo(nameof(NetworkHelper), "finished downloading asset index"); - } - - string indexFileText = File.ReadAllText(_indexFilePath); - var assetIndex = JsonConvert.DeserializeObject(indexFileText) - ?? throw new Exception($"can't deserialize asset index file '{_indexFilePath}'"); + var assetIndex = await ReadOrDownloadAndDeserialize( + _indexFilePath, + _descriptor.assetIndex.url, + _descriptor.assetIndex.sha1, + checkHashes); _assetsToDownload.Clear(); // removing duplicates for Dictionary (idk how can it be possible, but Newtonsoft.Json creates them) @@ -62,17 +47,10 @@ public class AssetsDownloadTaskFactory : INetworkTaskFactory if (assetHashes.Add(pair.Value.hash)) { var a = new AssetDownloadProperties(pair.Key, pair.Value); - if (!File.Exists(a.filePath)) + if (!HashHelper.CheckFileSHA1(a.filePath, a.hash, checkHashes)) { _assetsToDownload.Add(a); } - else if(checkHashes) - { - await using var fs = File.OpenRead(a.filePath); - string hash = _hasher.ComputeHash(fs).HashToString(); - if (hash != a.hash) - _assetsToDownload.Add(a); - } } } diff --git a/Mlaumcherb.Client.Avalonia/сеть/TaskFactories/JavaDownloadTaskFactory.cs b/Mlaumcherb.Client.Avalonia/сеть/TaskFactories/JavaDownloadTaskFactory.cs index 0486480..5966ebd 100644 --- a/Mlaumcherb.Client.Avalonia/сеть/TaskFactories/JavaDownloadTaskFactory.cs +++ b/Mlaumcherb.Client.Avalonia/сеть/TaskFactories/JavaDownloadTaskFactory.cs @@ -1,6 +1,4 @@ -using System.Security.Cryptography; -using DTLib.Extensions; -using Mlaumcherb.Client.Avalonia.зримое; +using Mlaumcherb.Client.Avalonia.зримое; using Mlaumcherb.Client.Avalonia.классы; using Mlaumcherb.Client.Avalonia.холопы; using static Mlaumcherb.Client.Avalonia.сеть.NetworkHelper; @@ -13,7 +11,6 @@ public class JavaDownloadTaskFactory : INetworkTaskFactory "https://launchermeta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"; private GameVersionDescriptor _descriptor; private IOPath _javaVersionDir; - private SHA1 _hasher; private JavaDistributiveManifest? _distributiveManifest; private List<(IOPath path, JavaDistributiveElementProps props)> _filesToDownload = new(); @@ -21,7 +18,6 @@ public class JavaDownloadTaskFactory : INetworkTaskFactory { _descriptor = descriptor; _javaVersionDir = PathHelper.GetJavaRuntimeDir(_descriptor.javaVersion.component); - _hasher = SHA1.Create(); } public async Task CreateAsync(bool checkHashes) @@ -52,18 +48,10 @@ public class JavaDownloadTaskFactory : INetworkTaskFactory { var artifact = pair.Value.downloads; IOPath file_path = Path.Concat(_javaVersionDir, pair.Key); - if (!File.Exists(file_path)) + if (!HashHelper.CheckFileSHA1(file_path, artifact.raw.sha1, checkHashes)) { _filesToDownload.Add((file_path, pair.Value)); } - else if(checkHashes) - { - using var fs = File.OpenRead(file_path); - if (_hasher.ComputeHash(fs).HashToString() != artifact.raw.sha1) - { - _filesToDownload.Add((file_path, pair.Value)); - } - } } } diff --git a/Mlaumcherb.Client.Avalonia/сеть/TaskFactories/LibrariesDownloadTaskFactory.cs b/Mlaumcherb.Client.Avalonia/сеть/TaskFactories/LibrariesDownloadTaskFactory.cs index ba345c5..acf9e7a 100644 --- a/Mlaumcherb.Client.Avalonia/сеть/TaskFactories/LibrariesDownloadTaskFactory.cs +++ b/Mlaumcherb.Client.Avalonia/сеть/TaskFactories/LibrariesDownloadTaskFactory.cs @@ -1,6 +1,4 @@ using System.IO.Compression; -using System.Security.Cryptography; -using DTLib.Extensions; using Mlaumcherb.Client.Avalonia.зримое; using Mlaumcherb.Client.Avalonia.классы; using Mlaumcherb.Client.Avalonia.холопы; @@ -12,7 +10,6 @@ public class LibrariesDownloadTaskFactory : INetworkTaskFactory { private GameVersionDescriptor _descriptor; private Libraries _libraries; - private SHA1 _hasher; private List _libsToDownload = new(); private IOPath _nativesDir; @@ -20,7 +17,6 @@ public class LibrariesDownloadTaskFactory : INetworkTaskFactory { _descriptor = descriptor; _libraries = libraries; - _hasher = SHA1.Create(); _nativesDir = PathHelper.GetNativeLibrariesDir(descriptor.id); } @@ -43,21 +39,15 @@ public class LibrariesDownloadTaskFactory : INetworkTaskFactory foreach (var l in _libraries.Libs) { - if (!File.Exists(l.jarFilePath)) + if (!HashHelper.CheckFileSHA1(l.jarFilePath, l.artifact.sha1, checkHashes)) { _libsToDownload.Add(l); } + //TODO: replace with actual native assets check else if (!nativeDirExists && l is Libraries.NativeLib) { _libsToDownload.Add(l); } - else if (checkHashes) - { - using var fs = File.OpenRead(l.jarFilePath); - string hash = _hasher.ComputeHash(fs).HashToString(); - if(hash != l.artifact.sha1) - _libsToDownload.Add(l); - } } return _libsToDownload.Count == 0; @@ -88,7 +78,8 @@ public class LibrariesDownloadTaskFactory : INetworkTaskFactory await DownloadFile(l.artifact.url, l.jarFilePath, _ct, pr.AddBytesCount); if (l is Libraries.NativeLib n) { - var zipf = File.OpenRead(n.jarFilePath); + await using var zipf = File.OpenRead(n.jarFilePath); + //TODO: replace following code with manual extraction ZipFile.ExtractToDirectory(zipf, _nativesDir.ToString(), true); if (n.extractionOptions?.exclude != null) { diff --git a/Mlaumcherb.Client.Avalonia/сеть/TaskFactories/ModpackDownloadTaskFactory.cs b/Mlaumcherb.Client.Avalonia/сеть/TaskFactories/ModpackDownloadTaskFactory.cs new file mode 100644 index 0000000..4bba49b --- /dev/null +++ b/Mlaumcherb.Client.Avalonia/сеть/TaskFactories/ModpackDownloadTaskFactory.cs @@ -0,0 +1,102 @@ +using System.IO.Compression; +using Mlaumcherb.Client.Avalonia.зримое; +using Mlaumcherb.Client.Avalonia.классы; +using Mlaumcherb.Client.Avalonia.холопы; + +namespace Mlaumcherb.Client.Avalonia.сеть.TaskFactories; + +public class ModpackDownloadTaskFactory : INetworkTaskFactory +{ + INetworkTaskFactory _implementationVersion; + + public ModpackDownloadTaskFactory(GameVersionDescriptor descriptor) + { + if(descriptor.modpack is null) + throw new ArgumentNullException(nameof(descriptor.modpack)); + _implementationVersion = descriptor.modpack.format_version switch + { + 1 => new MyModpackV1DownloadTaskFactory(descriptor), + _ => throw new Exception($"Unknown Modpack format_version: {descriptor.modpack.format_version}") + }; + } + + public Task CreateAsync(bool checkHashes) + { + return _implementationVersion.CreateAsync(checkHashes); + } +} + +public class MyModpackV1DownloadTaskFactory : INetworkTaskFactory +{ + private IOPath _modpackManifesPath; + private readonly GameVersionDescriptor _descriptor; + private IOPath _modpackDescriptorPath; + private IOPath _versionDir; + private MyModpackV1? _modpack; + private ModpackFilesManifest? _modpackManifest; + private HashSet _filesToDosnload = new(); + + public MyModpackV1DownloadTaskFactory(GameVersionDescriptor descriptor) + { + _descriptor = descriptor; + _modpackDescriptorPath = PathHelper.GetModpackDescriptorPath(_descriptor.id); + _modpackManifesPath = PathHelper.GetModpackManifestPath(_descriptor.id); + _versionDir = PathHelper.GetVersionDir(_descriptor.id); + } + + public async Task CreateAsync(bool checkHashes) + { + if(_descriptor.modpack is null) + throw new ArgumentNullException(nameof(_descriptor.modpack)); + + + _modpack = await NetworkHelper.ReadOrDownloadAndDeserialize( + _modpackDescriptorPath, + _descriptor.modpack.artifact.url, + _descriptor.modpack.artifact.sha1, + checkHashes); + if (_modpack.format_version != _descriptor.modpack.format_version) + throw new Exception($"Modpack.format_version mismatches descriptor.modpack.version: " + + $"{_modpack.format_version} != {_descriptor.modpack.format_version}"); + + _modpackManifest = await NetworkHelper.ReadOrDownloadAndDeserialize( + _modpackManifesPath, + _modpack.manifest.url, + _modpack.manifest.sha1, + checkHashes); + + if(!_modpackManifest.CheckFiles(_versionDir, checkHashes, _filesToDosnload)) + return new NetworkTask( + $"modpack '{_descriptor.assetIndex.id}'", + _modpack.zip.size, + Download + ); + + return null; + } + + private async Task Download(NetworkProgressReporter pr, CancellationToken ct) + { + LauncherApp.Logger.LogInfo(nameof(NetworkHelper), $"started downloading modpack '{_modpack!.name}'"); + if(string.IsNullOrEmpty(_modpack.zip.url)) + throw new Exception($"modpack '{_modpack.name}' doesn't have a url to download"); + + var _archivePath = Path.Concat("downloads/modpacks", _modpack.name + ".zip"); + await NetworkHelper.DownloadFile(_modpack.zip.url, _archivePath, ct, pr.AddBytesCount); + + await using var zipf = File.OpenRead(_archivePath); + using var archive = new ZipArchive(zipf); + foreach (var entry in archive.Entries) + { + IOPath localPath = new(entry.FullName); + if(_filesToDosnload.Contains(localPath)) + { + var real_path = Path.Concat(_versionDir, localPath); + Directory.Create(real_path.ParentDir()); + entry.ExtractToFile(real_path.ToString(), true); + } + } + + LauncherApp.Logger.LogInfo(nameof(NetworkHelper), $"finished downloading modpack '{_modpack.name}'"); + } +} \ No newline at end of file diff --git a/Mlaumcherb.Client.Avalonia/сеть/TaskFactories/VersionJarDownloadTaskFactory.cs b/Mlaumcherb.Client.Avalonia/сеть/TaskFactories/VersionJarDownloadTaskFactory.cs index bf614a8..ede6492 100644 --- a/Mlaumcherb.Client.Avalonia/сеть/TaskFactories/VersionJarDownloadTaskFactory.cs +++ b/Mlaumcherb.Client.Avalonia/сеть/TaskFactories/VersionJarDownloadTaskFactory.cs @@ -1,6 +1,4 @@ -using System.Security.Cryptography; -using DTLib.Extensions; -using Mlaumcherb.Client.Avalonia.зримое; +using Mlaumcherb.Client.Avalonia.зримое; using Mlaumcherb.Client.Avalonia.классы; using Mlaumcherb.Client.Avalonia.холопы; using static Mlaumcherb.Client.Avalonia.сеть.NetworkHelper; @@ -11,13 +9,11 @@ public class VersionJarDownloadTaskFactory : INetworkTaskFactory { private GameVersionDescriptor _descriptor; private IOPath _filePath; - private SHA1 _hasher; public VersionJarDownloadTaskFactory(GameVersionDescriptor descriptor) { _descriptor = descriptor; _filePath = PathHelper.GetVersionJarFilePath(_descriptor.id); - _hasher = SHA1.Create(); } public Task CreateAsync(bool checkHashes) @@ -25,7 +21,7 @@ public class VersionJarDownloadTaskFactory : INetworkTaskFactory NetworkTask? networkTask = null; if (!CheckFiles(checkHashes)) networkTask = new NetworkTask( - $"version jar '{_descriptor.id}'", + $"game version jar '{_descriptor.id}'", GetTotalSize(), Download ); @@ -34,18 +30,7 @@ public class VersionJarDownloadTaskFactory : INetworkTaskFactory private bool CheckFiles(bool checkHashes) { - if (!File.Exists(_filePath)) - return false; - - if (!checkHashes) - return true; - - if (_descriptor.downloads is null) - return true; - - using var fs = File.OpenRead(_filePath); - string hash = _hasher.ComputeHash(fs).HashToString(); - return hash == _descriptor.downloads.client.sha1; + return HashHelper.CheckFileSHA1(_filePath, _descriptor.downloads?.client.sha1, checkHashes); } private long GetTotalSize() @@ -58,10 +43,10 @@ public class VersionJarDownloadTaskFactory : INetworkTaskFactory private async Task Download(NetworkProgressReporter pr, CancellationToken ct) { if (_descriptor.downloads is null) - throw new Exception($"can't download version jar '{_descriptor.id}' because it has no download url"); + throw new Exception($"can't download game version jar '{_descriptor.id}' because it has no download url"); - LauncherApp.Logger.LogInfo(nameof(NetworkHelper), $"started downloading version jar '{_descriptor.id}'"); + LauncherApp.Logger.LogInfo(nameof(NetworkHelper), $"started downloading game version jar '{_descriptor.id}'"); await DownloadFile(_descriptor.downloads.client.url, _filePath, ct, pr.AddBytesCount); - LauncherApp.Logger.LogInfo(nameof(NetworkHelper), $"finished downloading version jar '{_descriptor.id}'"); + LauncherApp.Logger.LogInfo(nameof(NetworkHelper), $"finished downloading game version jar '{_descriptor.id}'"); } } \ No newline at end of file diff --git a/Mlaumcherb.Client.Avalonia/холопы/HashHelper.cs b/Mlaumcherb.Client.Avalonia/холопы/HashHelper.cs new file mode 100644 index 0000000..b01c08a --- /dev/null +++ b/Mlaumcherb.Client.Avalonia/холопы/HashHelper.cs @@ -0,0 +1,32 @@ +using System.Security.Cryptography; +using DTLib.Extensions; + +namespace Mlaumcherb.Client.Avalonia.холопы; + +public static class HashHelper +{ + private static SHA1 _hasher; + + static HashHelper() + { + _hasher = SHA1.Create(); + } + + public static string HashFileSHA1(IOPath f) + { + using var fs = File.OpenRead(f); + byte[] hash = _hasher.ComputeHash(fs); + return hash.HashToString(); + } + + public static bool CheckFileSHA1(IOPath f, string? sha1, bool checkHash) + { + if (!File.Exists(f)) + return false; + + if(checkHash) + return sha1 is not null && HashFileSHA1(f) == sha1; + + return true; + } +} \ No newline at end of file diff --git a/Mlaumcherb.Client.Avalonia/холопы/PathHelper.cs b/Mlaumcherb.Client.Avalonia/холопы/PathHelper.cs index c6f5dac..6ce73a1 100644 --- a/Mlaumcherb.Client.Avalonia/холопы/PathHelper.cs +++ b/Mlaumcherb.Client.Avalonia/холопы/PathHelper.cs @@ -54,4 +54,10 @@ public static class PathHelper public static IOPath GetLog4jConfigFilePath() => Path.Concat(GetAssetsDir(), GetLog4jConfigFileName()); + + public static IOPath GetModpackDescriptorPath(string game_version) => + Path.Concat(GetVersionDir(game_version), "timerix_modpack.json"); + + public static IOPath GetModpackManifestPath(string game_version) => + Path.Concat(GetVersionDir(game_version), "timerix_modpack_files.json"); }