added support for modpack defined in version descriptor
This commit is contained in:
@@ -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<string> 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<T> ReadOrDownloadAndDeserialize<T>(IOPath filePath, string url,
|
||||
string? sha1, bool checkHashes)
|
||||
{
|
||||
string text = await ReadOrDownload(filePath, url, sha1, checkHashes);
|
||||
var result = JsonConvert.DeserializeObject<T>(text)
|
||||
?? throw new Exception($"can't deserialize {typeof(T).Name}");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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}'");
|
||||
}
|
||||
}
|
||||
@@ -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}'");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user