using System.Security.Cryptography; using DTLib.Extensions; using Млаумчерб.Клиент.видимое; using Млаумчерб.Клиент.классы; using static Млаумчерб.Клиент.сеть.Сеть; namespace Млаумчерб.Клиент.сеть.NetworkTaskFactories; public class AssetsDownloadTaskFactory : INetworkTaskFactory { private const string ASSET_SERVER_URL = "https://resources.download.minecraft.net/"; private VersionDescriptor _descriptor; private SHA1 _hasher; private IOPath _indexFilePath; List _assetsToDownload = new(); public AssetsDownloadTaskFactory(VersionDescriptor descriptor) { _descriptor = descriptor; _hasher = SHA1.Create(); _indexFilePath = Пути.GetAssetIndexFilePath(_descriptor.assetIndex.id); } public async Task CreateAsync(bool checkHashes) { if (!await CheckFilesAsync(checkHashes)) return new NetworkTask( $"assets '{_descriptor.assetIndex.id}'", GetTotalSize(), Download ); return null; } private async Task CheckFilesAsync(bool checkHashes) { if(!File.Exists(_indexFilePath)) { Приложение.Логгер.LogInfo(nameof(Сеть), $"started downloading asset index to '{_indexFilePath}'"); await DownloadFileHTTP(_descriptor.assetIndex.url, _indexFilePath); Приложение.Логгер.LogInfo(nameof(Сеть), "finished downloading asset index"); } string indexFileText = File.ReadAllText(_indexFilePath); var assetIndex = JsonConvert.DeserializeObject(indexFileText) ?? throw new Exception($"can't deserialize asset index file '{_indexFilePath}'"); _assetsToDownload.Clear(); // removing duplicates for Dictionary (idk how can it be possible, but Newtonsoft.Json creates them) HashSet assetHashes = new HashSet(); foreach (var pair in assetIndex.objects) { if (assetHashes.Add(pair.Value.hash)) { var a = new AssetDownloadProperties(pair.Key, pair.Value); if (!File.Exists(a.filePath)) { _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); } } } return _assetsToDownload.Count == 0; } private long GetTotalSize() { long totalSize = 0; foreach (var a in _assetsToDownload) totalSize += a.size; return totalSize; } private class AssetDownloadProperties { public string name; public string hash; public long size; public string url; public IOPath filePath; public AssetDownloadProperties(string key, AssetProperties p) { name = key; hash = p.hash; size = p.size; string hashStart = hash.Substring(0, 2); url = $"{ASSET_SERVER_URL}/{hashStart}/{hash}"; filePath = Path.Concat(IOPath.ArrayCast(["assets", "objects", hashStart, hash], true)); } } private async Task Download(NetworkProgressReporter pr, CancellationToken ct) { Приложение.Логгер.LogInfo(nameof(Сеть), "started downloading assets"); ParallelOptions opt = new() { MaxDegreeOfParallelism = ParallelDownloadsN, CancellationToken = ct }; await Parallel.ForEachAsync(_assetsToDownload, opt, async (a, _ct) => { Приложение.Логгер.LogDebug(nameof(Сеть), $"downloading asset '{a.name}' {a.hash}"); await DownloadFileHTTP(a.url, a.filePath, _ct, pr.AddBytesCount); }); Приложение.Логгер.LogInfo(nameof(Сеть), "finished downloading assets"); } }