use ref-like keyword to accelerate runtime speed

This commit is contained in:
Lightczx
2023-04-18 18:54:43 +08:00
parent dc38def97c
commit 89fe93b3eb
39 changed files with 469 additions and 758 deletions

View File

@@ -0,0 +1,175 @@
using Microsoft.CodeAnalysis;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
namespace Snap.Hutao.SourceGeneration.Identity;
[Generator(LanguageNames.CSharp)]
internal sealed class IdentityGenerator : IIncrementalGenerator
{
private const string FileName = "IdentityStructs.json";
public void Initialize(IncrementalGeneratorInitializationContext context)
{
IncrementalValueProvider<ImmutableArray<AdditionalText>> provider = context.AdditionalTextsProvider.Where(MatchFileName).Collect();
context.RegisterImplementationSourceOutput(provider, GenerateIdentityStructs);
}
private static bool MatchFileName(AdditionalText text)
{
return Path.GetFileName(text.Path) == FileName;
}
private static void GenerateIdentityStructs(SourceProductionContext context, ImmutableArray<AdditionalText> texts)
{
AdditionalText jsonFile = texts.Single();
string identityJson = jsonFile.GetText(context.CancellationToken)!.ToString();
List<IdentityStructMetadata> identities = identityJson.FromJson<List<IdentityStructMetadata>>()!;
if (identities.Any())
{
GenerateIdentityConverter(context);
foreach (IdentityStructMetadata identityStruct in identities)
{
GenerateIdentityStruct(context, identityStruct);
}
}
}
private static void GenerateIdentityConverter(SourceProductionContext context)
{
string source = $$"""
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core.ExpressionService;
namespace Snap.Hutao.Model.Primitive.Converter;
/// <summary>
/// Id 转换器
/// </summary>
/// <typeparam name="TWrapper">包装类型</typeparam>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{nameof(IdentityGenerator)}}","1.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
internal sealed class IdentityConverter<TWrapper> : JsonConverter<TWrapper>
where TWrapper : struct
{
/// <inheritdoc/>
public override TWrapper Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return CastTo<TWrapper>.From(reader.GetInt32());
}
/// <inheritdoc/>
public override void Write(Utf8JsonWriter writer, TWrapper value, JsonSerializerOptions options)
{
writer.WriteNumberValue(CastTo<int>.From(value));
}
}
""";
context.AddSource("IdentityConverter.g.cs", source);
}
private static void GenerateIdentityStruct(SourceProductionContext context, IdentityStructMetadata identityStruct)
{
string name = identityStruct.Name;
string type = identityStruct.Type;
string source = $$"""
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Primitive.Converter;
namespace Snap.Hutao.Model.Primitive;
/// <summary>
/// {{identityStruct.Documentation}}
/// </summary>
[JsonConverter(typeof(IdentityConverter<{{name}}>))]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{nameof(IdentityGenerator)}}","1.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
internal readonly struct {{name}} : IEquatable<{{name}}>
{
/// <summary>
/// 值
/// </summary>
public readonly {{type}} Value;
/// <summary>
/// Initializes a new instance of the <see cref="{{name}}"/> struct.
/// </summary>
/// <param name="value">value</param>
public {{name}}({{type}} value)
{
Value = value;
}
public static implicit operator {{type}}({{name}} value)
{
return value.Value;
}
public static implicit operator {{name}}({{type}} value)
{
return new(value);
}
public static bool operator ==({{name}} left, {{name}} right)
{
return left.Value == right.Value;
}
public static bool operator !=({{name}} left, {{name}} right)
{
return !(left == right);
}
/// <inheritdoc/>
public bool Equals({{name}} other)
{
return Value == other.Value;
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj is {{name}} other && Equals(other);
}
/// <inheritdoc/>
public override int GetHashCode()
{
return Value.GetHashCode();
}
}
""";
context.AddSource($"{name}.g.cs", source);
}
private sealed class IdentityStructMetadata
{
/// <summary>
/// 名称
/// </summary>
public string Name { get; set; } = default!;
/// <summary>
/// 基底类型
/// </summary>
public string Type { get; set; } = default!;
/// <summary>
/// 文档
/// </summary>
public string? Documentation { get; set; }
}
}

View File

@@ -2,7 +2,6 @@
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Text;

View File

@@ -6,7 +6,7 @@ using System.Linq;
using System.Runtime.Serialization;
using System.Text;
namespace Snap.Hutao.SourceGeneration;
namespace Snap.Hutao.SourceGeneration.Reliquary;
[Generator(LanguageNames.CSharp)]
internal sealed class ReliquaryWeightConfigurationGenerator : IIncrementalGenerator
@@ -25,13 +25,13 @@ internal sealed class ReliquaryWeightConfigurationGenerator : IIncrementalGenera
return Path.GetFileName(text.Path) == FileName;
}
private static void GenerateReliquaryWeightConfiguration(SourceProductionContext context,ImmutableArray<AdditionalText> additionalTexts)
private static void GenerateReliquaryWeightConfiguration(SourceProductionContext context, ImmutableArray<AdditionalText> texts)
{
AdditionalText jsonFile = additionalTexts.Single();
AdditionalText jsonFile = texts.Single();
string configurationJson = jsonFile.GetText(context.CancellationToken)!.ToString();
Dictionary<string, ReliquaryWeightConfigurationMetadata> metadataMap =
JsonParser.FromJson<Dictionary<string, ReliquaryWeightConfigurationMetadata>>(configurationJson)!;
configurationJson.FromJson<Dictionary<string, ReliquaryWeightConfigurationMetadata>>()!;
StringBuilder sourceBuilder = new StringBuilder().Append($$"""
// Copyright (c) DGP Studio. All rights reserved.

View File

@@ -1,70 +0,0 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using System.Collections.Immutable;
namespace Snap.Hutao.SourceGeneration;
/// <summary>
/// 类型应为内部分析器
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal sealed class TypeInternalAnalyzer : DiagnosticAnalyzer
{
private static readonly DiagnosticDescriptor typeInternalDescriptor = new("SH001", "Type should be internal", "Type should be internal", "Quality", DiagnosticSeverity.Info, true);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(typeInternalDescriptor);
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterCompilationStartAction(CompilationStart);
}
private void CompilationStart(CompilationStartAnalysisContext context)
{
context.RegisterSyntaxNodeAction(HandleSyntax<ClassDeclarationSyntax>, SyntaxKind.ClassDeclaration);
context.RegisterSyntaxNodeAction(HandleSyntax<InterfaceDeclarationSyntax>, SyntaxKind.InterfaceDeclaration);
context.RegisterSyntaxNodeAction(HandleSyntax<StructDeclarationSyntax>, SyntaxKind.StructDeclaration);
context.RegisterSyntaxNodeAction(HandleSyntax<EnumDeclarationSyntax>, SyntaxKind.EnumDeclaration);
}
private void HandleSyntax<TSyntax>(SyntaxNodeAnalysisContext classSyntax)
where TSyntax : BaseTypeDeclarationSyntax
{
TSyntax syntax = (TSyntax)classSyntax.Node;
bool privateExists = false;
bool internalExists = false;
bool fileExists = false;
foreach(SyntaxToken token in syntax.Modifiers)
{
if (token.IsKind(SyntaxKind.PrivateKeyword))
{
privateExists = true;
}
if (token.IsKind(SyntaxKind.InternalKeyword))
{
internalExists = true;
}
if (token.IsKind(SyntaxKind.FileKeyword))
{
fileExists = true;
}
}
if (!privateExists && !internalExists && !fileExists)
{
Location location = syntax.Identifier.GetLocation();
Diagnostic diagnostic = Diagnostic.Create(typeInternalDescriptor, location);
classSyntax.ReportDiagnostic(diagnostic);
}
}
}

View File

@@ -0,0 +1,134 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
namespace Snap.Hutao.SourceGeneration;
/// <summary>
/// 通用分析器
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal sealed class UniversalAnalyzer : DiagnosticAnalyzer
{
private static readonly DiagnosticDescriptor typeInternalDescriptor = new("SH001", "Type should be internal", "Type [{0}] should be internal", "Quality", DiagnosticSeverity.Info, true);
private static readonly DiagnosticDescriptor readOnlyStructRefDescriptor = new("SH002", "ReadOnly struct should be passed with ref-like key word", "ReadOnly Struct [{0}] should be passed with ref-like key word", "Quality", DiagnosticSeverity.Info, true);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
{
get
{
return new DiagnosticDescriptor[]
{
typeInternalDescriptor,
readOnlyStructRefDescriptor,
}.ToImmutableArray();
}
}
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterCompilationStartAction(CompilationStart);
}
private void CompilationStart(CompilationStartAnalysisContext context)
{
SyntaxKind[] types = new SyntaxKind[]
{
SyntaxKind.ClassDeclaration,
SyntaxKind.InterfaceDeclaration,
SyntaxKind.StructDeclaration,
SyntaxKind.EnumDeclaration
};
context.RegisterSyntaxNodeAction(HandleTypeDeclaration, types);
context.RegisterSyntaxNodeAction(CollectReadOnlyStruct, SyntaxKind.StructDeclaration);
context.RegisterSyntaxNodeAction(HandleMethodDeclaration, SyntaxKind.MethodDeclaration);
}
private void HandleTypeDeclaration(SyntaxNodeAnalysisContext context)
{
BaseTypeDeclarationSyntax syntax = (BaseTypeDeclarationSyntax)context.Node;
bool privateExists = false;
bool internalExists = false;
bool fileExists = false;
foreach (SyntaxToken token in syntax.Modifiers)
{
if (token.IsKind(SyntaxKind.PrivateKeyword))
{
privateExists = true;
}
if (token.IsKind(SyntaxKind.InternalKeyword))
{
internalExists = true;
}
if (token.IsKind(SyntaxKind.FileKeyword))
{
fileExists = true;
}
}
if (!privateExists && !internalExists && !fileExists)
{
Location location = syntax.Identifier.GetLocation();
Diagnostic diagnostic = Diagnostic.Create(typeInternalDescriptor, location, syntax.Identifier);
context.ReportDiagnostic(diagnostic);
}
}
private readonly HashSet<string> readOnlyStructs = new();
private void CollectReadOnlyStruct(SyntaxNodeAnalysisContext context)
{
StructDeclarationSyntax structSyntax = (StructDeclarationSyntax)context.Node;
if (structSyntax.Modifiers.Any(token => token.IsKind(SyntaxKind.ReadOnlyKeyword)))
{
if (context.SemanticModel.GetDeclaredSymbol(structSyntax) is INamedTypeSymbol symbol)
{
readOnlyStructs.Add(symbol.ToDisplayString());
}
}
}
private void HandleMethodDeclaration(SyntaxNodeAnalysisContext context)
{
MethodDeclarationSyntax methodSyntax = (MethodDeclarationSyntax)context.Node;
// 跳过异步方法,因为异步方法无法使用 ref in out
if (methodSyntax.Modifiers.Any(token => token.IsKind(SyntaxKind.AsyncKeyword)))
{
return;
}
// 跳过方法定义 如 接口
if (methodSyntax.Body == null)
{
return;
}
foreach (ParameterSyntax parameter in methodSyntax.ParameterList.Parameters)
{
if (context.SemanticModel.GetDeclaredSymbol(parameter) is IParameterSymbol symbol)
{
if (readOnlyStructs.Contains(symbol.Type.ToDisplayString()) && symbol.RefKind == RefKind.None)
{
Location location = parameter.GetLocation();
Diagnostic diagnostic = Diagnostic.Create(readOnlyStructRefDescriptor, location, symbol.Type);
context.ReportDiagnostic(diagnostic);
}
}
}
}
}

View File

@@ -12,7 +12,7 @@ namespace Snap.Hutao.Core.IO;
internal static class PickerExtension
{
/// <inheritdoc cref="FileOpenPicker.PickSingleFileAsync"/>
public static async Task<ValueResult<bool, FilePath>> TryPickSingleFileAsync(this FileOpenPicker picker)
public static async Task<ValueResult<bool, ValueFile>> TryPickSingleFileAsync(this FileOpenPicker picker)
{
StorageFile? file;
Exception? exception = null;
@@ -38,7 +38,7 @@ internal static class PickerExtension
}
/// <inheritdoc cref="FileSavePicker.PickSaveFileAsync"/>
public static async Task<ValueResult<bool, FilePath>> TryPickSaveFileAsync(this FileSavePicker picker)
public static async Task<ValueResult<bool, ValueFile>> TryPickSaveFileAsync(this FileSavePicker picker)
{
StorageFile? file;
Exception? exception = null;

View File

@@ -8,7 +8,7 @@ namespace Snap.Hutao.Core.IO;
/// <summary>
/// 文件路径
/// </summary>
internal readonly struct FilePath : IEquatable<FilePath>
internal readonly struct ValueFile
{
/// <summary>
/// 值
@@ -16,30 +16,30 @@ internal readonly struct FilePath : IEquatable<FilePath>
public readonly string Value;
/// <summary>
/// Initializes a new instance of the <see cref="FilePath"/> struct.
/// Initializes a new instance of the <see cref="ValueFile"/> struct.
/// </summary>
/// <param name="value">value</param>
public FilePath(string value)
public ValueFile(string value)
{
Value = value;
}
public static implicit operator string(FilePath value)
public static implicit operator string(ValueFile value)
{
return value.Value;
}
public static implicit operator FilePath(string value)
public static implicit operator ValueFile(string value)
{
return new(value);
}
public static bool operator ==(FilePath left, FilePath right)
public static bool operator ==(ValueFile left, ValueFile right)
{
return left.Value == right.Value;
}
public static bool operator !=(FilePath left, FilePath right)
public static bool operator !=(ValueFile left, ValueFile right)
{
return !(left == right);
}
@@ -92,16 +92,10 @@ internal readonly struct FilePath : IEquatable<FilePath>
}
}
/// <inheritdoc/>
public bool Equals(FilePath other)
{
return Value == other.Value;
}
/// <inheritdoc/>
public override bool Equals(object? obj)
{
return obj is FilePath other && Equals(other);
return obj is ValueFile other && Equals(other);
}
/// <inheritdoc/>

View File

@@ -24,7 +24,7 @@ internal static class Persistence
/// </summary>
/// <param name="options">选项</param>
/// <typeparam name="TWindow">窗体类型</typeparam>
public static void RecoverOrInit<TWindow>(WindowOptions<TWindow> options)
public static void RecoverOrInit<TWindow>(in WindowOptions<TWindow> options)
where TWindow : Window, IExtendedWindowSource
{
// Set first launch size.
@@ -50,7 +50,7 @@ internal static class Persistence
/// </summary>
/// <param name="options">选项</param>
/// <typeparam name="TWindow">窗体类型</typeparam>
public static void Save<TWindow>(WindowOptions<TWindow> options)
public static void Save<TWindow>(in WindowOptions<TWindow> options)
where TWindow : Window, IExtendedWindowSource
{
WINDOWPLACEMENT windowPlacement = StructMarshal.WINDOWPLACEMENT();

View File

@@ -0,0 +1,47 @@
[
{
"Name": "AvatarId",
"Type": "int",
"Documentation": "8λ <20><>ɫId"
},
{
"Name": "EquipAffixId",
"Type": "int",
"Documentation": "6λ װ<><D7B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Id"
},
{
"Name": "ExtendedEquipAffixId",
"Type": "int",
"Documentation": "7λ װ<><D7B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Id"
},
{
"Name": "MaterialId",
"Type": "int",
"Documentation": "3-6λ <20><><EFBFBD><EFBFBD>Id"
},
{
"Name": "MonsterId",
"Type": "int",
"Documentation": "8λ <20><><EFBFBD><EFBFBD>Id"
},
{
"Name": "PromoteId",
"Type": "int",
"Documentation": "1-5λ <20><>ɫͻ<C9AB><CDBB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Id"
},
{
"Name": "ReliquaryAffixId",
"Type": "int",
"Documentation": "6λ ʥ<><CAA5><EFBFBD><EFBFBD><EFB8B1><EFBFBD><EFBFBD>Id"
},
{
"Name": "ReliquaryMainAffixId",
"Type": "int",
"Documentation": "5λ ʥ<><CAA5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Id"
},
{
"Name": "WeaponId",
"Type": "int",
"Documentation": "5λ <20><><EFBFBD><EFBFBD>Id"
}
]

View File

@@ -64,7 +64,7 @@ internal static class AvatarIds
public static readonly AvatarId Yae = 10000058;
public static readonly AvatarId Heizo = 10000059;
public static readonly AvatarId Yelan = 10000060;
public static readonly AvatarId Kirara = 10000061;
public static readonly AvatarId Aloy = 10000062;
public static readonly AvatarId Shenhe = 10000063;
public static readonly AvatarId Yunjin = 10000064;
@@ -84,13 +84,15 @@ internal static class AvatarIds
public static readonly AvatarId Alhaitham = 10000078;
public static readonly AvatarId Dehya = 10000079;
public static readonly AvatarId Mika = 10000080;
public static readonly AvatarId Kaveh = 10000081;
public static readonly AvatarId Baizhuer = 10000082;
/// <summary>
/// 检查该角色是否为主角
/// </summary>
/// <param name="avatarId">角色Id</param>
/// <returns>角色是否为主角</returns>
public static bool IsPlayer(AvatarId avatarId)
public static bool IsPlayer(in AvatarId avatarId)
{
return avatarId == PlayerBoy || avatarId == PlayerGirl;
}

View File

@@ -1,66 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Primitive.Converter;
namespace Snap.Hutao.Model.Primitive;
/// <summary>
/// 8位 角色Id
/// </summary>
[HighQuality]
[JsonConverter(typeof(IdentityConverter<AvatarId>))]
internal readonly struct AvatarId : IEquatable<AvatarId>
{
/// <summary>
/// 值
/// </summary>
public readonly int Value;
/// <summary>
/// Initializes a new instance of the <see cref="AvatarId"/> struct.
/// </summary>
/// <param name="value">value</param>
public AvatarId(int value)
{
Value = value;
}
public static implicit operator int(AvatarId value)
{
return value.Value;
}
public static implicit operator AvatarId(int value)
{
return new(value);
}
public static bool operator ==(AvatarId left, AvatarId right)
{
return left.Value == right.Value;
}
public static bool operator !=(AvatarId left, AvatarId right)
{
return !(left == right);
}
/// <inheritdoc/>
public bool Equals(AvatarId other)
{
return Value == other.Value;
}
/// <inheritdoc/>
public override bool Equals(object? obj)
{
return obj is AvatarId other && Equals(other);
}
/// <inheritdoc/>
public override int GetHashCode()
{
return Value.GetHashCode();
}
}

View File

@@ -1,27 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core.ExpressionService;
namespace Snap.Hutao.Model.Primitive.Converter;
/// <summary>
/// Id 转换器
/// </summary>
/// <typeparam name="TWrapper">包装类型</typeparam>
[HighQuality]
internal sealed class IdentityConverter<TWrapper> : JsonConverter<TWrapper>
where TWrapper : struct
{
/// <inheritdoc/>
public override TWrapper Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return CastTo<TWrapper>.From(reader.GetInt32());
}
/// <inheritdoc/>
public override void Write(Utf8JsonWriter writer, TWrapper value, JsonSerializerOptions options)
{
writer.WriteNumberValue(CastTo<int>.From(value));
}
}

View File

@@ -1,66 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Primitive.Converter;
namespace Snap.Hutao.Model.Primitive;
/// <summary>
/// 6位 装备属性Id
/// </summary>
[HighQuality]
[JsonConverter(typeof(IdentityConverter<EquipAffixId>))]
internal readonly struct EquipAffixId : IEquatable<EquipAffixId>
{
/// <summary>
/// 值
/// </summary>
public readonly int Value;
/// <summary>
/// Initializes a new instance of the <see cref="EquipAffixId"/> struct.
/// </summary>
/// <param name="value">value</param>
public EquipAffixId(int value)
{
Value = value;
}
public static implicit operator int(EquipAffixId value)
{
return value.Value;
}
public static implicit operator EquipAffixId(int value)
{
return new(value);
}
public static bool operator ==(EquipAffixId left, EquipAffixId right)
{
return left.Value == right.Value;
}
public static bool operator !=(EquipAffixId left, EquipAffixId right)
{
return !(left == right);
}
/// <inheritdoc/>
public bool Equals(EquipAffixId other)
{
return Value == other.Value;
}
/// <inheritdoc/>
public override bool Equals(object? obj)
{
return obj is EquipAffixId other && Equals(other);
}
/// <inheritdoc/>
public override int GetHashCode()
{
return Value.GetHashCode();
}
}

View File

@@ -1,66 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Primitive.Converter;
namespace Snap.Hutao.Model.Primitive;
/// <summary>
/// 7位 装备属性Id
/// </summary>
[HighQuality]
[JsonConverter(typeof(IdentityConverter<ExtendedEquipAffixId>))]
internal readonly struct ExtendedEquipAffixId : IEquatable<ExtendedEquipAffixId>
{
/// <summary>
/// 值
/// </summary>
public readonly int Value;
/// <summary>
/// Initializes a new instance of the <see cref="ExtendedEquipAffixId"/> struct.
/// </summary>
/// <param name="value">value</param>
public ExtendedEquipAffixId(int value)
{
Value = value;
}
public static implicit operator int(ExtendedEquipAffixId value)
{
return value.Value;
}
public static implicit operator ExtendedEquipAffixId(int value)
{
return new(value);
}
public static bool operator ==(ExtendedEquipAffixId left, ExtendedEquipAffixId right)
{
return left.Value == right.Value;
}
public static bool operator !=(ExtendedEquipAffixId left, ExtendedEquipAffixId right)
{
return !(left == right);
}
/// <inheritdoc/>
public bool Equals(ExtendedEquipAffixId other)
{
return Value == other.Value;
}
/// <inheritdoc/>
public override bool Equals(object? obj)
{
return obj is ExtendedEquipAffixId other && Equals(other);
}
/// <inheritdoc/>
public override int GetHashCode()
{
return Value.GetHashCode();
}
}

View File

@@ -1,72 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Primitive.Converter;
namespace Snap.Hutao.Model.Primitive;
/// <summary>
/// 3-6位 材料Id
/// </summary>
[HighQuality]
[JsonConverter(typeof(IdentityConverter<MaterialId>))]
internal readonly struct MaterialId : IEquatable<MaterialId>, IComparable<MaterialId>
{
/// <summary>
/// 值
/// </summary>
public readonly int Value;
/// <summary>
/// Initializes a new instance of the <see cref="MaterialId"/> struct.
/// </summary>
/// <param name="value">value</param>
public MaterialId(int value)
{
Value = value;
}
public static implicit operator int(MaterialId value)
{
return value.Value;
}
public static implicit operator MaterialId(int value)
{
return new(value);
}
public static bool operator ==(MaterialId left, MaterialId right)
{
return left.Value == right.Value;
}
public static bool operator !=(MaterialId left, MaterialId right)
{
return !(left == right);
}
/// <inheritdoc/>
public int CompareTo(MaterialId other)
{
return Value.CompareTo(other.Value);
}
/// <inheritdoc/>
public bool Equals(MaterialId other)
{
return Value == other.Value;
}
/// <inheritdoc/>
public override bool Equals(object? obj)
{
return obj is MaterialId other && Equals(other);
}
/// <inheritdoc/>
public override int GetHashCode()
{
return Value.GetHashCode();
}
}

View File

@@ -1,72 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Primitive.Converter;
namespace Snap.Hutao.Model.Primitive;
/// <summary>
/// 8位 怪物Id
/// </summary>
[HighQuality]
[JsonConverter(typeof(IdentityConverter<MonsterId>))]
internal readonly struct MonsterId : IEquatable<MonsterId>
{
/// <summary>
/// 值
/// </summary>
public readonly int Value;
/// <summary>
/// Initializes a new instance of the <see cref="MonsterId"/> struct.
/// </summary>
/// <param name="value">value</param>
public MonsterId(int value)
{
Value = value;
}
public static implicit operator int(MonsterId value)
{
return value.Value;
}
public static implicit operator MonsterId(int value)
{
return new(value);
}
public static bool operator ==(MonsterId left, MonsterId right)
{
return left.Value == right.Value;
}
public static bool operator !=(MonsterId left, MonsterId right)
{
return !(left == right);
}
/// <inheritdoc/>
public bool Equals(MonsterId other)
{
return Value == other.Value;
}
/// <inheritdoc/>
public override bool Equals(object? obj)
{
return obj is MonsterId other && Equals(other);
}
/// <inheritdoc/>
public override int GetHashCode()
{
return Value.GetHashCode();
}
/// <inheritdoc/>
public override string ToString()
{
return Value.ToString();
}
}

View File

@@ -1,66 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Primitive.Converter;
namespace Snap.Hutao.Model.Primitive;
/// <summary>
/// 1-5位 角色突破提升Id
/// </summary>
[HighQuality]
[JsonConverter(typeof(IdentityConverter<PromoteId>))]
internal readonly struct PromoteId : IEquatable<PromoteId>
{
/// <summary>
/// 值
/// </summary>
public readonly int Value;
/// <summary>
/// Initializes a new instance of the <see cref="PromoteId"/> struct.
/// </summary>
/// <param name="value">value</param>
public PromoteId(int value)
{
Value = value;
}
public static implicit operator int(PromoteId value)
{
return value.Value;
}
public static implicit operator PromoteId(int value)
{
return new(value);
}
public static bool operator ==(PromoteId left, PromoteId right)
{
return left.Value == right.Value;
}
public static bool operator !=(PromoteId left, PromoteId right)
{
return !(left == right);
}
/// <inheritdoc/>
public bool Equals(PromoteId other)
{
return Value == other.Value;
}
/// <inheritdoc/>
public override bool Equals(object? obj)
{
return obj is PromoteId other && Equals(other);
}
/// <inheritdoc/>
public override int GetHashCode()
{
return Value.GetHashCode();
}
}

View File

@@ -1,66 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Primitive.Converter;
namespace Snap.Hutao.Model.Primitive;
/// <summary>
/// 6位 圣遗物副词条Id
/// </summary>
[HighQuality]
[JsonConverter(typeof(IdentityConverter<ReliquaryAffixId>))]
internal readonly struct ReliquaryAffixId : IEquatable<ReliquaryAffixId>
{
/// <summary>
/// 值
/// </summary>
public readonly int Value;
/// <summary>
/// Initializes a new instance of the <see cref="ReliquaryAffixId"/> struct.
/// </summary>
/// <param name="value">value</param>
public ReliquaryAffixId(int value)
{
Value = value;
}
public static implicit operator int(ReliquaryAffixId value)
{
return value.Value;
}
public static implicit operator ReliquaryAffixId(int value)
{
return new(value);
}
public static bool operator ==(ReliquaryAffixId left, ReliquaryAffixId right)
{
return left.Value == right.Value;
}
public static bool operator !=(ReliquaryAffixId left, ReliquaryAffixId right)
{
return !(left == right);
}
/// <inheritdoc/>
public bool Equals(ReliquaryAffixId other)
{
return Value == other.Value;
}
/// <inheritdoc/>
public override bool Equals(object? obj)
{
return obj is ReliquaryAffixId other && Equals(other);
}
/// <inheritdoc/>
public override int GetHashCode()
{
return Value.GetHashCode();
}
}

View File

@@ -1,66 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Primitive.Converter;
namespace Snap.Hutao.Model.Primitive;
/// <summary>
/// 5位 圣遗物主属性Id
/// </summary>
[HighQuality]
[JsonConverter(typeof(IdentityConverter<ReliquaryMainAffixId>))]
internal readonly struct ReliquaryMainAffixId : IEquatable<ReliquaryMainAffixId>
{
/// <summary>
/// 值
/// </summary>
public readonly int Value;
/// <summary>
/// Initializes a new instance of the <see cref="ReliquaryMainAffixId"/> struct.
/// </summary>
/// <param name="value">value</param>
public ReliquaryMainAffixId(int value)
{
Value = value;
}
public static implicit operator int(ReliquaryMainAffixId value)
{
return value.Value;
}
public static implicit operator ReliquaryMainAffixId(int value)
{
return new(value);
}
public static bool operator ==(ReliquaryMainAffixId left, ReliquaryMainAffixId right)
{
return left.Value == right.Value;
}
public static bool operator !=(ReliquaryMainAffixId left, ReliquaryMainAffixId right)
{
return !(left == right);
}
/// <inheritdoc/>
public bool Equals(ReliquaryMainAffixId other)
{
return Value == other.Value;
}
/// <inheritdoc/>
public override bool Equals(object? obj)
{
return obj is ReliquaryMainAffixId other && Equals(other);
}
/// <inheritdoc/>
public override int GetHashCode()
{
return Value.GetHashCode();
}
}

View File

@@ -1,66 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Primitive.Converter;
namespace Snap.Hutao.Model.Primitive;
/// <summary>
/// 5位 武器Id
/// </summary>
[HighQuality]
[JsonConverter(typeof(IdentityConverter<WeaponId>))]
internal readonly struct WeaponId : IEquatable<WeaponId>
{
/// <summary>
/// 值
/// </summary>
public readonly int Value;
/// <summary>
/// Initializes a new instance of the <see cref="WeaponId"/> struct.
/// </summary>
/// <param name="value">value</param>
public WeaponId(int value)
{
Value = value;
}
public static implicit operator int(WeaponId value)
{
return value.Value;
}
public static implicit operator WeaponId(int value)
{
return new(value);
}
public static bool operator ==(WeaponId left, WeaponId right)
{
return left.Value == right.Value;
}
public static bool operator !=(WeaponId left, WeaponId right)
{
return !(left == right);
}
/// <inheritdoc/>
public bool Equals(WeaponId other)
{
return Value == other.Value;
}
/// <inheritdoc/>
public override bool Equals(object? obj)
{
return obj is WeaponId other && Equals(other);
}
/// <inheritdoc/>
public override int GetHashCode()
{
return Value.GetHashCode();
}
}

View File

@@ -5,10 +5,13 @@ using CommunityToolkit.Mvvm.Messaging;
using Microsoft.UI.Xaml;
using Snap.Hutao.Service;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Windows.Globalization;
using WinRT;
[assembly:InternalsVisibleTo("Snap.Hutao.Test")]
namespace Snap.Hutao;
/// <summary>

View File

@@ -35,7 +35,7 @@ internal static class ElementMastery
/// <param name="mastery">元素精通</param>
/// <param name="coeff">参数</param>
/// <returns>差异</returns>
public static float GetDelta(float mastery, ElementMasteryCoefficient coeff)
public static float GetDelta(float mastery, in ElementMasteryCoefficient coeff)
{
return mastery + coeff.P2 == 0 ? 0 : MathF.Max(mastery * coeff.P1 / (mastery + coeff.P2), 0);
}

View File

@@ -136,7 +136,7 @@ internal sealed class CultivationService : ICultivationService
.ToList();
List<InventoryItemView> results = new();
foreach (Material meta in metadata.Where(m => m.IsInventoryItem()).OrderBy(m => m.Id))
foreach (Material meta in metadata.Where(m => m.IsInventoryItem()).OrderBy(m => m.Id.Value))
{
InventoryItem entity = entities.SingleOrDefault(e => e.ItemId == meta.Id) ?? InventoryItem.Create(projectId, meta.Id);
results.Add(new(entity, meta, saveCommand));

View File

@@ -47,7 +47,7 @@ internal sealed class GachaStatisticsFactory : IGachaStatisticsFactory
private static GachaStatistics CreateCore(
IOrderedQueryable<GachaItem> items,
List<HistoryWishBuilder> historyWishBuilders,
GachaLogServiceContext context,
in GachaLogServiceContext context,
bool isEmptyHistoryWishVisible)
{
TypedWishSummaryBuilder standardWishBuilder = new(SH.ServiceGachaLogFactoryPermanentWishName, TypedWishSummaryBuilder.IsStandardWish, 90, 10);

View File

@@ -111,7 +111,7 @@ internal sealed class LaunchScheme
/// </summary>
/// <param name="multiChannel">多通道</param>
/// <returns>是否相等</returns>
public bool MultiChannelEqual(MultiChannel multiChannel)
public bool MultiChannelEqual(in MultiChannel multiChannel)
{
return Channel == multiChannel.Channel && SubChannel == multiChannel.SubChannel;
}

View File

@@ -36,7 +36,7 @@ internal sealed class ManualGameLocator : IGameLocator
SH.ServiceGameLocatorFileOpenPickerCommitText,
".exe");
(bool isPickerOk, FilePath file) = await picker.TryPickSingleFileAsync().ConfigureAwait(false);
(bool isPickerOk, ValueFile file) = await picker.TryPickSingleFileAsync().ConfigureAwait(false);
if (isPickerOk)
{

View File

@@ -72,6 +72,7 @@
<None Remove="Assets\Wide310x150Logo.scale-400.png" />
<None Remove="Control\Panel\PanelSelector.xaml" />
<None Remove="Control\Theme\FontStyle.xaml" />
<None Remove="IdentityStructs.json" />
<None Remove="LaunchGameWindow.xaml" />
<None Remove="NativeMethods.json" />
<None Remove="NativeMethods.txt" />
@@ -106,6 +107,7 @@
<None Remove="Resource\Segoe Fluent Icons.ttf" />
<None Remove="Resource\WelcomeView_Background.png" />
<None Remove="stylecop.json" />
<None Remove="View\Card\AchievementCard.xaml" />
<None Remove="View\Card\GachaStatisticsCard.xaml" />
<None Remove="View\Card\LaunchGameCard.xaml" />
<None Remove="View\Control\BaseValueSlider.xaml" />
@@ -157,6 +159,7 @@
<!-- Analyzer Files -->
<ItemGroup>
<AdditionalFiles Include="IdentityStructs.json" />
<AdditionalFiles Include="NativeMethods.json" />
<AdditionalFiles Include="NativeMethods.txt" />
<AdditionalFiles Include="ReliquaryWeightConfiguration.json" />
@@ -296,6 +299,12 @@
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Page Update="View\Card\AchievementCard.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Page Update="View\Card\GachaStatisticsCard.xaml">
<Generator>MSBuild:Compile</Generator>

View File

@@ -0,0 +1,11 @@
<Button
x:Class="Snap.Hutao.View.Card.AchievementCard"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:Snap.Hutao.View.Card"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid/>
</Button>

View File

@@ -0,0 +1,20 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Xaml.Controls;
namespace Snap.Hutao.View.Card;
/// <summary>
/// 成就卡片
/// </summary>
internal sealed partial class AchievementCard : Button
{
/// <summary>
/// 构造一个新的成就卡片
/// </summary>
public AchievementCard()
{
InitializeComponent();
}
}

View File

@@ -8,7 +8,7 @@ namespace Snap.Hutao.View.Control;
/// <summary>
/// <20>򵥵ļ<F2B5A5B5><C4BC><EFBFBD><EFBFBD><EFBFBD>ͼ
/// </summary>
public sealed partial class LoadingViewSlim : Loading
internal sealed partial class LoadingViewSlim : Loading
{
/// <summary>
/// <20><><EFBFBD><EFBFBD>һ<EFBFBD><D2BB><EFBFBD>µļ򵥵ļ<F2B5A5B5><C4BC><EFBFBD><EFBFBD><EFBFBD>ͼ

View File

@@ -73,7 +73,7 @@ internal sealed class AchievementImporter
{
if (achievementService.CurrentArchive != null)
{
(bool isPickerOk, FilePath file) = await serviceProvider
(bool isPickerOk, ValueFile file) = await serviceProvider
.GetRequiredService<IPickerFactory>()
.GetFileOpenPicker(PickerLocationId.Desktop, SH.FilePickerImportCommit, ".json")
.TryPickSingleFileAsync()

View File

@@ -2,7 +2,6 @@
// Licensed under the MIT license.
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.WinUI.UI;
using Microsoft.UI.Xaml.Controls;
using Snap.Hutao.Core.Database;
@@ -327,7 +326,7 @@ internal sealed class AchievementViewModel : Abstraction.ViewModel, INavigationR
picker.CommitButtonText = SH.FilePickerExportCommit;
picker.SuggestedFileName = $"{achievementService.CurrentArchive?.Name}.json";
(bool isPickerOk, FilePath file) = await picker.TryPickSaveFileAsync().ConfigureAwait(false);
(bool isPickerOk, ValueFile file) = await picker.TryPickSaveFileAsync().ConfigureAwait(false);
if (isPickerOk)
{
UIAF uiaf = await achievementService.ExportToUIAFAsync(SelectedArchive).ConfigureAwait(false);

View File

@@ -0,0 +1,19 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.ViewModel.Achievement;
/// <summary>
/// 简化的成就视图模型
/// </summary>
internal sealed class AchievementViewModelSlim : Abstraction.ViewModelSlim<View.Page.AchievementPage>
{
/// <summary>
/// 构造一个新的简化的成就视图模型
/// </summary>
/// <param name="serviceProvider">服务提供器</param>
public AchievementViewModelSlim(IServiceProvider serviceProvider)
: base(serviceProvider)
{
}
}

View File

@@ -242,7 +242,7 @@ internal sealed class GachaLogViewModel : Abstraction.ViewModel
private async Task ImportFromUIGFJsonAsync()
{
FileOpenPicker picker = pickerFactory.GetFileOpenPicker(PickerLocationId.Desktop, SH.FilePickerImportCommit, ".json");
(bool isPickerOk, FilePath file) = await picker.TryPickSingleFileAsync().ConfigureAwait(false);
(bool isPickerOk, ValueFile file) = await picker.TryPickSingleFileAsync().ConfigureAwait(false);
if (isPickerOk)
{
(bool isOk, UIGF? uigf) = await file.DeserializeFromJsonAsync<UIGF>(options).ConfigureAwait(false);
@@ -267,7 +267,7 @@ internal sealed class GachaLogViewModel : Abstraction.ViewModel
picker.CommitButtonText = SH.FilePickerExportCommit;
picker.FileTypeChoices.Add(SH.ViewModelGachaLogExportFileType, ".json".Enumerate().ToList());
(bool isPickerOk, FilePath file) = await picker.TryPickSaveFileAsync().ConfigureAwait(false);
(bool isPickerOk, ValueFile file) = await picker.TryPickSaveFileAsync().ConfigureAwait(false);
if (isPickerOk)
{

View File

@@ -116,7 +116,7 @@ internal static class ApiEndpoints
/// </summary>
/// <param name="uid">uid</param>
/// <returns>角色基本信息字符串</returns>
public static string GameRecordRoleBasicInfo(PlayerUid uid)
public static string GameRecordRoleBasicInfo(in PlayerUid uid)
{
return $"{ApiTakumiRecordApi}/roleBasicInfo?role_id={uid.Value}&server={uid.Region}";
}
@@ -131,7 +131,7 @@ internal static class ApiEndpoints
/// </summary>
/// <param name="uid">uid</param>
/// <returns>游戏记录实时便笺字符串</returns>
public static string GameRecordDailyNote(PlayerUid uid)
public static string GameRecordDailyNote(in PlayerUid uid)
{
return $"{ApiTakumiRecordApi}/dailyNote?server={uid.Region}&role_id={uid.Value}";
}
@@ -141,7 +141,7 @@ internal static class ApiEndpoints
/// </summary>
/// <param name="uid">uid</param>
/// <returns>游戏记录主页字符串</returns>
public static string GameRecordIndex(PlayerUid uid)
public static string GameRecordIndex(in PlayerUid uid)
{
return $"{ApiTakumiRecordApi}/index?server={uid.Region}&role_id={uid.Value}";
}
@@ -152,7 +152,7 @@ internal static class ApiEndpoints
/// <param name="scheduleType">深渊类型</param>
/// <param name="uid">Uid</param>
/// <returns>深渊信息字符串</returns>
public static string GameRecordSpiralAbyss(Hoyolab.Takumi.GameRecord.SpiralAbyssSchedule scheduleType, PlayerUid uid)
public static string GameRecordSpiralAbyss(Hoyolab.Takumi.GameRecord.SpiralAbyssSchedule scheduleType, in PlayerUid uid)
{
return $"{ApiTakumiRecordApi}/spiralAbyss?schedule_type={(int)scheduleType}&role_id={uid.Value}&server={uid.Region}";
}
@@ -208,7 +208,7 @@ internal static class ApiEndpoints
/// <param name="avatarId">角色Id</param>
/// <param name="uid">uid</param>
/// <returns>角色详情</returns>
public static string CalculateSyncAvatarDetail(AvatarId avatarId, PlayerUid uid)
public static string CalculateSyncAvatarDetail(AvatarId avatarId, in PlayerUid uid)
{
return $"{ApiTakumiEventCalculate}/v1/sync/avatar/detail?avatar_id={avatarId.Value}&uid={uid.Value}&region={uid.Region}";
}

View File

@@ -124,7 +124,7 @@ internal static class ApiOsEndpoints
/// </summary>
/// <param name="uid">uid</param>
/// <returns>角色基本信息字符串</returns>
public static string GameRecordRoleBasicInfo(PlayerUid uid)
public static string GameRecordRoleBasicInfo(in PlayerUid uid)
{
return $"{BbsApiOsGameRecordApi}/roleBasicInfo?role_id={uid.Value}&server={uid.Region}";
}
@@ -139,7 +139,7 @@ internal static class ApiOsEndpoints
/// </summary>
/// <param name="uid">uid</param>
/// <returns>游戏记录实时便笺字符串</returns>
public static string GameRecordDailyNote(PlayerUid uid)
public static string GameRecordDailyNote(in PlayerUid uid)
{
return $"{BbsApiOsGameRecordApi}/dailyNote?server={uid.Region}&role_id={uid.Value}";
}
@@ -149,7 +149,7 @@ internal static class ApiOsEndpoints
/// </summary>
/// <param name="uid">uid</param>
/// <returns>游戏记录主页字符串</returns>
public static string GameRecordIndex(PlayerUid uid)
public static string GameRecordIndex(in PlayerUid uid)
{
return $"{BbsApiOsGameRecordApi}/index?server={uid.Region}&role_id={uid.Value}";
}
@@ -160,7 +160,7 @@ internal static class ApiOsEndpoints
/// <param name="scheduleType">深渊类型</param>
/// <param name="uid">Uid</param>
/// <returns>深渊信息字符串</returns>
public static string GameRecordSpiralAbyss(Hoyolab.Takumi.GameRecord.SpiralAbyssSchedule scheduleType, PlayerUid uid)
public static string GameRecordSpiralAbyss(Hoyolab.Takumi.GameRecord.SpiralAbyssSchedule scheduleType, in PlayerUid uid)
{
return $"{BbsApiOsGameRecordApi}/spiralAbyss?schedule_type={(int)scheduleType}&role_id={uid.Value}&server={uid.Region}";
}
@@ -208,7 +208,7 @@ internal static class ApiOsEndpoints
/// <param name="avatarId">角色Id</param>
/// <param name="uid">uid</param>
/// <returns>角色详情</returns>
public static string CalculateSyncAvatarDetail(AvatarId avatarId, PlayerUid uid)
public static string CalculateSyncAvatarDetail(AvatarId avatarId, in PlayerUid uid)
{
return $"{SgPublicApi}/event/calculateos/sync/avatar/detail?avatar_id={avatarId.Value}&uid={uid.Value}&region={uid.Region}";
}

View File

@@ -41,7 +41,7 @@ internal sealed class EnkaClient
/// <param name="playerUid">玩家Uid</param>
/// <param name="token">取消令牌</param>
/// <returns>Enka API 响应</returns>
public Task<EnkaResponse?> GetForwardDataAsync(PlayerUid playerUid, CancellationToken token = default)
public Task<EnkaResponse?> GetForwardDataAsync(in PlayerUid playerUid, CancellationToken token = default)
{
return httpClient.TryCatchGetFromJsonAsync<EnkaResponse>(string.Format(EnkaAPIHutaoForward, playerUid.Value), options, logger, token);
}
@@ -52,7 +52,7 @@ internal sealed class EnkaClient
/// <param name="playerUid">玩家Uid</param>
/// <param name="token">取消令牌</param>
/// <returns>Enka API 响应</returns>
public Task<EnkaResponse?> GetDataAsync(PlayerUid playerUid, CancellationToken token = default)
public Task<EnkaResponse?> GetDataAsync(in PlayerUid playerUid, CancellationToken token = default)
{
return httpClient.TryCatchGetFromJsonAsync<EnkaResponse>(string.Format(EnkaAPI, playerUid.Value), options, logger, token);
}

View File

@@ -51,10 +51,7 @@ internal sealed partial class Cookie
{
string name = parts[0].Trim();
string value = parts.Length == 1 ? string.Empty : parts[1].Trim();
// System.ArgumentException: An item with the same key has already been added.
// cookieMap.Add(name, value);
cookieMap[name] = value;
cookieMap.TryAdd(name, value);
}
return new(cookieMap);
@@ -73,7 +70,7 @@ internal sealed partial class Cookie
}
/// <summary>
/// 从登录结果创建Cookie
/// 从登录结果创建 Cookie
/// </summary>
/// <param name="loginResult">登录结果</param>
/// <returns>Cookie</returns>
@@ -94,6 +91,12 @@ internal sealed partial class Cookie
return new(cookieMap);
}
/// <summary>
/// 从 SToken 创建 Cookie
/// </summary>
/// <param name="stuid">stuid</param>
/// <param name="stoken">stoken</param>
/// <returns>Cookie</returns>
public static Cookie FromSToken(string stuid, string stoken)
{
SortedDictionary<string, string> cookieMap = new()

View File

@@ -29,7 +29,6 @@ internal sealed class GenAuthKeyData
/// App Id
/// </summary>
[JsonPropertyName("auth_appid")]
public string AuthAppId { get; set; } = default!;
/// <summary>
@@ -55,7 +54,7 @@ internal sealed class GenAuthKeyData
/// </summary>
/// <param name="uid">uid</param>
/// <returns>验证密钥提交数据</returns>
public static GenAuthKeyData CreateForWebViewGacha(PlayerUid uid)
public static GenAuthKeyData CreateForWebViewGacha(in PlayerUid uid)
{
return new("webview_gacha", "hk4e_cn", uid);
}