mlaumcherb/Mlaumcherb.Client.Avalonia/сеть/TaskFactories/AssetsDownloadTaskFactory.cs
2024-12-31 20:03:31 +05:00

123 lines
4.3 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Net;
using System.Net.Http;
using Mlaumcherb.Client.Avalonia.зримое;
using Mlaumcherb.Client.Avalonia.классы;
using Mlaumcherb.Client.Avalonia.холопы;
using static Mlaumcherb.Client.Avalonia.сеть.NetworkHelper;
namespace Mlaumcherb.Client.Avalonia.сеть.TaskFactories;
public class AssetsDownloadTaskFactory : INetworkTaskFactory
{
private const string ASSET_SERVER_URL = "https://resources.download.minecraft.net/";
private GameVersionDescriptor _descriptor;
private IOPath _indexFilePath;
List<AssetDownloadProperties> _assetsToDownload = new();
public AssetsDownloadTaskFactory(GameVersionDescriptor descriptor)
{
_descriptor = descriptor;
_indexFilePath = PathHelper.GetAssetIndexFilePath(_descriptor.assetIndex.id);
}
public async Task<NetworkTask?> CreateAsync(bool checkHashes)
{
NetworkTask? networkTask = null;
if (!await CheckFilesAsync(checkHashes))
{
networkTask = new NetworkTask(
$"assets '{_descriptor.assetIndex.id}'",
GetTotalSize(),
Download
);
}
return networkTask;
}
private async Task<bool> CheckFilesAsync(bool checkHashes)
{
(AssetIndex assetIndex, _) = await ReadOrDownloadAndDeserialize<AssetIndex>(
_indexFilePath,
_descriptor.assetIndex.url,
_descriptor.assetIndex.sha1,
checkHashes);
// removing duplicates for Dictionary (idk how can it be possible, but Newtonsoft.Json creates them)
HashSet<string> assetHashes = new HashSet<string>();
foreach (var pair in assetIndex.objects)
{
if (assetHashes.Add(pair.Value.hash))
{
var a = new AssetDownloadProperties(pair.Key, pair.Value);
if (!HashHelper.CheckFileSHA1(a.filePath, a.hash, checkHashes))
{
_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)
{
LauncherApp.Logger.LogInfo(nameof(NetworkHelper), $"started downloading assets '{_descriptor.assetIndex.id}'");
ParallelOptions opt = new()
{
MaxDegreeOfParallelism = LauncherApp.Config.max_parallel_downloads,
CancellationToken = ct
};
await Parallel.ForEachAsync(_assetsToDownload, opt,
async (a, _ct) =>
{
bool completed = false;
while(!completed)
{
LauncherApp.Logger.LogDebug(nameof(NetworkHelper), $"downloading asset '{a.name}' {a.hash}");
try
{
await DownloadFile(a.url, a.filePath, _ct, pr.AddBytesCount);
completed = true;
}
catch (HttpRequestException httpException)
{
// wait on rate limit
if(httpException.StatusCode == HttpStatusCode.TooManyRequests)
{
LauncherApp.Logger.LogDebug(nameof(NetworkHelper), "rate limit hit");
await Task.Delay(1000, _ct);
}
else throw;
}
}
});
LauncherApp.Logger.LogInfo(nameof(NetworkHelper), $"finished downloading assets '{_descriptor.assetIndex.id}'");
}
}