diff --git a/src/Snap.Hutao/SettingsUI/SettingsUI.csproj b/src/Snap.Hutao/SettingsUI/SettingsUI.csproj index d06543fb..bc952b96 100644 --- a/src/Snap.Hutao/SettingsUI/SettingsUI.csproj +++ b/src/Snap.Hutao/SettingsUI/SettingsUI.csproj @@ -3,7 +3,8 @@ net6.0-windows10.0.17763.0 10.0.17763.0 SettingsUI - win10-x86;win10-x64;win10-arm64 + x64 + win10-x64 true latest enable diff --git a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/DedendencyInjection/InjectionGenerator.cs b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/DedendencyInjection/InjectionGenerator.cs index b3c2dc99..40154b3e 100644 --- a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/DedendencyInjection/InjectionGenerator.cs +++ b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/DedendencyInjection/InjectionGenerator.cs @@ -107,7 +107,7 @@ internal static partial class ServiceCollectionExtensions lines.Add(lineBuilder.ToString()); } - foreach (string line in lines.OrderByDescending(x => x)) + foreach (string line in lines.OrderBy(x => x)) { sourceCodeBuilder.Append(line); } diff --git a/src/Snap.Hutao/Snap.Hutao.sln b/src/Snap.Hutao/Snap.Hutao.sln index 2f861db8..bb171023 100644 --- a/src/Snap.Hutao/Snap.Hutao.sln +++ b/src/Snap.Hutao/Snap.Hutao.sln @@ -54,8 +54,8 @@ Global {DCA5678C-896E-49FB-97A7-5A504A5CFF17}.Debug|Any CPU.Build.0 = Debug|Any CPU {DCA5678C-896E-49FB-97A7-5A504A5CFF17}.Debug|arm64.ActiveCfg = Debug|Any CPU {DCA5678C-896E-49FB-97A7-5A504A5CFF17}.Debug|arm64.Build.0 = Debug|Any CPU - {DCA5678C-896E-49FB-97A7-5A504A5CFF17}.Debug|x64.ActiveCfg = Debug|Any CPU - {DCA5678C-896E-49FB-97A7-5A504A5CFF17}.Debug|x64.Build.0 = Debug|Any CPU + {DCA5678C-896E-49FB-97A7-5A504A5CFF17}.Debug|x64.ActiveCfg = Debug|x64 + {DCA5678C-896E-49FB-97A7-5A504A5CFF17}.Debug|x64.Build.0 = Debug|x64 {DCA5678C-896E-49FB-97A7-5A504A5CFF17}.Debug|x86.ActiveCfg = Debug|Any CPU {DCA5678C-896E-49FB-97A7-5A504A5CFF17}.Debug|x86.Build.0 = Debug|Any CPU {DCA5678C-896E-49FB-97A7-5A504A5CFF17}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/src/Snap.Hutao/Snap.Hutao/App.xaml b/src/Snap.Hutao/Snap.Hutao/App.xaml index f051bbca..dacaca02 100644 --- a/src/Snap.Hutao/Snap.Hutao/App.xaml +++ b/src/Snap.Hutao/Snap.Hutao/App.xaml @@ -17,6 +17,8 @@ + + 6,16,16,16 16,0,0,0 diff --git a/src/Snap.Hutao/Snap.Hutao/App.xaml.cs b/src/Snap.Hutao/Snap.Hutao/App.xaml.cs index e3216d7e..8a9c2bba 100644 --- a/src/Snap.Hutao/Snap.Hutao/App.xaml.cs +++ b/src/Snap.Hutao/Snap.Hutao/App.xaml.cs @@ -4,7 +4,11 @@ using CommunityToolkit.Mvvm.Messaging; using Microsoft.Extensions.DependencyInjection; using Microsoft.UI.Xaml; +using Microsoft.Windows.AppLifecycle; using Snap.Hutao.Core.Logging; +using Snap.Hutao.Service.Abstraction; +using System.Diagnostics; +using Windows.ApplicationModel.Activation; namespace Snap.Hutao; @@ -14,10 +18,10 @@ namespace Snap.Hutao; public partial class App : Application { private static Window? window; + private readonly ILogger logger; /// /// Initializes the singleton application object. - /// This is the first line of authored code executed, and as such is the logical equivalent of main() or WinMain(). /// public App() { @@ -25,6 +29,7 @@ public partial class App : Application InitializeComponent(); InitializeDependencyInjection(); + logger = Ioc.Default.GetRequiredService>(); UnhandledException += AppUnhandledException; } @@ -34,14 +39,38 @@ public partial class App : Application public static Window? Window { get => window; set => window = value; } /// - /// Invoked when the application is launched normally by the end user. - /// Other entry points will be used such as when the application is launched to open a specific file. + /// Invoked when the application is launched. /// /// Details about the launch request and process. - protected override void OnLaunched(LaunchActivatedEventArgs args) + [SuppressMessage("", "VSTHRD100")] + protected override async void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args) { - Window = Ioc.Default.GetRequiredService(); - Window.Activate(); + AppActivationArguments activatedEventArgs = AppInstance.GetCurrent().GetActivatedEventArgs(); + AppInstance mainInstance = AppInstance.FindOrRegisterForKey("main"); + + if (!mainInstance.IsCurrent) + { + // Redirect the activation (and args) to the "main" instance, and exit. + await mainInstance.RedirectActivationToAsync(activatedEventArgs); + Process.GetCurrentProcess().Kill(); + } + else + { + Uri? uri = null; + if (activatedEventArgs.Kind == ExtendedActivationKind.Protocol) + { + IProtocolActivatedEventArgs protocolArgs = (activatedEventArgs.Data as IProtocolActivatedEventArgs)!; + uri = protocolArgs.Uri; + } + + Window = Ioc.Default.GetRequiredService(); + Window.Activate(); + + if (uri != null) + { + Ioc.Default.GetRequiredService().Information(uri.ToString()); + } + } } private static void InitializeDependencyInjection() @@ -56,7 +85,7 @@ public partial class App : Application .AddInjections() .AddDatebase() .AddHttpClients() - .AddDefaultJsonSerializerOptions() + .AddJsonSerializerOptions() // Discrete services .AddSingleton(WeakReferenceMessenger.Default) @@ -68,7 +97,6 @@ public partial class App : Application private void AppUnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e) { - ILogger logger = Ioc.Default.GetRequiredService>(); logger.LogError(EventIds.UnhandledException, e.Exception, "未经处理的异常"); } } diff --git a/src/Snap.Hutao/Snap.Hutao/Context/Database/AppDbContext.cs b/src/Snap.Hutao/Snap.Hutao/Context/Database/AppDbContext.cs index c6cef5c8..6d9b092d 100644 --- a/src/Snap.Hutao/Snap.Hutao/Context/Database/AppDbContext.cs +++ b/src/Snap.Hutao/Snap.Hutao/Context/Database/AppDbContext.cs @@ -35,7 +35,7 @@ public class AppDbContext : DbContext /// /// 连接字符串 /// 应用程序数据库上下文 - public static AppDbContext CreateFrom(string sqlConnectionString) + public static AppDbContext Create(string sqlConnectionString) { return new(new DbContextOptionsBuilder().UseSqlite(sqlConnectionString).Options); } diff --git a/src/Snap.Hutao/Snap.Hutao/Context/Database/AppDbContextDesignTimeFactory.cs b/src/Snap.Hutao/Snap.Hutao/Context/Database/AppDbContextDesignTimeFactory.cs index 43027d32..774fe791 100644 --- a/src/Snap.Hutao/Snap.Hutao/Context/Database/AppDbContextDesignTimeFactory.cs +++ b/src/Snap.Hutao/Snap.Hutao/Context/Database/AppDbContextDesignTimeFactory.cs @@ -22,6 +22,6 @@ public class AppDbContextDesignTimeFactory : IDesignTimeDbContextFactory + /// 创建文件,若已存在文件,则不会创建 + /// + /// 文件 + public void CreateFileOrIgnore(string file) + { + file = Locate(file); + if (!File.Exists(file)) + { + File.Create(file).Dispose(); + } + } + + /// + /// 创建文件夹,若已存在文件,则不会创建 + /// + /// 文件夹 + public void CreateFolderOrIgnore(string folder) + { + folder = Locate(folder); + if (!Directory.Exists(folder)) + { + Directory.CreateDirectory(folder); + } + } + + /// + /// 尝试删除文件夹 + /// + /// 文件夹 + public void DeleteFolderOrIgnore(string folder) + { + folder = Locate(folder); + if (Directory.Exists(folder)) + { + Directory.Delete(folder, true); + } + } + /// /// 检查根目录 /// @@ -113,41 +152,22 @@ internal abstract class FileSystemContext } /// - /// 创建文件,若已存在文件,则不会创建 + /// 等效于 ,但路径经过解析 /// - /// 文件 - public void CreateFileOrIgnore(string file) + /// 文件名 + /// 文件流 + public FileStream OpenRead(string file) { - file = Locate(file); - if (!File.Exists(file)) - { - File.Create(file).Dispose(); - } + return File.OpenRead(Locate(file)); } /// - /// 创建文件夹,若已存在文件,则不会创建 + /// 等效于 ,但路径经过解析 /// - /// 文件夹 - public void CreateFolderOrIgnore(string folder) + /// 文件名 + /// 文件流 + public FileStream Create(string file) { - folder = Locate(folder); - if (!Directory.Exists(folder)) - { - Directory.CreateDirectory(folder); - } - } - - /// - /// 尝试删除文件夹 - /// - /// 文件夹 - public void DeleteFolderOrIgnore(string folder) - { - folder = Locate(folder); - if (Directory.Exists(folder)) - { - Directory.Delete(folder, true); - } + return File.Create(Locate(file)); } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Context/FileSystem/Location/Metadata.cs b/src/Snap.Hutao/Snap.Hutao/Context/FileSystem/Location/Metadata.cs new file mode 100644 index 00000000..7110d6a8 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Context/FileSystem/Location/Metadata.cs @@ -0,0 +1,27 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using System.IO; + +namespace Snap.Hutao.Context.FileSystem.Location; + +/// +/// 我的文档位置 +/// +[Injection(InjectAs.Transient)] +public class Metadata : IFileSystemLocation +{ + private string? path; + + /// + public string GetPath() + { + if (string.IsNullOrEmpty(path)) + { + string myDocument = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); + path = Path.GetFullPath(Path.Combine(myDocument, "Hutao", "Metadata")); + } + + return path; + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Context/FileSystem/Location/MyDocument.cs b/src/Snap.Hutao/Snap.Hutao/Context/FileSystem/Location/MyDocument.cs index e0678e46..1dfa1a24 100644 --- a/src/Snap.Hutao/Snap.Hutao/Context/FileSystem/Location/MyDocument.cs +++ b/src/Snap.Hutao/Snap.Hutao/Context/FileSystem/Location/MyDocument.cs @@ -24,4 +24,4 @@ public class MyDocument : IFileSystemLocation return path; } -} +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Context/FileSystem/MetadataContext.cs b/src/Snap.Hutao/Snap.Hutao/Context/FileSystem/MetadataContext.cs new file mode 100644 index 00000000..f50a29ca --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Context/FileSystem/MetadataContext.cs @@ -0,0 +1,19 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Context.FileSystem.Location; + +namespace Snap.Hutao.Context.FileSystem; + +/// +/// 元数据上下文 +/// +[Injection(InjectAs.Transient)] +internal class MetadataContext : FileSystemContext +{ + /// + public MetadataContext(Metadata metadata) + : base(metadata) + { + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Cancellable/CancellablePage.cs b/src/Snap.Hutao/Snap.Hutao/Control/Cancellable/CancellablePage.cs index 89f43683..82a8d272 100644 --- a/src/Snap.Hutao/Snap.Hutao/Control/Cancellable/CancellablePage.cs +++ b/src/Snap.Hutao/Snap.Hutao/Control/Cancellable/CancellablePage.cs @@ -17,9 +17,11 @@ public class CancellablePage : Page /// /// 初始化 /// - /// 视图模型 - public void Initialize(ISupportCancellation viewModel) + /// 视图模型类型 + public void InitializeWith() + where TViewModel : class, ISupportCancellation { + ISupportCancellation viewModel = Ioc.Default.GetRequiredService(); viewModel.CancellationToken = viewLoadingConcellationTokenSource.Token; DataContext = viewModel; } diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.cs b/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.cs new file mode 100644 index 00000000..8e6dcbe1 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.cs @@ -0,0 +1,57 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using CommunityToolkit.WinUI.UI; +using CommunityToolkit.WinUI.UI.Controls; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Media.Imaging; +using Snap.Hutao.Extension; + +namespace Snap.Hutao.Control.Image; + +/// +/// 缓存图像 +/// +public class CachedImage : ImageEx +{ + static CachedImage() + { + ImageCache.Instance.CacheDuration = Timeout.InfiniteTimeSpan; + ImageCache.Instance.RetryCount = 3; + } + + /// + /// 构造一个新的缓存图像 + /// + public CachedImage() + { + IsCacheEnabled = true; + EnableLazyLoading = true; + } + + /// + protected override async Task ProvideCachedResourceAsync(Uri imageUri, CancellationToken token) + { + BitmapImage image; + try + { + image = await ImageCache.Instance.GetFromCacheAsync(imageUri, true, token); + } + catch + { + // maybe the image is corrupted remove it and re-download + await ImageCache.Instance.RemoveAsync(imageUri.Enumerate()); + image = await ImageCache.Instance.GetFromCacheAsync(imageUri, false, token); + } + + // check token state to determine whether the operation should be canceled. + if (token.IsCancellationRequested) + { + throw new TaskCanceledException("Image source has changed."); + } + else + { + return Must.NotNull(image); + } + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Abstraction/ISupportAsyncInitialization.cs b/src/Snap.Hutao/Snap.Hutao/Core/Abstraction/ISupportAsyncInitialization.cs index d89bba52..3758457d 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Abstraction/ISupportAsyncInitialization.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Abstraction/ISupportAsyncInitialization.cs @@ -16,7 +16,7 @@ internal interface ISupportAsyncInitialization /// /// 异步初始化 /// - /// 取消令牌 + /// 取消令牌 /// 初始化任务 - Task InitializeAsync(CancellationToken cancellationToken = default); + ValueTask InitializeAsync(CancellationToken token = default); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/Annotation/InjectionAttribute.cs b/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/Annotation/InjectionAttribute.cs index 0a6a4dd6..ffc1cbbb 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/Annotation/InjectionAttribute.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/Annotation/InjectionAttribute.cs @@ -5,6 +5,7 @@ namespace Snap.Hutao.Core.DependencyInjection.Annotation; /// /// 指示被标注的类型可注入 +/// 由源生成器生成注入代码 /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] public class InjectionAttribute : Attribute @@ -15,27 +16,14 @@ public class InjectionAttribute : Attribute /// 指示注入方法 public InjectionAttribute(InjectAs injectAs) { - InjectAs = injectAs; } /// /// 指示该类将注入为带有接口实现的类 /// /// 指示注入方法 - /// 实现的接口类型 - public InjectionAttribute(InjectAs injectAs, Type impl) + /// 实现的接口类型 + public InjectionAttribute(InjectAs injectAs, Type interfaceType) { - InterfaceType = impl; - InjectAs = injectAs; } - - /// - /// 注入类型 - /// - public InjectAs InjectAs { get; } - - /// - /// 该类实现的接口类型 - /// - public Type? InterfaceType { get; } } diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/DateTimeOffsetConverter.cs b/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/DateTimeOffsetConverter.cs index 595e5d6e..5d7c2a5f 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/DateTimeOffsetConverter.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/DateTimeOffsetConverter.cs @@ -1,7 +1,6 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using System.Text.Json; using System.Text.Json.Serialization; namespace Snap.Hutao.Core.Json.Converter; diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/SeparatorCommaInt32EnumerableConverter.cs b/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/SeparatorCommaInt32EnumerableConverter.cs index 6c2cd69b..e23a3299 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/SeparatorCommaInt32EnumerableConverter.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/SeparatorCommaInt32EnumerableConverter.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; -using System.Text.Json; using System.Text.Json.Serialization; namespace Snap.Hutao.Core.Json.Converter; diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/StringEnumKeyDictionaryConverter.cs b/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/StringEnumKeyDictionaryConverter.cs new file mode 100644 index 00000000..f9d93129 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/StringEnumKeyDictionaryConverter.cs @@ -0,0 +1,131 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using System.Collections.Generic; +using System.Reflection; +using System.Text.Json.Serialization; + +namespace Snap.Hutao.Core.Json.Converter; + +/// +/// Json字典转换器 +/// +/// 键的类型 +public class StringEnumKeyDictionaryConverter : JsonConverterFactory +{ + /// + public override bool CanConvert(Type typeToConvert) + { + if (!typeToConvert.IsGenericType) + { + return false; + } + + if (typeToConvert.GetGenericTypeDefinition() != typeof(Dictionary<,>)) + { + return false; + } + + return typeToConvert.GetGenericArguments()[0].IsEnum; + } + + /// + public override JsonConverter CreateConverter(Type type, JsonSerializerOptions options) + { + Type keyType = type.GetGenericArguments()[0]; + Type valueType = type.GetGenericArguments()[1]; + + Type innerConverterType = typeof(StringEnumDictionaryConverterInner<,>).MakeGenericType(keyType, valueType); + JsonConverter converter = (JsonConverter)Activator.CreateInstance(innerConverterType, BindingFlags.Instance | BindingFlags.Public, null, new object[] { options }, null)!; + return converter; + } + + private class StringEnumDictionaryConverterInner : JsonConverter> + where TKey : struct, Enum + { + private readonly JsonConverter? valueConverter; + private readonly Type keyType; + private readonly Type valueType; + + public StringEnumDictionaryConverterInner(JsonSerializerOptions options) + { + valueConverter = (JsonConverter)options.GetConverter(typeof(TValue)); + + // Cache the key and value types. + keyType = typeof(TKey); + valueType = typeof(TValue); + } + + public override IDictionary Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException(); + } + + Dictionary dictionary = new(); + + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + return dictionary; + } + + // Get the key. + if (reader.TokenType != JsonTokenType.PropertyName) + { + throw new JsonException(); + } + + string? propertyName = reader.GetString(); + + if (!Enum.TryParse(propertyName, out TKey key)) + { + throw new JsonException($"Unable to convert \"{propertyName}\" to Enum \"{keyType}\"."); + } + + // Get the value. + TValue value; + if (valueConverter != null) + { + reader.Read(); + value = valueConverter.Read(ref reader, valueType, options)!; + } + else + { + value = JsonSerializer.Deserialize(ref reader, options)!; + } + + // Add to dictionary. + dictionary.Add(key, value); + } + + throw new JsonException(); + } + + public override void Write(Utf8JsonWriter writer, IDictionary dictionary, JsonSerializerOptions options) + { + writer.WriteStartObject(); + + foreach ((TKey key, TValue value) in dictionary) + { + string? propertyName = key.ToString(); + string? convertedName = options.PropertyNamingPolicy?.ConvertName(propertyName) ?? propertyName; + + writer.WritePropertyName(convertedName); + + if (valueConverter != null) + { + valueConverter.Write(writer, value, options); + } + else + { + JsonSerializer.Serialize(writer, value, options); + } + } + + writer.WriteEndObject(); + } + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Property.cs b/src/Snap.Hutao/Snap.Hutao/Core/Property.cs index c7070b2c..218acaff 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Property.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Property.cs @@ -19,7 +19,7 @@ internal static class Property /// 注册的依赖属性 public static DependencyProperty Depend(string name) { - return DependencyProperty.Register(name, typeof(TProperty), typeof(TOwner), new PropertyMetadata(null)); + return DependencyProperty.Register(name, typeof(TProperty), typeof(TOwner), new(default(TProperty))); } /// @@ -31,7 +31,7 @@ internal static class Property /// 注册的依赖属性 public static DependencyProperty Depend(string name, TProperty defaultValue) { - return DependencyProperty.Register(name, typeof(TProperty), typeof(TOwner), new PropertyMetadata(defaultValue)); + return DependencyProperty.Register(name, typeof(TProperty), typeof(TOwner), new(defaultValue)); } /// @@ -44,7 +44,7 @@ internal static class Property /// 注册的依赖属性 public static DependencyProperty Depend(string name, TProperty defaultValue, PropertyChangedCallback callback) { - return DependencyProperty.Register(name, typeof(TProperty), typeof(TOwner), new PropertyMetadata(defaultValue, callback)); + return DependencyProperty.Register(name, typeof(TProperty), typeof(TOwner), new(defaultValue, callback)); } /// @@ -55,7 +55,7 @@ internal static class Property /// 注册的附加属性 public static DependencyProperty Attach(string name) { - return DependencyProperty.RegisterAttached(name, typeof(TProperty), typeof(TOwner), new PropertyMetadata(null)); + return DependencyProperty.RegisterAttached(name, typeof(TProperty), typeof(TOwner), new(default(TProperty))); } /// @@ -67,7 +67,7 @@ internal static class Property /// 注册的附加属性 public static DependencyProperty Attach(string name, TProperty defaultValue) { - return DependencyProperty.RegisterAttached(name, typeof(TProperty), typeof(TOwner), new PropertyMetadata(defaultValue)); + return DependencyProperty.RegisterAttached(name, typeof(TProperty), typeof(TOwner), new(defaultValue)); } /// @@ -79,6 +79,6 @@ internal static class Property /// 注册的附加属性 public static DependencyProperty Attach(string name, PropertyChangedCallback callback) { - return DependencyProperty.RegisterAttached(name, typeof(TProperty), typeof(TOwner), new PropertyMetadata(callback)); + return DependencyProperty.RegisterAttached(name, typeof(TProperty), typeof(TOwner), new(callback)); } } diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Setting/LocalSetting.cs b/src/Snap.Hutao/Snap.Hutao/Core/Setting/LocalSetting.cs index 553e7997..c939b04d 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Setting/LocalSetting.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Setting/LocalSetting.cs @@ -12,7 +12,7 @@ namespace Snap.Hutao.Core.Setting; internal static class LocalSetting { /// - /// 由于 没有启用 nullable, + /// 由于 没有 nullable context, /// 在处理引用类型时需要格外小心 /// 将值类型的操作与引用类型区分开,可以提升一定的性能 /// diff --git a/src/Snap.Hutao/Snap.Hutao/Factory/AsyncRelayCommandFactory.cs b/src/Snap.Hutao/Snap.Hutao/Factory/AsyncRelayCommandFactory.cs index 90b1472d..99d5393b 100644 --- a/src/Snap.Hutao/Snap.Hutao/Factory/AsyncRelayCommandFactory.cs +++ b/src/Snap.Hutao/Snap.Hutao/Factory/AsyncRelayCommandFactory.cs @@ -92,8 +92,9 @@ internal class AsyncRelayCommandFactory : IAsyncRelayCommandFactory { if (asyncRelayCommand.ExecutionTask?.Exception is AggregateException exception) { - logger.LogError(exception, "异步命令发生了错误"); - Crashes.TrackError(exception); + Exception baseException = exception.GetBaseException(); + logger.LogError(baseException, "异步命令发生了错误"); + Crashes.TrackError(baseException); } } } diff --git a/src/Snap.Hutao/Snap.Hutao/GlobalUsing.cs b/src/Snap.Hutao/Snap.Hutao/GlobalUsing.cs index d8cc82de..174fead5 100644 --- a/src/Snap.Hutao/Snap.Hutao/GlobalUsing.cs +++ b/src/Snap.Hutao/Snap.Hutao/GlobalUsing.cs @@ -10,6 +10,7 @@ global using Snap.Hutao.Core.Validation; global using System; global using System.ComponentModel; global using System.Diagnostics.CodeAnalysis; +global using System.Text.Json; global using System.Threading; global using System.Threading.Tasks; -global using System.Windows.Input; +global using System.Windows.Input; \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/IocConfiguration.cs b/src/Snap.Hutao/Snap.Hutao/IocConfiguration.cs index 0cd30cd7..1c3eb592 100644 --- a/src/Snap.Hutao/Snap.Hutao/IocConfiguration.cs +++ b/src/Snap.Hutao/Snap.Hutao/IocConfiguration.cs @@ -7,7 +7,7 @@ using Snap.Hutao.Context.Database; using Snap.Hutao.Context.FileSystem; using System.Diagnostics; using System.Linq; -using System.Text.Json; +using System.Text.Encodings.Web; using System.Text.Json.Serialization; namespace Snap.Hutao; @@ -22,20 +22,20 @@ internal static class IocConfiguration /// /// 集合 /// 可继续操作的集合 - public static IServiceCollection AddDefaultJsonSerializerOptions(this IServiceCollection services) + public static IServiceCollection AddJsonSerializerOptions(this IServiceCollection services) { - // default json options, global configuration return services .AddSingleton(new JsonSerializerOptions() { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, PropertyNameCaseInsensitive = true, WriteIndented = true, }); } /// - /// 添加数据库 + /// 添加专用数据库 /// /// 集合 /// 可继续操作的集合 @@ -48,13 +48,12 @@ internal static class IocConfiguration string sqlConnectionString = $"Data Source={dbFile}"; // temporarily create a context - using (AppDbContext context = AppDbContext.CreateFrom(sqlConnectionString)) + using (AppDbContext context = AppDbContext.Create(sqlConnectionString)) { if (context.Database.GetPendingMigrations().Any()) { - Debug.WriteLine("Migrate started"); + Debug.WriteLine("Performing Migrations"); context.Database.Migrate(); - Debug.WriteLine("Migrate completed"); } } diff --git a/src/Snap.Hutao/Snap.Hutao/IocHttpClientConfiguration.cs b/src/Snap.Hutao/Snap.Hutao/IocHttpClientConfiguration.cs index bdb7320e..436cf0e9 100644 --- a/src/Snap.Hutao/Snap.Hutao/IocHttpClientConfiguration.cs +++ b/src/Snap.Hutao/Snap.Hutao/IocHttpClientConfiguration.cs @@ -2,6 +2,8 @@ // Licensed under the MIT license. using Microsoft.Extensions.DependencyInjection; +using Snap.Hutao.Service.Metadata; +using Snap.Hutao.Web.Enka; using Snap.Hutao.Web.Hoyolab.Bbs.User; using Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement; using Snap.Hutao.Web.Hoyolab.Takumi.Binding; @@ -25,10 +27,16 @@ internal static class IocHttpClientConfiguration /// 可继续操作的集合 public static IServiceCollection AddHttpClients(this IServiceCollection services) { + // services + services.AddHttpClient(DefaultConfiguration); + + // normal clients services.AddHttpClient(DefaultConfiguration); services.AddHttpClient(DefaultConfiguration); services.AddHttpClient(DefaultConfiguration); + services.AddHttpClient(DefaultConfiguration); + // x-rpc clients services.AddHttpClient(XRpcConfiguration); services.AddHttpClient(XRpcConfiguration); diff --git a/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml b/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml index 879a8114..00f8412f 100644 --- a/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml +++ b/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml @@ -5,9 +5,10 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:view="using:Snap.Hutao.View" - mc:Ignorable="d"> + mc:Ignorable="d" + Closed="MainWindowClosed"> - + diff --git a/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml.cs b/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml.cs index bf7007ea..ab7740ef 100644 --- a/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml.cs +++ b/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml.cs @@ -12,24 +12,25 @@ namespace Snap.Hutao; [Injection(InjectAs.Singleton)] public sealed partial class MainWindow : Window { + private readonly AppDbContext appDbContext; + /// /// 构造一个新的主窗体 /// - public MainWindow() + /// 数据库上下文 + public MainWindow(AppDbContext appDbContext) { + this.appDbContext = appDbContext; + InitializeComponent(); ExtendsContentIntoTitleBar = true; SetTitleBar(TitleBarView.DragableArea); - - Closed += MainWindowClosed; } private void MainWindowClosed(object sender, WindowEventArgs args) { // save datebase - AppDbContext appDbContext = Ioc.Default.GetRequiredService(); int changes = appDbContext.SaveChanges(); - Verify.Operation(changes == 0, "存在可避免的未经处理的数据库更改"); } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/EquipType.cs b/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/EquipType.cs new file mode 100644 index 00000000..515c2ffd --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/EquipType.cs @@ -0,0 +1,46 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Model.Intrinsic; + +/// +/// 装备类型 +/// https://github.com/Grasscutters/Grasscutter/blob/development/src/main/java/emu/grasscutter/game/inventory/EquipType.java +/// +public enum EquipType +{ + /// + /// 无 + /// + EQUIP_NONE = 0, + + /// + /// 花 + /// + EQUIP_BRACER = 1, + + /// + /// 羽毛 + /// + EQUIP_NECKLACE = 2, + + /// + /// 沙 + /// + EQUIP_SHOES = 3, + + /// + /// 杯 + /// + EQUIP_RING = 4, + + /// + /// 头 + /// + EQUIP_DRESS = 5, + + /// + /// 武器 + /// + EQUIP_WEAPON = 6, +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/FightProperty.cs b/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/FightProperty.cs new file mode 100644 index 00000000..1a944118 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/FightProperty.cs @@ -0,0 +1,491 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Model.Intrinsic; + +/// +/// 战斗属性 +/// https://github.com/Grasscutters/Grasscutter/tree/development/src/main/java/emu/grasscutter/game/props/FightProperty.java +/// +public enum FightProperty +{ + /// + /// 空 + /// + FIGHT_PROP_NONE = 0, + + /// + /// 基础生命值 + /// + FIGHT_PROP_BASE_HP = 1, + + /// + /// 生命值加成 + /// + FIGHT_PROP_HP = 2, + + /// + /// 生命值加成百分比 + /// + FIGHT_PROP_HP_PERCENT = 3, + + /// + /// 基础攻击力 + /// + FIGHT_PROP_BASE_ATTACK = 4, + + /// + /// 攻击力加成 + /// + FIGHT_PROP_ATTACK = 5, + + /// + /// 攻击力百分比 + /// + FIGHT_PROP_ATTACK_PERCENT = 6, + + /// + /// 基础防御力 + /// + FIGHT_PROP_BASE_DEFENSE = 7, + + /// + /// 防御力加成 + /// + FIGHT_PROP_DEFENSE = 8, + + /// + /// 防御力百分比 + /// + FIGHT_PROP_DEFENSE_PERCENT = 9, + + /// + /// 基础速度 + /// + FIGHT_PROP_BASE_SPEED = 10, + + /// + /// 速度加成 + /// + FIGHT_PROP_SPEED_PERCENT = 11, + + /// + /// ? + /// + FIGHT_PROP_HP_MP_PERCENT = 12, + + /// + /// ? + /// + FIGHT_PROP_ATTACK_MP_PERCENT = 13, + + /// + /// 暴击率 + /// + FIGHT_PROP_CRITICAL = 20, + + /// + /// 抗暴击率 + /// + FIGHT_PROP_ANTI_CRITICAL = 21, + + /// + /// 暴击伤害 + /// + FIGHT_PROP_CRITICAL_HURT = 22, + + /// + /// 元素充能效率 + /// + FIGHT_PROP_CHARGE_EFFICIENCY = 23, + + /// + /// 伤害加成 + /// + FIGHT_PROP_ADD_HURT = 24, + + /// + /// 抗性提升 + /// + FIGHT_PROP_SUB_HURT = 25, + + /// + /// 治疗提升 + /// + FIGHT_PROP_HEAL_ADD = 26, + + /// + /// 受治疗提升 + /// + FIGHT_PROP_HEALED_ADD = 27, + + /// + /// 元素精通 + /// + FIGHT_PROP_ELEMENT_MASTERY = 28, + + /// + /// 物理抗性提升 + /// + FIGHT_PROP_PHYSICAL_SUB_HURT = 29, + + /// + /// 物理伤害加成 + /// + FIGHT_PROP_PHYSICAL_ADD_HURT = 30, + + /// + /// 无视防御力百分比 + /// + FIGHT_PROP_DEFENCE_IGNORE_RATIO = 31, + + /// + /// 防御力降低 + /// + FIGHT_PROP_DEFENCE_IGNORE_DELTA = 32, + + /// + /// 火元素伤害加成 + /// + FIGHT_PROP_FIRE_ADD_HURT = 40, + + /// + /// 雷元素伤害加成 + /// + FIGHT_PROP_ELEC_ADD_HURT = 41, + + /// + /// 水元素伤害加成 + /// + FIGHT_PROP_WATER_ADD_HURT = 42, + + /// + /// 草元素伤害加成 + /// + FIGHT_PROP_GRASS_ADD_HURT = 43, + + /// + /// 风元素伤害加成 + /// + FIGHT_PROP_WIND_ADD_HURT = 44, + + /// + /// 岩元素伤害加成 + /// + FIGHT_PROP_ROCK_ADD_HURT = 45, + + /// + /// 冰元素伤害加成 + /// + FIGHT_PROP_ICE_ADD_HURT = 46, + + /// + /// 弱点伤害加成 + /// + FIGHT_PROP_HIT_HEAD_ADD_HURT = 47, + + /// + /// 火元素抗性提升 + /// + FIGHT_PROP_FIRE_SUB_HURT = 50, + + /// + /// 雷元素抗性提升 + /// + FIGHT_PROP_ELEC_SUB_HURT = 51, + + /// + /// 雷元素抗性提升 + /// + FIGHT_PROP_WATER_SUB_HURT = 52, + + /// + /// 草元素抗性提升 + /// + FIGHT_PROP_GRASS_SUB_HURT = 53, + + /// + /// 风元素抗性提升 + /// + FIGHT_PROP_WIND_SUB_HURT = 54, + + /// + /// 岩元素抗性提升 + /// + FIGHT_PROP_ROCK_SUB_HURT = 55, + + /// + /// 冰元素抗性提升 + /// + FIGHT_PROP_ICE_SUB_HURT = 56, + + /// + /// ? + /// + FIGHT_PROP_EFFECT_HIT = 60, + + /// + /// ? + /// + FIGHT_PROP_EFFECT_RESIST = 61, + + /// + /// 冻结抗性 + /// + FIGHT_PROP_FREEZE_RESIST = 62, + + /// + /// 迟缓抗性 + /// + FIGHT_PROP_TORPOR_RESIST = 63, + + /// + /// 眩晕抗性 + /// + FIGHT_PROP_DIZZY_RESIST = 64, + + /// + /// 冻结缩减 + /// + FIGHT_PROP_FREEZE_SHORTEN = 65, + + /// + /// 迟缓缩减 + /// + FIGHT_PROP_TORPOR_SHORTEN = 66, + + /// + /// 眩晕缩减 + /// + FIGHT_PROP_DIZZY_SHORTEN = 67, + + /// + /// 火元素爆发能量 + /// + FIGHT_PROP_MAX_FIRE_ENERGY = 70, + + /// + /// 雷元素爆发能量 + /// + FIGHT_PROP_MAX_ELEC_ENERGY = 71, + + /// + /// 水元素爆发能量 + /// + FIGHT_PROP_MAX_WATER_ENERGY = 72, + + /// + /// 草元素爆发能量 + /// + FIGHT_PROP_MAX_GRASS_ENERGY = 73, + + /// + /// 风元素爆发能量 + /// + FIGHT_PROP_MAX_WIND_ENERGY = 74, + + /// + /// 冰元素爆发能量 + /// + FIGHT_PROP_MAX_ICE_ENERGY = 75, + + /// + /// 岩元素爆发能量 + /// + FIGHT_PROP_MAX_ROCK_ENERGY = 76, + + /// + /// 技能冷却缩减 + /// + FIGHT_PROP_SKILL_CD_MINUS_RATIO = 80, + + /// + /// 护盾强效 + /// + FIGHT_PROP_SHIELD_COST_MINUS_RATIO = 81, + + /// + /// 火元素爆发当前能量 + /// + FIGHT_PROP_CUR_FIRE_ENERGY = 1000, + + /// + /// 雷元素爆发当前能量 + /// + FIGHT_PROP_CUR_ELEC_ENERGY = 1001, + + /// + /// 水元素爆发当前能量 + /// + FIGHT_PROP_CUR_WATER_ENERGY = 1002, + + /// + /// 草元素爆发当前能量 + /// + FIGHT_PROP_CUR_GRASS_ENERGY = 1003, + + /// + /// 风元素爆发当前能量 + /// + FIGHT_PROP_CUR_WIND_ENERGY = 1004, + + /// + /// 冰元素爆发当前能量 + /// + FIGHT_PROP_CUR_ICE_ENERGY = 1005, + + /// + /// 岩元素爆发当前能量 + /// + FIGHT_PROP_CUR_ROCK_ENERGY = 1006, + + /// + /// 当前生命值 + /// + FIGHT_PROP_CUR_HP = 1010, + + /// + /// 最大生命值 + /// + FIGHT_PROP_MAX_HP = 2000, + + /// + /// 当前攻击力 + /// + FIGHT_PROP_CUR_ATTACK = 2001, + + /// + /// 当前防御力 + /// + FIGHT_PROP_CUR_DEFENSE = 2002, + + /// + /// 当前速度 + /// + FIGHT_PROP_CUR_SPEED = 2003, + + /// + /// 总攻击力 + /// + FIGHT_PROP_NONEXTRA_ATTACK = 3000, + + /// + /// 总防御力 + /// + FIGHT_PROP_NONEXTRA_DEFENSE = 3001, + + /// + /// 总暴击率 + /// + FIGHT_PROP_NONEXTRA_CRITICAL = 3002, + + /// + /// 总抗暴击率 + /// + FIGHT_PROP_NONEXTRA_ANTI_CRITICAL = 3003, + + /// + /// 总暴击伤害 + /// + FIGHT_PROP_NONEXTRA_CRITICAL_HURT = 3004, + + /// + /// 总元素充能效率 + /// + FIGHT_PROP_NONEXTRA_CHARGE_EFFICIENCY = 3005, + + /// + /// 元素精通 + /// + FIGHT_PROP_NONEXTRA_ELEMENT_MASTERY = 3006, + + /// + /// 总物理抗性提升 + /// + FIGHT_PROP_NONEXTRA_PHYSICAL_SUB_HURT = 3007, + + /// + /// 总火元素伤害提升 + /// + FIGHT_PROP_NONEXTRA_FIRE_ADD_HURT = 3008, + + /// + /// 总雷元素伤害加成 + /// + FIGHT_PROP_NONEXTRA_ELEC_ADD_HURT = 3009, + + /// + /// 总水元素伤害加成 + /// + FIGHT_PROP_NONEXTRA_WATER_ADD_HURT = 3010, + + /// + /// 总草元素伤害加成 + /// + FIGHT_PROP_NONEXTRA_GRASS_ADD_HURT = 3011, + + /// + /// 总风元素伤害加成 + /// + FIGHT_PROP_NONEXTRA_WIND_ADD_HURT = 3012, + + /// + /// 总岩元素伤害加成 + /// + FIGHT_PROP_NONEXTRA_ROCK_ADD_HURT = 3013, + + /// + /// 总冰元素伤害加成 + /// + FIGHT_PROP_NONEXTRA_ICE_ADD_HURT = 3014, + + /// + /// 总火元素抗性提升 + /// + FIGHT_PROP_NONEXTRA_FIRE_SUB_HURT = 3015, + + /// + /// 总雷元素抗性提升 + /// + FIGHT_PROP_NONEXTRA_ELEC_SUB_HURT = 3016, + + /// + /// 总水元素抗性提升 + /// + FIGHT_PROP_NONEXTRA_WATER_SUB_HURT = 3017, + + /// + /// 总草元素抗性提升 + /// + FIGHT_PROP_NONEXTRA_GRASS_SUB_HURT = 3018, + + /// + /// 总风元素抗性提升 + /// + FIGHT_PROP_NONEXTRA_WIND_SUB_HURT = 3019, + + /// + /// 总岩元素抗性提升 + /// + FIGHT_PROP_NONEXTRA_ROCK_SUB_HURT = 3020, + + /// + /// 总冰元素抗性提升 + /// + FIGHT_PROP_NONEXTRA_ICE_SUB_HURT = 3021, + + /// + /// 总冷却缩减 + /// + FIGHT_PROP_NONEXTRA_SKILL_CD_MINUS_RATIO = 3022, + + /// + /// 总护盾强效 + /// + FIGHT_PROP_NONEXTRA_SHIELD_COST_MINUS_RATIO = 3023, + + /// + /// 总物理伤害加成 + /// + FIGHT_PROP_NONEXTRA_PHYSICAL_ADD_HURT = 3024, +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Avatar/ItemQuality.cs b/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/ItemQuality.cs similarity index 93% rename from src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Avatar/ItemQuality.cs rename to src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/ItemQuality.cs index b6af18e7..f6b82fd3 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Avatar/ItemQuality.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/ItemQuality.cs @@ -1,7 +1,7 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Avatar; +namespace Snap.Hutao.Model.Intrinsic; /// /// 稀有度 diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/ItemType.cs b/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/ItemType.cs new file mode 100644 index 00000000..a415446c --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/ItemType.cs @@ -0,0 +1,46 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Model.Intrinsic; + +/// +/// 物品类型 +/// https://github.com/Grasscutters/Grasscutter/tree/development/src/main/java/emu/grasscutter/game/inventory/ItemType.java +/// +public enum ItemType +{ + /// + /// 无 + /// + ITEM_NONE = 0, + + /// + /// 贵重道具 + /// + ITEM_VIRTUAL = 1, + + /// + /// 材料 + /// + ITEM_MATERIAL = 2, + + /// + /// 圣遗物 + /// + ITEM_RELIQUARY = 3, + + /// + /// 武器 + /// + ITEM_WEAPON = 4, + + /// + /// 任务等 + /// + ITEM_DISPLAY = 5, + + /// + /// 家具 + /// + ITEM_FURNITURE = 6, +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Avatar/WeaponType.cs b/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/WeaponType.cs similarity index 96% rename from src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Avatar/WeaponType.cs rename to src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/WeaponType.cs index 3709bd1d..724bef79 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Avatar/WeaponType.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/WeaponType.cs @@ -1,7 +1,7 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Avatar; +namespace Snap.Hutao.Model.Intrinsic; /// /// 武器类型 diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/Achievement.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/Achievement.cs new file mode 100644 index 00000000..1354d463 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/Achievement.cs @@ -0,0 +1,57 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using System.Collections.Generic; + +namespace Snap.Hutao.Model.Metadata.Achievement; + +/// +/// 成就 +/// +public class Achievement +{ + /// + /// Id + /// + public int Id { get; set; } + + /// + /// 分类Id + /// + public int Goal { get; set; } + + /// + /// 排序顺序 + /// + public int Order { get; set; } + + /// + /// 标题 + /// + public string Title { get; set; } = default!; + + /// + /// 描述 + /// + public string Description { get; set; } = default!; + + /// + /// 完成奖励 + /// + public Reward FinishReward { get; set; } = default!; + + /// + /// 总进度 + /// + public int Progress { get; set; } + + /// + /// 触发器 + /// + public IEnumerable? Triggers { get; set; } + + /// + /// 图标 + /// + public string? Icon { get; set; } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/AchievementGoal.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/AchievementGoal.cs new file mode 100644 index 00000000..279827d7 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/AchievementGoal.cs @@ -0,0 +1,30 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Model.Metadata.Achievement; + +/// +/// 成就分类 +/// +public class AchievementGoal +{ + /// + /// 排序顺序 + /// + public int Order { get; set; } + + /// + /// 名称 + /// + public string Name { get; set; } = default!; + + /// + /// 完成奖励 + /// + public Reward? FinishReward { get; set; } + + /// + /// 图标 + /// + public string? Icon { get; set; } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/AchievementTrigger.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/AchievementTrigger.cs new file mode 100644 index 00000000..5178701f --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/AchievementTrigger.cs @@ -0,0 +1,30 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Model.Metadata.Achievement; + +/// +/// 成就触发器 +/// +public class AchievementTrigger +{ + /// + /// 触发器类型 + /// + public AchievementTriggerType Type { get; set; } + + /// + /// Id + /// + public string Id { get; set; } = default!; + + /// + /// 标题 + /// + public string Title { get; set; } = default!; + + /// + /// 描述 + /// + public string Description { get; set; } = default!; +} diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/AchievementTriggerType.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/AchievementTriggerType.cs new file mode 100644 index 00000000..ec1d0dcb --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/AchievementTriggerType.cs @@ -0,0 +1,25 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Model.Metadata.Achievement; + +/// +/// 成就触发器类型 +/// +public enum AchievementTriggerType +{ + /// + /// 任务 + /// + Quest = 1, + + /// + /// 子任务 + /// + SubQuest = 2, + + /// + /// 日常任务 + /// + DailyTask = 3, +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/Reward.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/Reward.cs new file mode 100644 index 00000000..2472f1b6 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/Reward.cs @@ -0,0 +1,20 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Model.Metadata.Achievement; + +/// +/// 奖励 +/// +public class Reward +{ + /// + /// Id + /// + public int Id { get; set; } + + /// + /// 数量 + /// + public int Count { get; set; } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.cs new file mode 100644 index 00000000..e59594ad --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.cs @@ -0,0 +1,83 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Model.Intrinsic; +using System.Collections.Generic; + +namespace Snap.Hutao.Model.Metadata.Avatar; + +/// +/// 角色 +/// +public class Avatar +{ + /// + /// Id + /// + public int Id { get; set; } + + /// + /// 排序号 + /// + public int Sort { get; set; } + + /// + /// 体型 + /// + public string Body { get; set; } = default!; + + /// + /// 正面图标 + /// + public string Icon { get; set; } = default!; + + /// + /// 侧面图标 + /// + public string SideIcon { get; set; } = default!; + + /// + /// 名称 + /// + public string Name { get; set; } = default!; + + /// + /// 描述 + /// + public string Description { get; set; } = default!; + + /// + /// 角色加入游戏时间 + /// + public DateTimeOffset BeginTime { get; set; } + + /// + /// 星级 + /// + public ItemQuality Quality { get; set; } + + /// + /// 武器类型 + /// + public WeaponType Weapon { get; set; } + + /// + /// 属性 + /// + public PropertyInfo Property { get; set; } = default!; + + /// + /// 技能 + /// + public SkillDepot SkillDepot { get; set; } = default!; + + /// + /// 好感信息 + /// + public FetterInfo FetterInfo { get; set; } = default!; + + /// + /// 皮肤 + /// + public IEnumerable Costumes { get; set; } = default!; +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Costume.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Costume.cs new file mode 100644 index 00000000..a434f13b --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Costume.cs @@ -0,0 +1,30 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Model.Metadata.Avatar; + +/// +/// 皮肤 +/// +public class Costume +{ + /// + /// Id + /// + public int Id { get; set; } + + /// + /// 名称 + /// + public string Name { get; set; } = default!; + + /// + /// 描述 + /// + public string Description { get; set; } = default!; + + /// + /// 是否为默认 + /// + public bool IsDefault { get; set; } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/DescParam.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/DescParam.cs new file mode 100644 index 00000000..452982c5 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/DescParam.cs @@ -0,0 +1,22 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using System.Collections.Generic; + +namespace Snap.Hutao.Model.Metadata.Avatar; + +/// +/// 描述与参数 +/// +public class DescParam +{ + /// + /// 描述 + /// + public IEnumerable Descriptions { get; set; } = default!; + + /// + /// 参数 + /// + public IEnumerable> Parameters { get; set; } = default!; +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Fetter.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Fetter.cs new file mode 100644 index 00000000..139ba087 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Fetter.cs @@ -0,0 +1,20 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Model.Metadata.Avatar; + +/// +/// 好感故事 +/// +public class Fetter +{ + /// + /// 标题 + /// + public string Title { get; set; } = default!; + + /// + /// 上下文 + /// + public string Context { get; set; } = default!; +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/FetterInfo.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/FetterInfo.cs new file mode 100644 index 00000000..e7487d0e --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/FetterInfo.cs @@ -0,0 +1,92 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using System.Collections.Generic; + +namespace Snap.Hutao.Model.Metadata.Avatar; + +/// +/// 好感信息 +/// +public class FetterInfo +{ + /// + /// 称号 + /// + public string Title { get; set; } = default!; + + /// + /// 详细 + /// + public string Detail { get; set; } = default!; + + /// + /// 地区 + /// + public string Association { get; set; } = default!; + + /// + /// 属于组织 + /// + public string Native { get; set; } = default!; + + /// + /// 生月 + /// + public int BirthMonth { get; set; } + + /// + /// 生日 + /// + public int BirthDay { get; set; } + + /// + /// 神之眼属性-前 + /// + public string VisionBefore { get; set; } = default!; + + /// + /// 神之眼属性-后 + /// + public string VisionAfter { get; set; } = default!; + + /// + /// 命座-前 + /// + public string ConstellationBefore { get; set; } = default!; + + /// + /// 命座-后 + /// + public string ConstellationAfter { get; set; } = default!; + + /// + /// 中文CV + /// + public string CvChinese { get; set; } = default!; + + /// + /// 日语CV + /// + public string CvJapanese { get; set; } = default!; + + /// + /// 英语CV + /// + public string CvEnglish { get; set; } = default!; + + /// + /// 韩语CV + /// + public string CvKorean { get; set; } = default!; + + /// + /// 好感语音 + /// + public IEnumerable Fetters { get; set; } = default!; + + /// + /// 好感故事 + /// + public IEnumerable FetterStories { get; set; } = default!; +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/ProudableSkill.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/ProudableSkill.cs new file mode 100644 index 00000000..b24fdcea --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/ProudableSkill.cs @@ -0,0 +1,21 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Model.Metadata.Avatar; + +/// +/// 技能信息 +/// +public class ProudableSkill : SkillBase +{ + + /// + /// 组Id + /// + public int GroupId { get; set; } + + /// + /// 提升属性 + /// + public DescParam Proud { get; set; } = default!; +} diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/SkillBase.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/SkillBase.cs new file mode 100644 index 00000000..57575ec3 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/SkillBase.cs @@ -0,0 +1,31 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Model.Metadata.Avatar; + +/// +/// 技能基础 +/// 命座 +/// +public class SkillBase +{ + /// + /// Id + /// + public int Id { get; set; } + + /// + /// 名称 + /// + public string Name { get; set; } = default!; + + /// + /// 描述 + /// + public string Description { get; set; } = default!; + + /// + /// 图标 + /// + public string Icon { get; set; } = default!; +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/SkillDepot.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/SkillDepot.cs new file mode 100644 index 00000000..a61e6604 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/SkillDepot.cs @@ -0,0 +1,32 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using System.Collections.Generic; + +namespace Snap.Hutao.Model.Metadata.Avatar; + +/// +/// 技能组 +/// +public class SkillDepot +{ + /// + /// 技能天赋 + /// + public IEnumerable Skills { get; set; } = default!; + + /// + /// 大招 + /// + public ProudableSkill EnergySkill { get; set; } = default!; + + /// + /// 固有天赋 + /// + public IEnumerable Inherents { get; set; } = default!; + + /// + /// 命之座 + /// + public IEnumerable Talents { get; set; } = default!; +} diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/LevelParam.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/LevelParam.cs new file mode 100644 index 00000000..ad14e546 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/LevelParam.cs @@ -0,0 +1,23 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using System.Collections.Generic; + +namespace Snap.Hutao.Model.Metadata; + +/// +/// 等级与参数 +/// +/// 等级的类型 +public class LevelParam +{ + /// + /// 等级 + /// + public TLevel Level { get; set; } = default!; + + /// + /// 参数 + /// + public IEnumerable Parameters { get; set; } = default!; +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/PropertyInfo.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/PropertyInfo.cs new file mode 100644 index 00000000..f4263a7d --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/PropertyInfo.cs @@ -0,0 +1,23 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Model.Intrinsic; +using System.Collections.Generic; + +namespace Snap.Hutao.Model.Metadata; + +/// +/// 属性信息 +/// +public class PropertyInfo +{ + /// + /// 提升的属性 + /// + public IEnumerable Properties { get; set; } = default!; + + /// + /// 参数 + /// + public IEnumerable> Parameters { get; set; } = default!; +} diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Reliquary/Reliquary.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Reliquary/Reliquary.cs new file mode 100644 index 00000000..42310af5 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Reliquary/Reliquary.cs @@ -0,0 +1,53 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Model.Intrinsic; +using System.Collections.Generic; + +namespace Snap.Hutao.Model.Metadata.Reliquary; + +/// +/// 圣遗物信息 +/// +public class Reliquary +{ + /// + /// 表示同种类的Id + /// + public IEnumerable Ids { get; set; } = default!; + + /// + /// 允许出现的等级 + /// + public IEnumerable RankLevels { get; set; } = default!; + + /// + /// 套装Id + /// + public int SetId { get; set; } + + /// + /// 装备类型 + /// + public EquipType EquipType { get; set; } = default!; + + /// + /// 物品类型 + /// + public ItemType ItemType { get; set; } = default!; + + /// + /// 名称 + /// + public string Name { get; set; } = default!; + + /// + /// 描述 + /// + public string Description { get; set; } = default!; + + /// + /// 图标 + /// + public string Icon { get; set; } = default!; +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Reliquary/ReliquaryAffix.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Reliquary/ReliquaryAffix.cs new file mode 100644 index 00000000..1cd4779f --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Reliquary/ReliquaryAffix.cs @@ -0,0 +1,15 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Model.Metadata.Reliquary; + +/// +/// 圣遗物突破属性 +/// +public class ReliquaryAffix : ReliquaryAffixBase +{ + /// + /// 值 + /// + public double Value { get; set; } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Reliquary/ReliquaryAffixBase.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Reliquary/ReliquaryAffixBase.cs new file mode 100644 index 00000000..d4dd9c22 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Reliquary/ReliquaryAffixBase.cs @@ -0,0 +1,22 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Model.Intrinsic; + +namespace Snap.Hutao.Model.Metadata.Reliquary; + +/// +/// 圣遗物主属性 +/// +public class ReliquaryAffixBase +{ + /// + /// Id + /// + public int Id { get; set; } + + /// + /// 战斗属性 + /// + public FightProperty Type { get; set; } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Reliquary/ReliquarySet.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Reliquary/ReliquarySet.cs new file mode 100644 index 00000000..743e55e7 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Reliquary/ReliquarySet.cs @@ -0,0 +1,27 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using System.Collections.Generic; + +namespace Snap.Hutao.Model.Metadata.Reliquary; + +/// +/// 圣遗物套装 +/// +public class ReliquarySet +{ + /// + /// 套装Id + /// + public int SetId { get; set; } = default!; + + /// + /// 需要的数量 + /// + public IEnumerable NeedNumber { get; set; } = default!; + + /// + /// 描述 + /// + public IEnumerable Descriptions { get; set; } = default!; +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.cs new file mode 100644 index 00000000..90371b71 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.cs @@ -0,0 +1,52 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Model.Intrinsic; + +namespace Snap.Hutao.Model.Metadata.Weapon; + +/// +/// 武器 +/// +public class Weapon +{ + /// + /// Id + /// + public int Id { get; set; } + + /// + /// 武器类型 + /// + public WeaponType WeaponType { get; set; } + + /// + /// 等级 + /// + public ItemQuality RankLevel { get; set; } + + /// + /// 名称 + /// + public string Name { get; set; } = default!; + + /// + /// 描述 + /// + public string Description { get; set; } = default!; + + /// + /// 图标 + /// + public string Icon { get; set; } = default!; + + /// + /// 觉醒图标 + /// + public string AwakenIcon { get; set; } = default!; + + /// + /// 属性 + /// + public PropertyInfo Property { get; set; } = default!; +} diff --git a/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest b/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest index 34781fb9..04580171 100644 --- a/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest +++ b/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest @@ -9,7 +9,7 @@ + Version="1.0.6.0" /> 胡桃 @@ -32,13 +32,20 @@ EntryPoint="$targetentrypoint$"> + + + + 胡桃 + + + diff --git a/src/Snap.Hutao/Snap.Hutao/Program.cs b/src/Snap.Hutao/Snap.Hutao/Program.cs index a61fd43f..8fd06c9a 100644 --- a/src/Snap.Hutao/Snap.Hutao/Program.cs +++ b/src/Snap.Hutao/Snap.Hutao/Program.cs @@ -20,8 +20,8 @@ public static class Program private static void Main(string[] args) { XamlCheckProcessRequirements(); - ComWrappersSupport.InitializeComWrappers(); + Application.Start(p => { DispatcherQueueSynchronizationContext context = new(DispatcherQueue.GetForCurrentThread()); diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/IInfoBarService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/IInfoBarService.cs index 74e40a43..86d35622 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/IInfoBarService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/IInfoBarService.cs @@ -45,7 +45,7 @@ public interface IInfoBarService /// /// 消息 /// 关闭延迟 - void Information(string message, int delay = 3000); + void Information(string message, int delay = 5000); /// /// 显示提示信息 @@ -53,7 +53,7 @@ public interface IInfoBarService /// 标题 /// 消息 /// 关闭延迟 - void Information(string title, string message, int delay = 3000); + void Information(string title, string message, int delay = 5000); /// /// 使用指定的 初始化服务 @@ -66,7 +66,7 @@ public interface IInfoBarService /// /// 消息 /// 关闭延迟 - void Success(string message, int delay = 3000); + void Success(string message, int delay = 5000); /// /// 显示成功信息 @@ -74,7 +74,7 @@ public interface IInfoBarService /// 标题 /// 消息 /// 关闭延迟 - void Success(string title, string message, int delay = 3000); + void Success(string title, string message, int delay = 5000); /// /// 显示警告信息 diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/IMetadataService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/IMetadataService.cs new file mode 100644 index 00000000..597ed47b --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/IMetadataService.cs @@ -0,0 +1,79 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Model.Metadata.Achievement; +using Snap.Hutao.Model.Metadata.Avatar; +using Snap.Hutao.Model.Metadata.Reliquary; +using Snap.Hutao.Model.Metadata.Weapon; +using System.Collections.Generic; + +namespace Snap.Hutao.Service.Metadata; + +/// +/// 元数据服务 +/// +internal interface IMetadataService +{ + /// + /// 异步初始化服务,尝试更新元数据 + /// + /// 取消令牌 + /// 初始化是否成功 + ValueTask InitializeAsync(CancellationToken token = default); + + /// + /// 异步获取成就列表 + /// + /// 取消令牌 + /// 成就列表 + ValueTask> GetAchievementsAsync(CancellationToken token = default); + + /// + /// 异步获取成就分类列表 + /// + /// 取消令牌 + /// 成就分类列表 + ValueTask> GetAchievementGoalsAsync(CancellationToken token = default); + + /// + /// 异步获取角色列表 + /// + /// 取消令牌 + /// 角色列表 + ValueTask> GetAvatarsAsync(CancellationToken token = default); + + /// + /// 异步获取圣遗物列表 + /// + /// 取消令牌 + /// 圣遗物列表 + ValueTask> GetReliquariesAsync(CancellationToken token = default); + + /// + /// 异步获取圣遗物强化属性列表 + /// + /// 取消令牌 + /// 圣遗物强化属性列表 + ValueTask> GetReliquaryAffixesAsync(CancellationToken token = default); + + /// + /// 异步获取圣遗物主属性强化属性列表 + /// + /// 取消令牌 + /// 圣遗物强化属性列表 + ValueTask> GetReliquaryMainAffixesAsync(CancellationToken token = default); + + /// + /// 异步获取武器列表 + /// + /// 取消令牌 + /// 武器列表 + ValueTask> GetWeaponsAsync(CancellationToken token = default); + + /// + /// 异步更新元数据 + /// + /// 取消令牌 + /// 更新是否完成 + Task UpdateMetadataAsync(CancellationToken token = default); +} diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AnnouncementService.cs b/src/Snap.Hutao/Snap.Hutao/Service/AnnouncementService.cs index 4f76b449..1f829ce8 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/AnnouncementService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/AnnouncementService.cs @@ -14,7 +14,7 @@ namespace Snap.Hutao.Service; [Injection(InjectAs.Transient, typeof(IAnnouncementService))] internal class AnnouncementService : IAnnouncementService { - private const string CacheKey = $"{nameof(IAnnouncementService)}.Cache.{nameof(AnnouncementWrapper)}"; + private const string CacheKey = $"{nameof(AnnouncementService)}.Cache.{nameof(AnnouncementWrapper)}"; private readonly AnnouncementClient announcementClient; private readonly IMemoryCache memoryCache; diff --git a/src/Snap.Hutao/Snap.Hutao/Service/InfoBarService.cs b/src/Snap.Hutao/Snap.Hutao/Service/InfoBarService.cs index cf3d5db2..718d05da 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/InfoBarService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/InfoBarService.cs @@ -19,25 +19,25 @@ internal class InfoBarService : IInfoBarService } /// - public void Information(string message, int delay = 3000) + public void Information(string message, int delay = 5000) { PrepareInfoBarAndShow(InfoBarSeverity.Informational, null, message, delay); } /// - public void Information(string title, string message, int delay = 3000) + public void Information(string title, string message, int delay = 5000) { PrepareInfoBarAndShow(InfoBarSeverity.Informational, title, message, delay); } /// - public void Success(string message, int delay = 3000) + public void Success(string message, int delay = 5000) { PrepareInfoBarAndShow(InfoBarSeverity.Success, null, message, delay); } /// - public void Success(string title, string message, int delay = 3000) + public void Success(string title, string message, int delay = 5000) { PrepareInfoBarAndShow(InfoBarSeverity.Success, title, message, delay); } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/MetadataService.cs b/src/Snap.Hutao/Snap.Hutao/Service/MetadataService.cs new file mode 100644 index 00000000..08109eae --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/MetadataService.cs @@ -0,0 +1,249 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Microsoft.Extensions.Caching.Memory; +using Snap.Hutao.Context.FileSystem; +using Snap.Hutao.Core.Abstraction; +using Snap.Hutao.Model.Metadata.Achievement; +using Snap.Hutao.Model.Metadata.Avatar; +using Snap.Hutao.Model.Metadata.Reliquary; +using Snap.Hutao.Model.Metadata.Weapon; +using Snap.Hutao.Service.Abstraction; +using System.Collections.Generic; +using System.IO; +using System.Net.Http; +using System.Net.Http.Json; +using System.Security.Cryptography; + +namespace Snap.Hutao.Service.Metadata; + +/// +/// 元数据服务 +/// +[Injection(InjectAs.Singleton, typeof(IMetadataService))] +internal class MetadataService : IMetadataService, ISupportAsyncInitialization +{ + private const string MetaAPIHost = "http://hutao-metadata.snapgenshin.com"; + private const string MetaFileName = "Meta.json"; + private readonly IInfoBarService infoBarService; + private readonly HttpClient httpClient; + private readonly FileSystemContext metadataContext; + private readonly JsonSerializerOptions options; + private readonly ILogger logger; + private readonly IMemoryCache memoryCache; + + private bool isInitialized = false; + + /// + /// 构造一个新的元数据服务 + /// + /// 信息条服务 + /// http客户端 + /// 我的文档上下文 + /// json序列化选项 + /// 日志器 + /// 内存缓存 + public MetadataService( + IInfoBarService infoBarService, + HttpClient httpClient, + MetadataContext metadataContext, + JsonSerializerOptions options, + ILogger logger, + IMemoryCache memoryCache) + { + this.infoBarService = infoBarService; + this.httpClient = httpClient; + this.metadataContext = metadataContext; + this.options = options; + this.logger = logger; + this.memoryCache = memoryCache; + } + + /// + public bool IsInitialized { get => isInitialized; private set => isInitialized = value; } + + /// + public async ValueTask InitializeAsync(CancellationToken token = default) + { + if (IsInitialized) + { + return true; + } + + metadataContext.EnsureDirectory(); + if (metadataContext.FileExists(MetaFileName)) + { + IDictionary? metaMd5Map; + using (Stream metaFile = metadataContext.OpenRead(MetaFileName)) + { + metaMd5Map = await JsonSerializer + .DeserializeAsync>(metaFile, options, token) + .ConfigureAwait(false); + } + + await CheckMetadataAsync(Must.NotNull(metaMd5Map!), token) + .ConfigureAwait(false); + + IsInitialized = true; + } + else + { + IsInitialized = await UpdateMetadataAsync(token) + .ConfigureAwait(false); + } + + return IsInitialized; + } + + /// + public async Task UpdateMetadataAsync(CancellationToken token = default) + { + IDictionary? metaMd5Map = await httpClient + .GetFromJsonAsync>($"{MetaAPIHost}/{MetaFileName}", options, token) + .ConfigureAwait(false); + + if (metaMd5Map is null) + { + infoBarService.Error("元数据校验文件解析失败"); + return false; + } + + await CheckMetadataAsync(metaMd5Map, token).ConfigureAwait(false); + + using (FileStream metaFileStream = metadataContext.Create(MetaFileName)) + { + await JsonSerializer + .SerializeAsync(metaFileStream, metaMd5Map, options, token) + .ConfigureAwait(false); + } + + return true; + } + + /// + public ValueTask> GetAchievementGoalsAsync(CancellationToken token = default) + { + return GetMetadataAsync>("AchievementGoal", token); + } + + /// + public ValueTask> GetAchievementsAsync(CancellationToken token = default) + { + return GetMetadataAsync>("Achievement", token); + } + + /// + public ValueTask> GetAvatarsAsync(CancellationToken token = default) + { + return GetMetadataAsync>("Avatar", token); + } + + /// + public ValueTask> GetReliquariesAsync(CancellationToken token = default) + { + return GetMetadataAsync>("Reliquary", token); + } + + /// + public ValueTask> GetReliquaryAffixesAsync(CancellationToken token = default) + { + return GetMetadataAsync>("ReliquaryAffix", token); + } + + /// + public ValueTask> GetReliquaryMainAffixesAsync(CancellationToken token = default) + { + return GetMetadataAsync>("ReliquaryMainAffix", token); + } + + /// + public ValueTask> GetWeaponsAsync(CancellationToken token = default) + { + return GetMetadataAsync>("Weapon", token); + } + + private async ValueTask GetMetadataAsync(string fileName, CancellationToken token) + where T : class + { + Verify.Operation(IsInitialized, "元数据服务尚未初始化,或初始化失败"); + string cacheKey = $"{nameof(MetadataService)}.Cache.{fileName}"; + + if (memoryCache.TryGetValue(cacheKey, out object? value)) + { + return Must.NotNull((value as T)!); + } + + T? result = await JsonSerializer + .DeserializeAsync(metadataContext.OpenRead($"{fileName}.json"), options, token) + .ConfigureAwait(false); + + return memoryCache.Set(cacheKey, Must.NotNull(result!)); + } + + private async Task GetFileMd5Async(string fileFullName, CancellationToken token) + { + using (FileStream stream = metadataContext.OpenRead(fileFullName)) + { + byte[] bytes = await MD5.Create() + .ComputeHashAsync(stream, token) + .ConfigureAwait(false); + + return Convert.ToHexString(bytes); + } + } + + /// + /// 检查元数据的Md5值是否匹配 + /// 如果不匹配则尝试下载 + /// + /// 元数据校验表 + /// 取消令牌 + /// 令牌 + private async Task CheckMetadataAsync(IDictionary metaMd5Map, CancellationToken token) + { + // enumerate files and compare md5 + foreach ((string fileName, string md5) in metaMd5Map) + { + string fileFullName = $"{fileName}.json"; + bool skip = false; + + if (metadataContext.FileExists(fileFullName)) + { + skip = md5 == await GetFileMd5Async(fileFullName, token) + .ConfigureAwait(false); + } + + if (!skip) + { + logger.LogInformation("{file} 文件 MD5 不匹配", fileFullName); + + await DownloadMetadataAsync(fileFullName, token) + .ConfigureAwait(false); + } + } + } + + private async Task DownloadMetadataAsync(string fileFullName, CancellationToken token) + { + Stream sourceStream = await httpClient + .GetStreamAsync($"{MetaAPIHost}/{fileFullName}", token) + .ConfigureAwait(false); + + // Write stream while convert LF to CRLF + using (StreamReader streamReader = new(sourceStream)) + { + using (StreamWriter streamWriter = new(metadataContext.Create(fileFullName))) + { + while (await streamReader.ReadLineAsync().ConfigureAwait(false) is string line) + { + await (streamReader.EndOfStream + ? streamWriter.WriteAsync(line) // Don't append the last line + : streamWriter.WriteLineAsync(line)) + .ConfigureAwait(false); + } + } + } + + logger.LogInformation("{file} 下载完成", fileFullName); + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/Navigation/INavigationAwaiter.cs b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/INavigationAwaiter.cs similarity index 90% rename from src/Snap.Hutao/Snap.Hutao/Service/Abstraction/Navigation/INavigationAwaiter.cs rename to src/Snap.Hutao/Snap.Hutao/Service/Navigation/INavigationAwaiter.cs index b25ad268..15ad7ec4 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/Navigation/INavigationAwaiter.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/INavigationAwaiter.cs @@ -1,7 +1,7 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -namespace Snap.Hutao.Service.Abstraction.Navigation; +namespace Snap.Hutao.Service.Navigation; /// /// 表示导航等待器 diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/Navigation/INavigationExtra.cs b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/INavigationExtra.cs similarity index 80% rename from src/Snap.Hutao/Snap.Hutao/Service/Abstraction/Navigation/INavigationExtra.cs rename to src/Snap.Hutao/Snap.Hutao/Service/Navigation/INavigationExtra.cs index 418f0c4c..6d286a04 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/Navigation/INavigationExtra.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/INavigationExtra.cs @@ -1,8 +1,11 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -namespace Snap.Hutao.Service.Abstraction.Navigation; +namespace Snap.Hutao.Service.Navigation; +/// +/// 为 提供抽象接口 +/// public interface INavigationExtra { /// diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/Navigation/INavigationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/INavigationService.cs similarity index 98% rename from src/Snap.Hutao/Snap.Hutao/Service/Abstraction/Navigation/INavigationService.cs rename to src/Snap.Hutao/Snap.Hutao/Service/Navigation/INavigationService.cs index e3921cc2..19ae3f58 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/Navigation/INavigationService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/INavigationService.cs @@ -3,7 +3,7 @@ using Microsoft.UI.Xaml.Controls; -namespace Snap.Hutao.Service.Abstraction.Navigation; +namespace Snap.Hutao.Service.Navigation; /// /// 导航服务 diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/Navigation/NavigationExtra.cs b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationExtra.cs similarity index 95% rename from src/Snap.Hutao/Snap.Hutao/Service/Abstraction/Navigation/NavigationExtra.cs rename to src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationExtra.cs index fb0508d9..7b120f6f 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/Navigation/NavigationExtra.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationExtra.cs @@ -1,7 +1,7 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -namespace Snap.Hutao.Service.Abstraction.Navigation; +namespace Snap.Hutao.Service.Navigation; /// /// 导航额外信息 diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/Navigation/NavigationResult.cs b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationResult.cs similarity index 88% rename from src/Snap.Hutao/Snap.Hutao/Service/Abstraction/Navigation/NavigationResult.cs rename to src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationResult.cs index 0f6e36bc..c56898cf 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/Navigation/NavigationResult.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationResult.cs @@ -1,7 +1,7 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -namespace Snap.Hutao.Service.Abstraction.Navigation; +namespace Snap.Hutao.Service.Navigation; /// /// 导航结果 diff --git a/src/Snap.Hutao/Snap.Hutao/Service/NavigationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationService.cs similarity index 98% rename from src/Snap.Hutao/Snap.Hutao/Service/NavigationService.cs rename to src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationService.cs index 4d8d4d35..91855e21 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/NavigationService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationService.cs @@ -4,12 +4,11 @@ using Microsoft.UI.Xaml.Controls; using Snap.Hutao.Core.Logging; using Snap.Hutao.Service.Abstraction; -using Snap.Hutao.Service.Abstraction.Navigation; using Snap.Hutao.View.Helper; using Snap.Hutao.View.Page; using System.Linq; -namespace Snap.Hutao.Service; +namespace Snap.Hutao.Service.Navigation; /// /// 导航服务 diff --git a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj index 1ac200a0..98e100f4 100644 --- a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj +++ b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj @@ -5,8 +5,8 @@ 10.0.17763.0 Snap.Hutao app.manifest - x86;x64;arm64 - win10-x86;win10-x64;win10-arm64 + x64 + win10-x64 win10-$(Platform).pubxml true false @@ -31,9 +31,11 @@ + + @@ -130,6 +132,16 @@ + + + MSBuild:Compile + + + + + MSBuild:Compile + + MSBuild:Compile diff --git a/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml b/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml index fa1849e1..b7d33f69 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml @@ -28,6 +28,12 @@ + + + + + + diff --git a/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml.cs index 4b0109b7..a78cba36 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml.cs +++ b/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml.cs @@ -3,7 +3,8 @@ using Microsoft.UI.Xaml.Controls; using Snap.Hutao.Service.Abstraction; -using Snap.Hutao.Service.Abstraction.Navigation; +using Snap.Hutao.Service.Navigation; +using Snap.Hutao.View.Page; namespace Snap.Hutao.View; @@ -27,6 +28,6 @@ public sealed partial class MainView : UserControl navigationService = Ioc.Default.GetRequiredService(); navigationService.Initialize(NavView, ContentFrame); - navigationService.Navigate(INavigationAwaiter.Default, true); + navigationService.Navigate(INavigationAwaiter.Default, false); } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml new file mode 100644 index 00000000..86c0aed5 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml.cs new file mode 100644 index 00000000..c4cfb5f9 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml.cs @@ -0,0 +1,22 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Control.Cancellable; +using Snap.Hutao.ViewModel; + +namespace Snap.Hutao.View.Page; + +/// +/// 成就页面 +/// +public sealed partial class AchievementPage : CancellablePage +{ + /// + /// 构造一个新的成就页面 + /// + public AchievementPage() + { + InitializeWith(); + InitializeComponent(); + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementContentPage.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementContentPage.xaml.cs index 316a7b6b..f9d36df9 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementContentPage.xaml.cs +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementContentPage.xaml.cs @@ -4,7 +4,8 @@ using Microsoft.UI.Xaml.Navigation; using Microsoft.VisualStudio.Threading; using Snap.Hutao.Core; -using Snap.Hutao.Service.Abstraction.Navigation; +using Snap.Hutao.Extension; +using Snap.Hutao.Service.Navigation; namespace Snap.Hutao.View.Page; @@ -38,7 +39,7 @@ openInWebview: function(url){ location.href = url }}"; if (e.Parameter is INavigationExtra extra) { targetContent = extra.Data as string; - LoadAnnouncementAsync(extra).Forget(); + LoadAnnouncementAsync(extra).SafeForget(); } } @@ -60,4 +61,4 @@ openInWebview: function(url){ location.href = url }}"; WebView.NavigateToString(targetContent); extra.NotifyNavigationCompleted(); } -} +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementPage.xaml index 6ffc9406..d2c5bf77 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementPage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementPage.xaml @@ -43,9 +43,9 @@ Margin="0,0,0,12" Style="{StaticResource TitleTextBlockStyle}"/> diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementPage.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementPage.xaml.cs index dfc07c1d..81a28f8c 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementPage.xaml.cs +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementPage.xaml.cs @@ -16,7 +16,7 @@ public sealed partial class AnnouncementPage : CancellablePage /// public AnnouncementPage() { - Initialize(Ioc.Default.GetRequiredService()); + InitializeWith(); InitializeComponent(); } } diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/WelcomePage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/WelcomePage.xaml new file mode 100644 index 00000000..5f7fba55 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/WelcomePage.xaml @@ -0,0 +1,16 @@ + + + + + + diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/WelcomePage.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Page/WelcomePage.xaml.cs new file mode 100644 index 00000000..3ac10f4e --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/WelcomePage.xaml.cs @@ -0,0 +1,18 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.View.Page; + +/// +/// 欢迎页 +/// +public sealed partial class WelcomePage : Microsoft.UI.Xaml.Controls.Page +{ + /// + /// 构造一个新的欢迎页 + /// + public WelcomePage() + { + InitializeComponent(); + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/AchievementViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/AchievementViewModel.cs new file mode 100644 index 00000000..334af782 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/AchievementViewModel.cs @@ -0,0 +1,66 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.WinUI.UI; +using Microsoft.VisualStudio.Threading; +using Snap.Hutao.Control.Cancellable; +using Snap.Hutao.Factory.Abstraction; +using Snap.Hutao.Model.Metadata.Achievement; +using Snap.Hutao.Service.Metadata; +using System.Collections.Generic; +using System.Linq; + +namespace Snap.Hutao.ViewModel; + +/// +/// 成就视图模型 +/// +[Injection(InjectAs.Transient)] +internal class AchievementViewModel : ObservableObject, ISupportCancellation +{ + private readonly IMetadataService metadataService; + private AdvancedCollectionView? achievementsView; + + /// + /// 构造一个新的成就视图模型 + /// + /// 元数据服务 + /// 异步命令工厂 + public AchievementViewModel(IMetadataService metadataService, IAsyncRelayCommandFactory asyncRelayCommandFactory) + { + this.metadataService = metadataService; + OpenUICommand = asyncRelayCommandFactory.Create(OpenUIAsync); + } + + /// + public CancellationToken CancellationToken { get; set; } + + /// + /// 成就视图 + /// + public AdvancedCollectionView? AchievementsView + { + get => achievementsView; + set => SetProperty(ref achievementsView, value); + } + + /// + /// 打开页面命令 + /// + public ICommand OpenUICommand { get; } + + private async Task OpenUIAsync(CancellationToken token) + { + using (CancellationTokenExtensions.CombinedCancellationToken combined = token.CombineWith(CancellationToken)) + { + if (await metadataService.InitializeAsync(combined.Token)) + { + IEnumerable achievements = await metadataService.GetAchievementsAsync(combined.Token); + + // TODO + AchievementsView = new(achievements.ToList()); + } + } + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/AnnouncementViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/AnnouncementViewModel.cs index c9f20d10..a2e1bc72 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/AnnouncementViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/AnnouncementViewModel.cs @@ -8,7 +8,8 @@ using Snap.Hutao.Core; using Snap.Hutao.Core.Threading; using Snap.Hutao.Factory.Abstraction; using Snap.Hutao.Service.Abstraction; -using Snap.Hutao.Service.Abstraction.Navigation; +using Snap.Hutao.Service.Navigation; +using Snap.Hutao.View.Page; using Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement; namespace Snap.Hutao.ViewModel; @@ -99,7 +100,7 @@ internal class AnnouncementViewModel : ObservableObject, ISupportCancellation if (WebView2Helper.IsSupported) { - navigationService.Navigate(data: new NavigationExtra(content)); + navigationService.Navigate(data: new NavigationExtra(content)); } else { diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/EnkaClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/EnkaClient.cs new file mode 100644 index 00000000..d24f964f --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/EnkaClient.cs @@ -0,0 +1,40 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Web.Enka.Model; +using Snap.Hutao.Web.Hoyolab; +using System.Net.Http; +using System.Net.Http.Json; + +namespace Snap.Hutao.Web.Enka; + +/// +/// Enka API 客户端 +/// +[Injection(InjectAs.Transient)] +internal class EnkaClient +{ + private const string EnkaAPI = "https://enka.shinshin.moe/u/{0}/__data.json"; + + private readonly HttpClient httpClient; + + /// + /// 构造一个新的 Enka API 客户端 + /// + /// http客户端 + public EnkaClient(HttpClient httpClient) + { + this.httpClient = httpClient; + } + + /// + /// 异步获取 Enka API 响应 + /// + /// 玩家Uid + /// 取消令牌 + /// Enka API 响应 + public Task GetDataAsync(PlayerUid playerUid, CancellationToken token) + { + return httpClient.GetFromJsonAsync(string.Format(EnkaAPI, playerUid.Value), token); + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/AvatarInfo.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/AvatarInfo.cs new file mode 100644 index 00000000..118faa96 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/AvatarInfo.cs @@ -0,0 +1,92 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Core.Json.Converter; +using Snap.Hutao.Model.Intrinsic; +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Snap.Hutao.Web.Enka.Model; + +/// +/// 角色信息 +/// +public class AvatarInfo +{ + /// + /// 角色Id + /// Character ID + /// + [JsonPropertyName("avatarId")] + public int AvatarId { get; set; } + + /// + /// 基础属性 + /// Character Info Properties List + /// + /// + [JsonPropertyName("propMap")] + [JsonConverter(typeof(StringEnumKeyDictionaryConverter))] + public IDictionary PropMap { get; set; } = default!; + + /// + /// 命座 Id + /// + [JsonPropertyName("talentIdList")] + public IList TalentIdList { get; set; } = default!; + + /// + /// 属性Map + /// Map of Character's Combat Properties. + /// + [JsonPropertyName("fightPropMap")] + [JsonConverter(typeof(StringEnumKeyDictionaryConverter))] + public IDictionary FightPropMap { get; set; } = default!; + + /// + /// 技能组Id + /// Character Skill Set ID + /// + [JsonPropertyName("skillDepotId")] + public int SkillDepotId { get; set; } + + /// + /// List of Unlocked Skill Ids + /// 被动天赋 + /// + [JsonPropertyName("inherentProudSkillList")] + public IList InherentProudSkillList { get; set; } = default!; + + /// + /// Map of Skill Levels + /// + [JsonPropertyName("skillLevelMap")] + public IDictionary SkillLevelMap { get; set; } = default!; + + /// + /// 装备列表 + /// 最后一个为武器 + /// List of Equipments: Weapon, Ariftacts + /// + [JsonPropertyName("equipList")] + public IList EquipList { get; set; } = default!; + + /// + /// 好感度信息 + /// Character Friendship Level + /// + [JsonPropertyName("fetterInfo")] + public FetterInfo FetterInfo { get; set; } = default!; + + /// + /// 皮肤 Id + /// + [JsonPropertyName("costumeId")] + public int? CostumeId { get; set; } + + /// + /// 命座额外技能等级 + /// + [JsonPropertyName("proudSkillExtraLevelMap")] + public IDictionary ProudSkillExtraLevelMap { get; set; } = default!; +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/EnkaResponse.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/EnkaResponse.cs new file mode 100644 index 00000000..f147df69 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/EnkaResponse.cs @@ -0,0 +1,48 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Snap.Hutao.Web.Enka.Model; + +/// +/// Enka API 响应 +/// +public class EnkaResponse +{ + /// + /// 玩家基础信息 + /// + [JsonPropertyName("playerInfo")] + public PlayerInfo? PlayerInfo { get; set; } = default!; + + /// + /// 展示的角色详细信息列表 + /// + [JsonPropertyName("avatarInfoList")] + public IList? AvatarInfoList { get; set; } = default!; + + /// + /// 刷新剩余秒数 + /// 生存时间值 + /// + [JsonPropertyName("ttl")] + public int? Ttl { get; set; } + + /// + /// 此响应是否有效 + /// + public bool IsValid + { + get => Ttl.HasValue; + } + + /// + /// 是否包含角色详细数据 + /// + public bool HasDetail + { + get => AvatarInfoList != null; + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/Equip.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/Equip.cs new file mode 100644 index 00000000..8b7f8eff --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/Equip.cs @@ -0,0 +1,39 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using System.Text.Json.Serialization; + +namespace Snap.Hutao.Web.Enka.Model; + +/// +/// 装备 +/// +public class Equip +{ + /// + /// 物品Id + /// Equipment ID + /// + [JsonPropertyName("itemId")] + public int ItemId { get; set; } + + /// + /// 圣遗物 + /// Artifact Base Info + /// + [JsonPropertyName("reliquary")] + public Reliquary? Reliquary { get; set; } + + /// + /// 武器 + /// Weapon Base Info + /// + [JsonPropertyName("weapon")] + public Weapon? Weapon { get; set; } + + /// + /// Detailed Info of Equipment + /// + [JsonPropertyName("flat")] + public Flat Flat { get; set; } = default!; +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/FetterInfo.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/FetterInfo.cs new file mode 100644 index 00000000..569d7bcb --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/FetterInfo.cs @@ -0,0 +1,18 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using System.Text.Json.Serialization; + +namespace Snap.Hutao.Web.Enka.Model; + +/// +/// 好感度信息 +/// +public class FetterInfo +{ + /// + /// 好感度等级 + /// + [JsonPropertyName("expLevel")] + public int ExpLevel { get; set; } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/Flat.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/Flat.cs new file mode 100644 index 00000000..e54ebd74 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/Flat.cs @@ -0,0 +1,84 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Model.Intrinsic; +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Snap.Hutao.Web.Enka.Model; + +/// +/// 平展值 +/// +public class Flat +{ + /// + /// 名称 + /// Hash for Equipment Name + /// + [JsonPropertyName("nameTextMapHash")] + public string NameTextMapHash { get; set; } = default!; + + /// + /// 套装名称 + /// Hash for Artifact Set Name + /// + [JsonPropertyName("setNameTextMapHash")] + public string? SetNameTextMapHash { get; set; } + + /// + /// 等级 + /// Rarity Level of Equipment + /// + [JsonPropertyName("rankLevel")] + public int RankLevel { get; set; } + + /// + /// 圣遗物主属性 + /// Artifact Main Stat + /// + [JsonPropertyName("reliquaryMainstat")] + public ReliquaryMainstat? ReliquaryMainstat { get; set; } + + /// + /// 圣遗物副属性 + /// List of Artifact Substats + /// + [JsonPropertyName("reliquarySubstats")] + public IList? ReliquarySubstats { get; set; } + + /// + /// 物品类型 + /// Equipment Type: Weapon or Artifact + /// ITEM_WEAPON + /// ITEM_RELIQUARY + /// + [JsonPropertyName("itemType")] + [JsonConverter(typeof(JsonStringEnumConverter))] + public ItemType ItemType { get; set; } = default!; + + /// + /// 图标 + /// Equipment Icon Name + /// + [JsonPropertyName("icon")] + public string Icon { get; set; } = default!; + + /// + /// 圣遗物类型 + /// 当为武器时 + /// 值为 + /// + [JsonPropertyName("equipType")] + [JsonConverter(typeof(JsonStringEnumConverter))] + public EquipType EquipType { get; set; } + + /// + /// 武器主副属性 + /// 0 基础攻击力 + /// 1 主属性 + /// List of Weapon Stat: Base ATK, Substat + /// + [JsonPropertyName("weaponStats")] + public IList? WeaponStats { get; set; } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/PlayerInfo.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/PlayerInfo.cs new file mode 100644 index 00000000..15ba1143 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/PlayerInfo.cs @@ -0,0 +1,89 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Snap.Hutao.Web.Enka.Model; + +/// +/// 玩家信息 +/// +public class PlayerInfo +{ + /// + /// 昵称 + /// Player Nickname + /// + [JsonPropertyName("nickname")] + public string Nickname { get; set; } = default!; + + /// + /// 等级 + /// + [JsonPropertyName("level")] + public int Level { get; set; } + + /// + /// 签名 + /// Profile Signature + /// + [JsonPropertyName("signature")] + public string Signature { get; set; } = default!; + + /// + /// 世界等级 + /// Player World Level + /// + [JsonPropertyName("worldLevel")] + public int WorldLevel { get; set; } + + /// + /// 名片的Id + /// Profile Namecard ID + /// + [JsonPropertyName("nameCardId")] + public int NameCardId { get; set; } + + /// + /// 完成的成就个数 + /// Number of Completed Achievements + /// + [JsonPropertyName("finishAchievementNum")] + public int FinishAchievementNum { get; set; } + + /// + /// 深渊层数 + /// Abyss Floor + /// + [JsonPropertyName("towerFloorIndex")] + public int TowerFloorIndex { get; set; } + + /// + /// 深渊间数 + /// Abyss Floor's Level + /// + [JsonPropertyName("towerLevelIndex")] + public int TowerLevelIndex { get; set; } + + /// + /// 展示的角色信息 + /// List of Character IDs and Levels + /// + [JsonPropertyName("showAvatarInfoList")] + public IList ShowAvatarInfoList { get; set; } = default!; + + /// + /// 展示的名片信息 + /// List of Namecard IDs + /// + [JsonPropertyName("showNameCardIdList")] + public IList ShowNameCardIdList { get; set; } = default!; + + /// + /// 头像信息 + /// Character ID of Profile Picture + /// + [JsonPropertyName("profilePicture")] + public ProfilePicture ProfilePicture { get; set; } = default!; +} diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/PlayerProperty.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/PlayerProperty.cs new file mode 100644 index 00000000..2d59fe20 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/PlayerProperty.cs @@ -0,0 +1,228 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Web.Enka.Model; + +/// +/// 玩家属性 +/// https://github.com/Grasscutters/Grasscutter/tree/development/src/main/java/emu/grasscutter/game/props/PlayerProperty.java +/// +public enum PlayerProperty +{ + /// + /// 空 + /// + PROP_NONE = 0, + + /// + /// 经验值 + /// + PROP_EXP = 1001, + + /// + /// 突破等级 + /// + PROP_BREAK_LEVEL = 1002, + + /// + /// 饱食度 + /// + PROP_SATIATION_VAL = 1003, + + /// + /// 饱食度冷却 + /// + PROP_SATIATION_PENALTY_TIME = 1004, + + /// + /// 等级 + /// + PROP_LEVEL = 4001, + + /// + /// 上次切换角色的时间 + /// + PROP_LAST_CHANGE_AVATAR_TIME = 10001, + + /// + /// Maximum volume of the Statue of the Seven for the player [0, 8500000] + /// + PROP_MAX_SPRING_VOLUME = 10002, + + /// + /// Current volume of the Statue of the Seven [0, PROP_MAX_SPRING_VOLUME] + /// + PROP_CUR_SPRING_VOLUME = 10003, + + /// + /// Auto HP recovery when approaching the Statue of the Seven [0, 1] + /// + PROP_IS_SPRING_AUTO_USE = 10004, + + /// + /// Auto HP recovery percentage [0, 100] + /// + PROP_SPRING_AUTO_USE_PERCENT = 10005, + + /// + /// Are you in a state that disables your flying ability? e.g. new player [0, 1] + /// + PROP_IS_FLYABLE = 10006, + + /// + /// 天气是否锁定 + /// + PROP_IS_WEATHER_LOCKED = 10007, + + /// + /// 游戏时间是否锁定 + /// + PROP_IS_GAME_TIME_LOCKED = 10008, + + /// + /// ? + /// + PROP_IS_TRANSFERABLE = 10009, + + /// + /// Maximum stamina of the player =0 - 24000 + /// + PROP_MAX_STAMINA = 10010, + + /// + /// Used stamina of the player =0 - PROP_MAX_STAMINA + /// + PROP_CUR_PERSIST_STAMINA = 10011, + + /// + /// 临时体力,食物? + /// + PROP_CUR_TEMPORARY_STAMINA = 10012, + + /// + /// 玩家等级 + /// + PROP_PLAYER_LEVEL = 10013, + + /// + /// 玩家经验 + /// + PROP_PLAYER_EXP = 10014, + + /// + /// Primogem =-inf, +inf + /// It is known that Mihoyo will make Primogem negative in the cases that a player spends + /// his gems and then got a money refund, so negative is allowed. + /// + PROP_PLAYER_HCOIN = 10015, + + /// + /// Mora [0, +inf + /// + PROP_PLAYER_SCOIN = 10016, + + /// + /// Do you allow other players to join your game? [0=no 1=direct 2=approval] + /// + PROP_PLAYER_MP_SETTING_TYPE = 10017, + + /// + /// 0 if in quest or something that disables MP [0, 1] + /// + PROP_IS_MP_MODE_AVAILABLE = 10018, + + /// + /// [0, 8] + /// + PROP_PLAYER_WORLD_LEVEL = 10019, + + /// + /// Original Resin [0, 2000] - note that values above 160 require refills + /// + PROP_PLAYER_RESIN = 10020, + + /// + /// ? + /// + PROP_PLAYER_WAIT_SUB_HCOIN = 10022, + + /// + /// ? + /// + PROP_PLAYER_WAIT_SUB_SCOIN = 10023, + + /// + /// Is only MP with PlayStation players? [0, 1] + /// + PROP_IS_ONLY_MP_WITH_PS_PLAYER = 10024, + + /// + /// Genesis Crystal =-inf, +inf see 10015 + /// + PROP_PLAYER_MCOIN = 10025, + + /// + /// ? + /// + PROP_PLAYER_WAIT_SUB_MCOIN = 10026, + + /// + /// ? + /// + PROP_PLAYER_LEGENDARY_KEY = 10027, + + /// + /// ? + /// + PROP_IS_HAS_FIRST_SHARE = 10028, + + /// + /// ? + /// + PROP_PLAYER_FORGE_POINT = 10029, + + /// + /// ? + /// + PROP_CUR_CLIMATE_METER = 10035, + + /// + /// ? + /// + PROP_CUR_CLIMATE_TYPE = 10036, + + /// + /// ? + /// + PROP_CUR_CLIMATE_AREA_ID = 10037, + + /// + /// ? + /// + PROP_CUR_CLIMATE_AREA_CLIMATE_TYPE = 10038, + + /// + /// ? + /// + PROP_PLAYER_WORLD_LEVEL_LIMIT = 10039, + + /// + /// ? + /// + PROP_PLAYER_WORLD_LEVEL_ADJUST_CD = 10040, + + /// + /// ? + /// + PROP_PLAYER_LEGENDARY_DAILY_TASK_NUM = 10041, + + /// + /// Realm currency [0, +inf + /// + PROP_PLAYER_HOME_COIN = 10042, + + /// + /// ? + /// + PROP_PLAYER_WAIT_SUB_HOME_COIN = 10043, +} diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/ProfilePicture.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/ProfilePicture.cs new file mode 100644 index 00000000..fbaf35eb --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/ProfilePicture.cs @@ -0,0 +1,18 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using System.Text.Json.Serialization; + +namespace Snap.Hutao.Web.Enka.Model; + +/// +/// 档案头像 +/// +public class ProfilePicture +{ + /// + /// 使用的角色Id + /// + [JsonPropertyName("avatarId")] + public int AvatarId { get; set; } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/Reliquary.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/Reliquary.cs new file mode 100644 index 00000000..d0314cb3 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/Reliquary.cs @@ -0,0 +1,34 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Snap.Hutao.Web.Enka.Model; + +/// +/// 圣遗物 +/// +public class Reliquary +{ + /// + /// 等级 +20 = 21 + /// [1,21] + /// Artifact Level [1-21] + /// + [JsonPropertyName("level")] + public int Level { get; set; } + + /// + /// 主属性Id + /// Artifact Main Stat ID + /// + [JsonPropertyName("mainPropId")] + public int MainPropId { get; set; } + + /// + /// 强化属性Id + /// + [JsonPropertyName("appendPropIdList")] + public IList AppendPropIdList { get; set; } = default!; +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/ReliquaryMainstat.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/ReliquaryMainstat.cs new file mode 100644 index 00000000..56999a39 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/ReliquaryMainstat.cs @@ -0,0 +1,26 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Model.Intrinsic; +using System.Text.Json.Serialization; + +namespace Snap.Hutao.Web.Enka.Model; + +/// +/// 圣遗物主属性 +/// +public class ReliquaryMainstat +{ + /// + /// Equipment Append Property Name. + /// + [JsonPropertyName("mainPropId")] + [JsonConverter(typeof(JsonStringEnumConverter))] + public FightProperty MainPropId { get; set; } + + /// + /// Property Value + /// + [JsonPropertyName("statValue")] + public double StatValue { get; set; } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/ReliquarySubstat.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/ReliquarySubstat.cs new file mode 100644 index 00000000..a3859edc --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/ReliquarySubstat.cs @@ -0,0 +1,26 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Model.Intrinsic; +using System.Text.Json.Serialization; + +namespace Snap.Hutao.Web.Enka.Model; + +/// +/// 圣遗物副属性 +/// +public class ReliquarySubstat +{ + /// + /// 增加属性 + /// + [JsonPropertyName("appendPropId")] + [JsonConverter(typeof(JsonStringEnumConverter))] + public FightProperty AppendPropId { get; set; } + + /// + /// 值 + /// + [JsonPropertyName("statValue")] + public double StatValue { get; set; } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/ShowAvatarInfo.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/ShowAvatarInfo.cs new file mode 100644 index 00000000..fd74788f --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/ShowAvatarInfo.cs @@ -0,0 +1,32 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using System.Text.Json.Serialization; + +namespace Snap.Hutao.Web.Enka.Model; + +/// +/// 角色列表信息 +/// +public class ShowAvatarInfo +{ + /// + /// 角色Id + /// Character ID + /// + [JsonPropertyName("avatarId")] + public int AvatarId { get; set; } + + /// + /// 角色等级 + /// Character Level + /// + [JsonPropertyName("level")] + public int Level { get; set; } + + /// + /// 可能的皮肤Id + /// + [JsonPropertyName("costumeId")] + public int? CostumeId { get; set; } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/TypeValue.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/TypeValue.cs new file mode 100644 index 00000000..61aaa9eb --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/TypeValue.cs @@ -0,0 +1,24 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using System.Text.Json.Serialization; + +namespace Snap.Hutao.Web.Enka.Model; + +/// +/// 类型与值 +/// +public class TypeValue +{ + /// + /// 类型 + /// + [JsonPropertyName("type")] + public PlayerProperty Type { get; set; } + + /// + /// 值 + /// + [JsonPropertyName("val")] + public string? Value { get; set; } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/Weapon.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/Weapon.cs new file mode 100644 index 00000000..9c7d7805 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/Weapon.cs @@ -0,0 +1,34 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Snap.Hutao.Web.Enka.Model; + +/// +/// 武器信息 +/// +public class Weapon +{ + /// + /// 等级 + /// Weapon Level + /// + [JsonPropertyName("level")] + public int Level { get; set; } + + /// + /// 突破等级 + /// Weapon Ascension Level + /// + [JsonPropertyName("promoteLevel")] + public int PromoteLevel { get; set; } + + /// + /// 精炼 相较于实际等级 -1 + /// Weapon Refinement Level [0-4] + /// + [JsonPropertyName("affixMap")] + public IDictionary AffixMap { get; set; } = default!; +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/WeaponStat.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/WeaponStat.cs new file mode 100644 index 00000000..6575ea94 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/WeaponStat.cs @@ -0,0 +1,26 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Model.Intrinsic; +using System.Text.Json.Serialization; + +namespace Snap.Hutao.Web.Enka.Model; + +/// +/// 武器属性 +/// +public class WeaponStat +{ + /// + /// 提升属性Id + /// + [JsonPropertyName("appendPropId")] + [JsonConverter(typeof(JsonStringEnumConverter))] + public FightProperty AppendPropId { get; set; } + + /// + /// 值 + /// + [JsonPropertyName("statValue")] + public double StatValue { get; set; } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/CommunityInfo.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/CommunityInfo.cs index d38ef786..7ea79c19 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/CommunityInfo.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/CommunityInfo.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. using System.Collections.Generic; -using System.Text.Json; using System.Text.Json.Serialization; namespace Snap.Hutao.Web.Hoyolab.Bbs.User; diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserClient.cs index b737df07..3711b84e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserClient.cs @@ -5,7 +5,6 @@ using Snap.Hutao.Web.Hoyolab.DynamicSecret; using Snap.Hutao.Web.Response; using System.Net.Http; using System.Net.Http.Json; -using System.Text.Json; namespace Snap.Hutao.Web.Hoyolab.Bbs.User; diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserFullInfoWrapper.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserFullInfoWrapper.cs index 0274e316..cffc0a6a 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserFullInfoWrapper.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserFullInfoWrapper.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. using System.Collections.Generic; -using System.Text.Json; using System.Text.Json.Serialization; namespace Snap.Hutao.Web.Hoyolab.Bbs.User; diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/DynamicSecretProvider2.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/DynamicSecretProvider2.cs index fbc0d545..95c27d59 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/DynamicSecretProvider2.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/DynamicSecretProvider2.cs @@ -3,7 +3,6 @@ using Snap.Hutao.Core.Convertion; using System.Linq; -using System.Text.Json; namespace Snap.Hutao.Web.Hoyolab.DynamicSecret; diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/HttpClientDynamicSecretExtensions.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/HttpClientDynamicSecretExtensions.cs index 6c48bb74..28b8aafe 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/HttpClientDynamicSecretExtensions.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/HttpClientDynamicSecretExtensions.cs @@ -3,7 +3,6 @@ using Snap.Hutao.Extension; using System.Net.Http; -using System.Text.Json; namespace Snap.Hutao.Web.Hoyolab.DynamicSecret; diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Hk4e/Common/Announcement/AnnouncementClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Hk4e/Common/Announcement/AnnouncementClient.cs index 7a488087..d677fdef 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Hk4e/Common/Announcement/AnnouncementClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Hk4e/Common/Announcement/AnnouncementClient.cs @@ -6,8 +6,6 @@ using Snap.Hutao.Web.Response; using System.Collections.Generic; using System.Net.Http; using System.Net.Http.Json; -using System.Text; -using System.Text.Json; namespace Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement; @@ -52,8 +50,7 @@ internal class AnnouncementClient /// 公告内容列表 public async Task> GetAnnouncementContentsAsync(CancellationToken cancellationToken = default) { - Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); - + // Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); Response>? resp = await httpClient .GetFromJsonAsync>>(ApiEndpoints.AnnContent, jsonSerializerOptions, cancellationToken) .ConfigureAwait(false); diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/PlayerUid.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/PlayerUid.cs similarity index 97% rename from src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/PlayerUid.cs rename to src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/PlayerUid.cs index f214d0a0..ab6ec83e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/PlayerUid.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/PlayerUid.cs @@ -1,7 +1,7 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -namespace Snap.Hutao.Web.Hoyolab.Takumi; +namespace Snap.Hutao.Web.Hoyolab; /// /// 玩家 Uid diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Avatar/Avatar.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Avatar/Avatar.cs index 5d60f719..5a75410c 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Avatar/Avatar.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Avatar/Avatar.cs @@ -1,6 +1,7 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using Snap.Hutao.Model.Intrinsic; using System.Text.Json.Serialization; namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Avatar; diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Avatar/Reliquary.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Avatar/Reliquary.cs index ee7ef9c6..efc2a5cf 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Avatar/Reliquary.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Avatar/Reliquary.cs @@ -1,6 +1,7 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using Snap.Hutao.Model.Intrinsic; using System.Text.Json.Serialization; namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Avatar; diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Avatar/Weapon.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Avatar/Weapon.cs index 70819bce..3d7ef97b 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Avatar/Weapon.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Avatar/Weapon.cs @@ -1,6 +1,7 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using Snap.Hutao.Model.Intrinsic; using System.Text.Json.Serialization; namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Avatar; diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/GameRecordClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/GameRecordClient.cs index 7e1908bf..735a3b92 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/GameRecordClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/GameRecordClient.cs @@ -10,7 +10,6 @@ using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Net.Http.Json; -using System.Text.Json; using System.Text.Json.Serialization; namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord; diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HutaoClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HutaoClient.cs index 81cc077f..2de44aeb 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HutaoClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HutaoClient.cs @@ -14,7 +14,6 @@ using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Net.Http.Json; -using System.Text.Json; namespace Snap.Hutao.Web.Hutao; @@ -50,29 +49,28 @@ internal class HutaoClient : ISupportAsyncInitialization } /// - public bool IsInitialized { get => isInitialized; } + public bool IsInitialized { get => isInitialized; private set => isInitialized = value; } /// - public async Task InitializeAsync(CancellationToken token = default) + public async ValueTask InitializeAsync(CancellationToken token = default) { - if (isInitialized) + if (!IsInitialized) { - return true; + Auth auth = new( + "08d9e212-0cb3-4d71-8ed7-003606da7b20", + "7ueWgZGn53dDhrm8L5ZRw+YWfOeSWtgQmJWquRgaygw="); + + HttpResponseMessage response = await httpClient + .PostAsJsonAsync($"{AuthAPIHost}/Auth/Login", auth, jsonSerializerOptions, token) + .ConfigureAwait(false); + Response? resp = await response.Content + .ReadFromJsonAsync>(jsonSerializerOptions, token) + .ConfigureAwait(false); + + httpClient.DefaultRequestHeaders.Authorization = new("Bearer", Must.NotNull(resp?.Data?.AccessToken!)); + IsInitialized = true; } - Auth auth = new( - "08d9e212-0cb3-4d71-8ed7-003606da7b20", - "7ueWgZGn53dDhrm8L5ZRw+YWfOeSWtgQmJWquRgaygw="); - - HttpResponseMessage response = await httpClient - .PostAsJsonAsync($"{AuthAPIHost}/Auth/Login", auth, jsonSerializerOptions, token) - .ConfigureAwait(false); - Response? resp = await response.Content - .ReadFromJsonAsync>(jsonSerializerOptions, token) - .ConfigureAwait(false); - - httpClient.DefaultRequestHeaders.Authorization = new("Bearer", Must.NotNull(resp?.Data?.AccessToken!)); - isInitialized = true; return true; } @@ -333,6 +331,7 @@ internal class HutaoClient : ISupportAsyncInitialization /// 角色详细信息 /// 取消令牌 /// 响应 + [Obsolete("不再强制要求上传物品")] [EditorBrowsable(EditorBrowsableState.Never)] internal async Task?> UploadItemsAsync(List characters, CancellationToken token = default) { diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Converter/ReliquarySetsConverter.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Converter/ReliquarySetsConverter.cs index 937c95aa..07f1be78 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Converter/ReliquarySetsConverter.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Converter/ReliquarySetsConverter.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. using System.Linq; -using System.Text.Json; using System.Text.Json.Serialization; namespace Snap.Hutao.Web.Hutao.Model.Converter; diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Post/PlayerRecord.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Post/PlayerRecord.cs index 5f0b34f6..de752f9c 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Post/PlayerRecord.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Post/PlayerRecord.cs @@ -43,8 +43,6 @@ public class PlayerRecord /// public IEnumerable PlayerSpiralAbyssesLevels { get; } - private List CachedAvatars { get; set; } = default!; - /// /// 建造玩家记录 /// @@ -61,25 +59,7 @@ public class PlayerRecord .SelectMany(f => f.Levels, (f, level) => new IndexedLevel(f.Index, level)) .Select(indexedLevel => new PlayerSpiralAbyssLevel(indexedLevel)); - return new PlayerRecord(uid, playerAvatars, playerSpiralAbyssLevels) - { - CachedAvatars = detailAvatars, - }; - } - - /// - /// 代替胡桃客户端上传物品数据 - /// - /// 使用的客户端 - /// 取消令牌 - /// 是否上传成功 - internal async Task UploadItemsAsync(HutaoClient hutaoClient, CancellationToken token) - { - Response? resp = await hutaoClient - .UploadItemsAsync(CachedAvatars, token) - .ConfigureAwait(false); - - return Response.Response.IsOk(resp); + return new PlayerRecord(uid, playerAvatars, playerSpiralAbyssLevels); } ///