Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions MinecraftLaunch.Base/Interfaces/IVerifiableDependency.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
namespace MinecraftLaunch.Base.Interfaces;
using MinecraftLaunch.Base.Models.SHA1;

namespace MinecraftLaunch.Base.Interfaces;

public interface IVerifiableDependency {
long? Size { get; }
string Sha1 { get; }
Sha1Data? Sha1 { get; }
}
8 changes: 7 additions & 1 deletion MinecraftLaunch.Base/MinecraftLaunch.Base.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,18 @@
<Nullable>disable</Nullable>
<PackageIcon>logo.png</PackageIcon>
<GenerateDocumentationFile>False</GenerateDocumentationFile>
<VSTestNoBuild>true</VSTestNoBuild>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="NbtToolkit" Version="0.1.2-beta" />
</ItemGroup>

<!--DEBUG时测试-->
<ItemGroup Condition="'$(Configuration)' == 'DEBUG'">
<PackageReference Include="xunit.v3.extensibility.core" Version="3.2.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageReference Include="xunit.v3.assert" Version="3.2.2" />
</ItemGroup>

<ItemGroup>
<None Include=".nuget\logo.png">
Expand Down
274 changes: 213 additions & 61 deletions MinecraftLaunch.Base/Models/Game/MinecraftEntry.cs

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions MinecraftLaunch.Base/Models/Game/MinecraftJsonEntry.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System.Text.Json;
using MinecraftLaunch.Base.Interfaces;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using MinecraftLaunch.Base.Models.SHA1;

namespace MinecraftLaunch.Base.Models.Game;

Expand All @@ -10,7 +10,7 @@ public class AssstIndex : MinecraftDependency, IDownloadDependency, IVerifiableD

[JsonPropertyName("id")] public string Id { get; set; }
[JsonPropertyName("url")] public string Url { get; set; }
[JsonPropertyName("sha1")] public string Sha1 { get; set; }
[JsonPropertyName("sha1")] public Sha1Data? Sha1 { get; set; }
[JsonPropertyName("size")] public long? Size { get; set; }

[JsonIgnore] public override string FilePath => Path.Combine("assets", "indexes", $"{Id}.json");
Expand All @@ -35,7 +35,7 @@ public record AssstIndexJsonEntry {
[JsonPropertyName("id")] public string Id { get; set; }
[JsonPropertyName("size")] public int Size { get; set; }
[JsonPropertyName("url")] public string Url { get; set; }
[JsonPropertyName("sha1")] public string Sha1 { get; set; }
[JsonPropertyName("sha1")] public Sha1Data Sha1 { get; set; }
[JsonPropertyName("totalSize")] public int TotalSize { get; set; }
}

Expand Down
3 changes: 2 additions & 1 deletion MinecraftLaunch.Base/Models/Network/CurseforgeResource.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using MinecraftLaunch.Base.Enums;
using MinecraftLaunch.Base.Interfaces;
using MinecraftLaunch.Base.Models.SHA1;

namespace MinecraftLaunch.Base.Models.Network;

Expand Down Expand Up @@ -32,7 +33,7 @@ public record CurseforgeResourceFile {
public required bool IsAvailable { get; init; }
public required bool IsServerPack { get; init; }

public required string Sha1 { get; init; }
public required Sha1Data? Sha1 { get; init; }
public required string FileName { get; init; }
public required string DisplayName { get; init; }
public required string DownloadUrl { get; init; }
Expand Down
4 changes: 3 additions & 1 deletion MinecraftLaunch.Base/Models/Network/FileHashes.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@

using MinecraftLaunch.Base.Models.SHA1;

namespace MinecraftLaunch.Base.Models.Network;

public record FileHashes
{
public required string Sha512 { get; init; }
public required string Sha1 { get; init; }
public required Sha1Data Sha1 { get; init; }
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Text.Json.Serialization;
using MinecraftLaunch.Base.Models.SHA1;

namespace MinecraftLaunch.Base.Models.Network;

Expand All @@ -20,7 +21,7 @@ public record McbbsModpackAddons {

public record McbbsModpackFileEntry {
[JsonPropertyName("path")] public string Path { get; set; }
[JsonPropertyName("hash")] public string Hash { get; set; }
[JsonPropertyName("hash")] public Sha1Data? Hash { get; set; }
[JsonPropertyName("type")] public string Type { get; set; }
[JsonPropertyName("force")] public bool IsForce { get; set; }
}
Expand Down
4 changes: 2 additions & 2 deletions MinecraftLaunch.Base/Models/Network/ModrinthResource.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using MinecraftLaunch.Base.Enums;
using MinecraftLaunch.Base.Interfaces;
using System;
using MinecraftLaunch.Base.Models.SHA1;

namespace MinecraftLaunch.Base.Models.Network;

Expand Down Expand Up @@ -31,7 +31,7 @@ public record ModrinthResourceFile {

public FileReleaseType ReleaseType { get; init; }

public required string Sha1 { get; init; }
public required Sha1Data Sha1 { get; init; }
public required string Sha512 { get; init; }
public required string FileName { get; init; }
public required string DownloadUrl { get; init; }
Expand Down
121 changes: 121 additions & 0 deletions MinecraftLaunch.Base/Models/SHA1/Sha1DataConvert.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
using System.Runtime.CompilerServices;
using System.Text.Json;
using System.Text.Json.Serialization;

Check notice on line 4 in MinecraftLaunch.Base/Models/SHA1/Sha1DataConvert.cs

View check run for this annotation

codefactor.io / CodeFactor

MinecraftLaunch.Base/Models/SHA1/Sha1DataConvert.cs#L4

Code should not contain multiple blank lines in a row. (SA1507)

namespace MinecraftLaunch.Base.Models.SHA1;

Check notice on line 7 in MinecraftLaunch.Base/Models/SHA1/Sha1DataConvert.cs

View check run for this annotation

codefactor.io / CodeFactor

MinecraftLaunch.Base/Models/SHA1/Sha1DataConvert.cs#L7

Code should not contain multiple blank lines in a row. (SA1507)

[JsonConverter(typeof(Sha1DataJsonConverter))]
[InlineArray(20)]
public partial struct Sha1Data : IEquatable<Sha1Data>
{
public bool Equals(Sha1Data other) => _data == other._data;

public override bool Equals(object obj) => obj is Sha1Data other && Equals(other);

public override int GetHashCode() => _data.GetHashCode();

private byte _data;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FormatTo(Span<char> destination)
{
for (var i = 0; i < 20; i++)
{
var b = this[i];
destination[i * 2] = ToHexCharLower(b >> 4);
destination[i * 2 + 1] = ToHexCharLower(b & 0x0F);

Check notice on line 28 in MinecraftLaunch.Base/Models/SHA1/Sha1DataConvert.cs

View check run for this annotation

codefactor.io / CodeFactor

MinecraftLaunch.Base/Models/SHA1/Sha1DataConvert.cs#L28

Arithmetic expressions should declare precedence. (SA1407)
}
return;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
static char ToHexCharLower(int b) => b switch
{
< 10 => (char)('0' + b),
_ => (char)('a' + (b - 10))
};
}
/// <summary>
/// 返回 40 位小写十六进制字符串。
/// </summary>
public override string ToString()
{
// 栈上分配 40 个 char,避免托管内存分配
Span<char> buffer = stackalloc char[40];
FormatTo(buffer);
return new string(buffer);
}

Check notice on line 49 in MinecraftLaunch.Base/Models/SHA1/Sha1DataConvert.cs

View check run for this annotation

codefactor.io / CodeFactor

MinecraftLaunch.Base/Models/SHA1/Sha1DataConvert.cs#L49

Code should not contain multiple blank lines in a row. (SA1507)


public sealed class Sha1DataJsonConverter : JsonConverter<Sha1Data>
{
public override Sha1Data Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (typeToConvert != typeof(Sha1Data))
{
ThrowHelper.ThrowInvalidTargetType();
}
if (reader.TokenType != JsonTokenType.String)
ThrowHelper.ThrowUnexpectedTokenType();

var utf8Hex = reader.ValueSpan;
if (utf8Hex.Length != 40)
ThrowHelper.ThrowInvalidLength(utf8Hex.Length);
Unsafe.SkipInit(out Sha1Data result);
for (var i = 0; i < 20; i++)
{
var idx = i * 2;
result[i] = ParseHex(utf8Hex[idx], utf8Hex[idx + 1]);
}
return result;
}

Check notice on line 74 in MinecraftLaunch.Base/Models/SHA1/Sha1DataConvert.cs

View check run for this annotation

codefactor.io / CodeFactor

MinecraftLaunch.Base/Models/SHA1/Sha1DataConvert.cs#L74

Code should not contain multiple blank lines in a row. (SA1507)

public override void Write(Utf8JsonWriter writer, Sha1Data value, JsonSerializerOptions options)
{
var buffer = (Span<byte>)stackalloc byte[40];
for (var i = 0; i < 20; i++)
{
var b = value[i];
buffer[i * 2] = ToHexCharLower(b >> 4);
buffer[i * 2 + 1] = ToHexCharLower(b & 0x0F);

Check notice on line 83 in MinecraftLaunch.Base/Models/SHA1/Sha1DataConvert.cs

View check run for this annotation

codefactor.io / CodeFactor

MinecraftLaunch.Base/Models/SHA1/Sha1DataConvert.cs#L83

Arithmetic expressions should declare precedence. (SA1407)
}
writer.WriteStringValue(buffer);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static byte ParseHex(byte c1, byte c2)
{
var h = (c1 & 0x0F) + ((c1 & 0x40) != 0 ? 9 : 0);
var l = (c2 & 0x0F) + ((c2 & 0x40) != 0 ? 9 : 0);
return (byte)((h << 4) | l);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static byte ToHexCharLower(int b) => b switch
{
< 10 => (byte)('0' + b),
_ => (byte)('a' + (b - 10))
};

private static class ThrowHelper
{
[MethodImpl(MethodImplOptions.NoInlining)]
public static void ThrowUnexpectedTokenType() => throw new JsonException("Unexpected token type for SHA1 data, expected a string.");
[MethodImpl(MethodImplOptions.NoInlining)]
public static void ThrowInvalidLength(int len) => throw new JsonException($"SHA1 hex string must be 40 characters long, got {len}.");
[MethodImpl(MethodImplOptions.NoInlining)]
public static void ThrowInvalidTargetType()
{
throw new JsonException("Invalid target type.");
}
}
}

[JsonSerializable(typeof(Sha1Data))]
[JsonSerializable(typeof(Sha1Data[]))]
public sealed partial class Sha1DataSerializerContext : JsonSerializerContext;
}

Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,8 @@ public async Task<MicrosoftAccount> RefreshAsync(MicrosoftAccount account, Cance
var result = await request.PostAsync(new FormUrlEncodedContent(payload),
cancellationToken: cancellationToken);

var json = await result.GetStringAsync();
var response = json.Deserialize(OAuth2TokenResponseContext.Default.OAuth2TokenResponse);

await using var json = await result.GetStreamAsync();
var response = await JsonSerializer.DeserializeAsync(json,OAuth2TokenResponseContext.Default.OAuth2TokenResponse, cancellationToken);
return await AuthenticateAsync(response, cancellationToken);
}

Expand Down Expand Up @@ -73,10 +72,10 @@ public async Task<OAuth2TokenResponse> DeviceFlowAuthAsync(Action<DeviceCodeResp
};

var request = HttpUtil.Request("https://login.microsoftonline.com/consumers/oauth2/v2.0/devicecode");
string json = await request.PostUrlEncodedAsync(parameters, cancellationToken: cancellationToken)
.ReceiveString();
await using var json = await request.PostUrlEncodedAsync(parameters, cancellationToken: cancellationToken)
.ReceiveStream();

var codeResponse = json.Deserialize(DeviceCodeResponseContext.Default.DeviceCodeResponse);
var codeResponse = await JsonSerializer.DeserializeAsync(json,DeviceCodeResponseContext.Default.DeviceCodeResponse, cancellationToken);
deviceCode.Invoke(codeResponse);

//Polling
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using MinecraftLaunch.Extensions;
using MinecraftLaunch.Utilities;
using System.Net.Http.Json;
using System.Text.Json;

namespace MinecraftLaunch.Components.Authenticator;

Expand Down Expand Up @@ -41,8 +42,8 @@ public async Task<YggdrasilAccount> RefreshAsync(YggdrasilAccount account, Cance
YggdrasilRequestPayloadContext.Default.YggdrasilRefreshPayload),
cancellationToken: cancellationToken);

var json = await responseMessage.GetStringAsync();
var entry = json.Deserialize(YggdrasilResponseContext.Default.YggdrasilResponse);
await using var json = await responseMessage.GetStreamAsync();
var entry = await JsonSerializer.DeserializeAsync(json,YggdrasilResponseContext.Default.YggdrasilResponse, cancellationToken);
var profile = entry.SelectedProfile;

return new YggdrasilAccount(profile.Name, Guid.Parse(profile.Id), entry.AccessToken, _url, entry.ClientToken);
Expand All @@ -65,8 +66,8 @@ public async Task<IEnumerable<YggdrasilAccount>> AuthenticateAsync(CancellationT
YggdrasilRequestPayloadContext.Default.YggdrasilAuthenticatePayload),
cancellationToken: cancellationToken);

var json = await responseMessage.GetStringAsync();
var entry = json.Deserialize(YggdrasilResponseContext.Default.YggdrasilResponse);
await using var json = await responseMessage.GetStreamAsync();
var entry = await JsonSerializer.DeserializeAsync(json,YggdrasilResponseContext.Default.YggdrasilResponse, cancellationToken);

return entry.AvailableProfiles.Select(profile =>
new YggdrasilAccount(profile.Name, Guid.Parse(profile.Id), entry.AccessToken, _url, entry.ClientToken));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,15 +111,10 @@ private static bool VerifyDependency(MinecraftDependency dep, CancellationToken

bool VerifySha1() {
using var fileStream = File.OpenRead(dep.FullPath);
byte[] sha1Bytes = SHA1.HashData(fileStream);

#if NET9_0_OR_GREATER
string sha1Str = Convert.ToHexStringLower(sha1Bytes);
#else
string sha1Str = BitConverter.ToString(sha1Bytes).Replace("-", string.Empty).ToLowerInvariant();
#endif

return sha1Str == verifiableDependency.Sha1;
var sha1Bytes = (Span<byte>)stackalloc byte[20];
SHA1.HashData(fileStream, sha1Bytes);
var sp = verifiableDependency.Sha1.Value;
return sha1Bytes.SequenceEqual(sp);
}

bool VerifySize() {
Expand Down
7 changes: 4 additions & 3 deletions MinecraftLaunch/Components/Installer/FabricInstaller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,11 @@ public static FabricInstaller Create(string mcFolder, FabricInstallEntry install
}

public static async Task<IEnumerable<FabricInstallEntry>> EnumerableFabricAsync(string mcVersion, CancellationToken cancellationToken = default) {
string json = await HttpUtil.FlurlClient.Request($"https://meta.fabricmc.net/v2/versions/loader/{mcVersion}")
.GetStringAsync(cancellationToken: cancellationToken);
await using var json = await HttpUtil.FlurlClient.Request($"https://meta.fabricmc.net/v2/versions/loader/{mcVersion}")
.GetStreamAsync(cancellationToken: cancellationToken);

var entries = json.Deserialize(FabricInstallEntryContext.Default.IEnumerableFabricInstallEntry)
var entries = (await JsonSerializer.DeserializeAsync(json,
FabricInstallEntryContext.Default.IEnumerableFabricInstallEntry, cancellationToken))
.OrderByDescending(x => new Version(x.Loader.Version.Replace(x.Loader.Separator, ".")));

return entries;
Expand Down
11 changes: 6 additions & 5 deletions MinecraftLaunch/Components/Installer/ForgeInstaller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ await WriteVersionJsonAndSomeDependenciesAsync(isLegacy, doc.RootElement, packag
{
ReportProgress(InstallStep.Interrupted, 1.0d, TaskStatus.Canceled, 1, 1);
ReportCompleted(false, ex);
throw;
}

return entry ?? throw new ArgumentNullException(nameof(entry), "Unexpected null reference to variable");
}

Expand All @@ -77,8 +77,9 @@ public static async Task<IEnumerable<ForgeInstallEntry>> EnumerableForgeAsync(st
? $"https://bmclapi2.bangbang93.com/neoforge/list/{mcVersion}"
: $"https://bmclapi2.bangbang93.com/forge/minecraft/{mcVersion}";

string json = await packagesUrl.GetStringAsync(cancellationToken: cancellationToken);
var entries = json.Deserialize(ForgeInstallEntryContext.Default.IEnumerableForgeInstallEntry)
await using var json = await packagesUrl.GetStreamAsync(cancellationToken: cancellationToken);
var entries = (await JsonSerializer.DeserializeAsync(json,
ForgeInstallEntryContext.Default.IEnumerableForgeInstallEntry, cancellationToken))
.OrderByDescending(entry => entry.Build);

foreach (var entry in entries)
Expand Down Expand Up @@ -130,9 +131,9 @@ private async Task<FileInfo> DownloadForgePackageAsync(CancellationToken cancell
var packageFile = new FileInfo(Path.Combine(MinecraftFolder, fileName));
var downloadRequest = new DownloadRequest(packageUrl, packageFile.FullName);

await new DefaultDownloader()
var downloadResult = await new DefaultDownloader()
.DownloadAsync(downloadRequest, cancellationToken);

if (downloadResult.Type is DownloadResultType.Failed) throw downloadResult.Exception;
ReportProgress(InstallStep.DownloadPackage, 0.45d, TaskStatus.Running, 1, 1);

return packageFile;
Expand Down
5 changes: 3 additions & 2 deletions MinecraftLaunch/Components/Installer/OptifineInstaller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ public static OptifineInstaller Create(string mcFolder, OptifineInstallEntry opt
public static async Task<IEnumerable<OptifineInstallEntry>> EnumerableOptifineAsync(string mcVersion, CancellationToken cancellationToken = default) {
string url = $"https://bmclapi2.bangbang93.com/optifine/{mcVersion}";

string json = await url.GetStringAsync(cancellationToken: cancellationToken);
var entries = json.Deserialize(OptifineInstallEntryContext.Default.IEnumerableOptifineInstallEntry)
await using var json = await url.GetStreamAsync(cancellationToken: cancellationToken);
var entries = (await JsonSerializer.DeserializeAsync(json,
OptifineInstallEntryContext.Default.IEnumerableOptifineInstallEntry, cancellationToken))
.OrderByDescending(entry => entry.Patch);

return entries;
Expand Down
6 changes: 3 additions & 3 deletions MinecraftLaunch/Components/Installer/QuiltInstaller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ public static QuiltInstaller Create(string mcFolder, QuiltInstallEntry installEn
}

public static async Task<IEnumerable<QuiltInstallEntry>> EnumerableQuiltAsync(string mcVersion, CancellationToken cancellationToken = default) {
string json = await $"https://meta.quiltmc.org/v3/versions/loader/{mcVersion}"
.GetStringAsync(cancellationToken: cancellationToken);
await using var json = await $"https://meta.quiltmc.org/v3/versions/loader/{mcVersion}"
.GetStreamAsync(cancellationToken: cancellationToken);

var entries = json.Deserialize(QuiltInstallEntryContext.Default.IEnumerableQuiltInstallEntry);
var entries = await JsonSerializer.DeserializeAsync(json,QuiltInstallEntryContext.Default.IEnumerableQuiltInstallEntry, cancellationToken);
return entries;
}

Expand Down
Loading