added support for modpack defined in version descriptor

This commit is contained in:
2024-12-29 12:15:51 +05:00
parent d141ec23dc
commit 2c780afea8
15 changed files with 256 additions and 92 deletions

View File

@@ -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<AssetDownloadProperties> _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<bool> 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<AssetIndex>(indexFileText)
?? throw new Exception($"can't deserialize asset index file '{_indexFilePath}'");
var assetIndex = await ReadOrDownloadAndDeserialize<AssetIndex>(
_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);
}
}
}

View File

@@ -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<NetworkTask?> 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));
}
}
}
}

View File

@@ -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<Libraries.JarLib> _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)
{

View File

@@ -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<NetworkTask?> 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<IOPath> _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<NetworkTask?> CreateAsync(bool checkHashes)
{
if(_descriptor.modpack is null)
throw new ArgumentNullException(nameof(_descriptor.modpack));
_modpack = await NetworkHelper.ReadOrDownloadAndDeserialize<MyModpackV1>(
_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<ModpackFilesManifest>(
_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}'");
}
}

View File

@@ -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<NetworkTask?> 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}'");
}
}