using System.Security.Cryptography; using DTLib.Extensions; using Млаумчерб.Клиент.зримое; using Млаумчерб.Клиент.классы; using static Млаумчерб.Клиент.сеть.Сеть; namespace Млаумчерб.Клиент.сеть.NetworkTaskFactories; public class JavaDownloadTaskFactory : INetworkTaskFactory { private const string CATALOG_URL = "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(); public JavaDownloadTaskFactory(GameVersionDescriptor descriptor) { _descriptor = descriptor; _javaVersionDir = Пути.GetJavaRuntimeDir(_descriptor.javaVersion.component); _hasher = SHA1.Create(); } public async Task CreateAsync(bool checkHashes) { var catalog = await DownloadStringAndDeserialize(CATALOG_URL); var versionProps = catalog.GetVersionProps(_descriptor.javaVersion); _distributiveManifest = await DownloadStringAndDeserialize(versionProps.manifest.url); NetworkTask? networkTask = null; if (!CheckFiles(checkHashes)) networkTask = new( $"java runtime '{_descriptor.javaVersion.component}'", GetTotalSize(), Download ); return networkTask; } private bool CheckFiles(bool checkHashes) { _filesToDownload.Clear(); foreach (var pair in _distributiveManifest!.files) { if (pair.Value.type != "file") continue; if (pair.Value.downloads != null) { var artifact = pair.Value.downloads; IOPath file_path = Path.Concat(_javaVersionDir, pair.Key); if (!File.Exists(file_path)) { _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)); } } } } return _filesToDownload.Count == 0; } private long GetTotalSize() { long totalSize = 0; foreach (var file in _filesToDownload) { if(file.props.downloads == null) continue; totalSize += file.props.downloads.lzma?.size ?? file.props.downloads.raw.size; } return totalSize; } private async Task Download(NetworkProgressReporter pr, CancellationToken ct) { Приложение.Логгер.LogInfo(nameof(Сеть), "started downloading java runtime " + $"{_descriptor.javaVersion.majorVersion} '{_descriptor.javaVersion.component}'"); ParallelOptions opt = new() { MaxDegreeOfParallelism = Приложение.Настройки.максимум_параллельных_загрузок, CancellationToken = ct }; await Parallel.ForEachAsync(_filesToDownload, opt, async (f, _ct) => { if (f.props.downloads!.lzma != null) { Приложение.Логгер.LogDebug(nameof(Сеть), $"downloading lzma-compressed file '{f.path}'"); await using var pipe = new TransformStream(await GetStream(f.props.downloads.lzma.url, _ct)); pipe.AddTransform(pr.AddBytesCount); await using var fs = File.OpenWrite(f.path); LZMACompressor.Decompress(pipe, fs); } else { Приложение.Логгер.LogDebug(nameof(Сеть), $"downloading raw file '{f.path}'"); await DownloadFile(f.props.downloads.raw.url, f.path, _ct, pr.AddBytesCount); } if(!OperatingSystem.IsWindows() && f.props.executable is true) { Приложение.Логгер.LogDebug(nameof(Сеть), $"adding execute rights to file '{f.path}'"); System.IO.File.SetUnixFileMode(f.path.ToString(), UnixFileMode.UserExecute); } }); Приложение.Логгер.LogInfo(nameof(Сеть), "finished downloading java runtime " + $"{_descriptor.javaVersion.majorVersion} '{_descriptor.javaVersion.component}'"); } }