diff --git a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Snap.Hutao.SourceGeneration.csproj b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Snap.Hutao.SourceGeneration.csproj
index 003c57a285..d1957c5605 100644
--- a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Snap.Hutao.SourceGeneration.csproj
+++ b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Snap.Hutao.SourceGeneration.csproj
@@ -21,7 +21,7 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/src/Snap.Hutao/Snap.Hutao/Context/Database/AppDbContext.cs b/src/Snap.Hutao/Snap.Hutao/Context/Database/AppDbContext.cs
index c0ffb9fd8b..e090853d11 100644
--- a/src/Snap.Hutao/Snap.Hutao/Context/Database/AppDbContext.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Context/Database/AppDbContext.cs
@@ -56,6 +56,11 @@ public AppDbContext(DbContextOptions options)
///
public DbSet AvatarInfos { get; set; } = default!;
+ ///
+ /// 游戏内账号
+ ///
+ public DbSet GameAccounts { get; set; } = default!;
+
///
/// 构造一个临时的应用程序数据库上下文
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Threading/ThreadHelper.cs b/src/Snap.Hutao/Snap.Hutao/Core/Threading/ThreadHelper.cs
index 3090c0ca7f..29b38d965b 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Threading/ThreadHelper.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Threading/ThreadHelper.cs
@@ -9,8 +9,9 @@ namespace Snap.Hutao.Core.Threading;
internal static class ThreadHelper
{
///
- /// 异步切换到主线程
+ /// 使用此静态方法以 异步切换到 主线程
///
+ /// 使用 异步切换到 后台线程
/// 等待体
public static DispatherQueueSwitchOperation SwitchToMainThreadAsync()
{
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Binding/Gacha/HistoryWish.cs b/src/Snap.Hutao/Snap.Hutao/Model/Binding/Gacha/HistoryWish.cs
index ec094ad690..2b55acd4f9 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Binding/Gacha/HistoryWish.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Binding/Gacha/HistoryWish.cs
@@ -13,7 +13,12 @@ public class HistoryWish : WishBase
///
/// 版本
///
- public string Version { get; set; }
+ public string Version { get; set; } = default!;
+
+ ///
+ /// 卡池图片
+ ///
+ public Uri BannerImage { get; set; } = default!;
///
/// 五星Up
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Binding/LaunchGame/LaunchScheme.cs b/src/Snap.Hutao/Snap.Hutao/Model/Binding/LaunchGame/LaunchScheme.cs
new file mode 100644
index 0000000000..4a29e9b934
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Binding/LaunchGame/LaunchScheme.cs
@@ -0,0 +1,42 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Model.Binding.LaunchGame;
+
+///
+/// 服务器方案
+///
+///
+/// 启动方案
+///
+public class LaunchScheme
+{
+ ///
+ /// 构造一个新的启动方案
+ ///
+ /// 名称
+ /// 通道
+ /// 通道描述字符串
+ /// 子通道
+ public LaunchScheme(string name, string channel, string subChannel)
+ {
+ Name = name;
+ Channel = channel;
+ SubChannel = subChannel;
+ }
+
+ ///
+ /// 名称
+ ///
+ public string Name { get; set; }
+
+ ///
+ /// 通道
+ ///
+ public string Channel { get; set; }
+
+ ///
+ /// 子通道
+ ///
+ public string SubChannel { get; set; }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Binding/LaunchGame/SchemeType.cs b/src/Snap.Hutao/Snap.Hutao/Model/Binding/LaunchGame/SchemeType.cs
new file mode 100644
index 0000000000..8e2a66d139
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Binding/LaunchGame/SchemeType.cs
@@ -0,0 +1,25 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Model.Binding.LaunchGame;
+
+///
+/// 启动类型
+///
+public enum SchemeType
+{
+ ///
+ /// 国际服
+ ///
+ Mihoyo,
+
+ ///
+ /// 国服官服
+ ///
+ Officical,
+
+ ///
+ /// 渠道服
+ ///
+ Bilibili,
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/GameAccount.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/GameAccount.cs
new file mode 100644
index 0000000000..97fe2ed76c
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/GameAccount.cs
@@ -0,0 +1,47 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Core.Database;
+using Snap.Hutao.Model.Binding.LaunchGame;
+using Snap.Hutao.Web.Hoyolab;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace Snap.Hutao.Model.Entity;
+
+///
+/// 游戏内账号
+///
+[Table("game_accounts")]
+public class GameAccount : ISelectable
+{
+ ///
+ /// 内部Id
+ ///
+ [Key]
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public Guid InnerId { get; set; }
+
+ ///
+ public bool IsSelected { get; set; }
+
+ ///
+ /// 对应的Uid
+ ///
+ public string? AttachUid { get; set; }
+
+ ///
+ /// 服务器类型
+ ///
+ public SchemeType Type { get; set; }
+
+ ///
+ /// 名称
+ ///
+ public string Name { get; set; } = default!;
+
+ ///
+ /// MIHOYOSDK_ADL_PROD_CN_h3123967166
+ ///
+ public string MihoyoSDK { get; set; } = default!;
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/User.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/User.cs
index ca000fda1a..24cc09559a 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/User.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/User.cs
@@ -40,4 +40,4 @@ public static User Create(Cookie cookie)
{
return new() { Cookie = cookie };
}
-}
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/GachaEvent.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/GachaEvent.cs
index 345cb15db6..3440e005bc 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/GachaEvent.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/GachaEvent.cs
@@ -20,6 +20,11 @@ public class GachaEvent
///
public string Version { get; set; } = default!;
+ ///
+ /// 卡池图
+ ///
+ public Uri Banner { get; set; } = default!;
+
///
/// 开始时间
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/NamedValue.cs b/src/Snap.Hutao/Snap.Hutao/Model/NamedValue.cs
new file mode 100644
index 0000000000..490d5cb530
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/NamedValue.cs
@@ -0,0 +1,35 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using CommunityToolkit.Mvvm.ComponentModel;
+
+namespace Snap.Hutao.Model;
+
+///
+/// 封装带有名称描述的值
+/// 在绑定枚举变量时非常有用
+///
+/// 包含值的类型
+public class NamedValue
+{
+ ///
+ /// 构造一个新的命名的值
+ ///
+ /// 命名
+ /// 值
+ public NamedValue(string name, T value)
+ {
+ Name = name;
+ Value = value;
+ }
+
+ ///
+ /// 名称
+ ///
+ public string Name { get; }
+
+ ///
+ /// 值
+ ///
+ public T Value { get; }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Selectable.cs b/src/Snap.Hutao/Snap.Hutao/Model/Selectable.cs
index a3c12bfaf6..1614749f17 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Selectable.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Selectable.cs
@@ -46,4 +46,4 @@ public bool IsSelected
/// 存放的对象
///
public T Value { get => value; set => SetProperty(ref this.value, value); }
-}
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/HistoryWishBuilder.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/HistoryWishBuilder.cs
index 2f2e51dd69..492f125c93 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/HistoryWishBuilder.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/HistoryWishBuilder.cs
@@ -148,6 +148,7 @@ public HistoryWish ToHistoryWish()
// fill
Version = gachaEvent.Version,
+ BannerImage = gachaEvent.Banner,
OrangeUpList = orangeUpCounter.ToStatisticsList(),
PurpleUpList = purpleUpCounter.ToStatisticsList(),
OrangeList = orangeCounter.ToStatisticsList(),
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs
index e5ef503f0b..722e2eb2f7 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs
@@ -6,10 +6,12 @@
using Snap.Hutao.Context.Database;
using Snap.Hutao.Core;
using Snap.Hutao.Core.Database;
+using Snap.Hutao.Core.IO.Ini;
using Snap.Hutao.Core.Threading;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Service.Game.Locator;
using Snap.Hutao.Service.Game.Unlocker;
+using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
@@ -27,6 +29,8 @@ internal class GameService : IGameService
private readonly IMemoryCache memoryCache;
private readonly SemaphoreSlim gameSemaphore = new(1);
+ private ObservableCollection? gameAccounts;
+
///
/// 构造一个新的游戏服务
///
@@ -129,51 +133,77 @@ public void OverwriteGamePath(string path)
}
///
- public async ValueTask LaunchAsync(LaunchConfiguration configuration)
+ public MultiChannel GetMultiChannel()
{
- (bool isOk, string gamePath) = await GetGamePathAsync().ConfigureAwait(false);
+ string gamePath = GetGamePathSkipLocator();
+ string configPath = Path.Combine(gamePath, "config.ini");
- if (isOk)
+ using (FileStream stream = File.OpenRead(configPath))
{
- if (gameSemaphore.CurrentCount == 0)
- {
- return;
- }
+ List elements = IniSerializer.Deserialize(stream).ToList();
+ string? channel = elements.OfType().FirstOrDefault(p => p.Key == "channel")?.Value;
+ string? subChannel = elements.OfType().FirstOrDefault(p => p.Key == "sub_channel")?.Value;
+
+ return new(channel, subChannel);
+ }
+ }
+
+ public async Task> GetGameAccountCollectionAsync()
+ {
+ if (gameAccounts == null)
+ {
+
+ }
+
+ return gameAccounts;
+ }
+
+ ///
+ public async ValueTask LaunchAsync(LaunchConfiguration configuration)
+ {
+ if (gameSemaphore.CurrentCount == 0)
+ {
+ return;
+ }
+
+ string gamePath = GetGamePathSkipLocator();
- string commandLine = new CommandLineBuilder()
- .Append("-window-mode", configuration.WindowMode)
- .Append("-screen-fullscreen", configuration.IsFullScreen ? 1 : 0)
- .Append("-screen-width", configuration.ScreenWidth)
- .Append("-screen-height", configuration.ScreenHeight)
- .Append("-monitor", configuration.Monitor)
- .Build();
+ string commandLine = new CommandLineBuilder()
+ .AppendIf("-popupwindow", configuration.IsBorderless)
+ .Append("-screen-fullscreen", configuration.IsFullScreen ? 1 : 0)
+ .Append("-screen-width", configuration.ScreenWidth)
+ .Append("-screen-height", configuration.ScreenHeight)
+ .Append("-monitor", configuration.Monitor)
+ .Build();
- Process game = new()
+ Process game = new()
+ {
+ StartInfo = new()
{
- StartInfo = new()
- {
- Arguments = commandLine,
- FileName = gamePath,
- UseShellExecute = true,
- Verb = "runas",
- WorkingDirectory = Path.GetDirectoryName(gamePath),
- },
- };
-
- using (await gameSemaphore.EnterAsync().ConfigureAwait(false))
+ Arguments = commandLine,
+ FileName = gamePath,
+ UseShellExecute = true,
+ Verb = "runas",
+ WorkingDirectory = Path.GetDirectoryName(gamePath),
+ },
+ };
+
+ using (await gameSemaphore.EnterAsync().ConfigureAwait(false))
+ {
+ if (configuration.UnlockFPS)
{
- if (configuration.UnlockFPS)
- {
- IGameFpsUnlocker unlocker = new GameFpsUnlocker(game, configuration.TargetFPS);
+ IGameFpsUnlocker unlocker = new GameFpsUnlocker(game, configuration.TargetFPS);
- await unlocker.UnlockAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromMilliseconds(10000), TimeSpan.FromMilliseconds(2000)).ConfigureAwait(false);
- }
- else
+ TimeSpan findModuleDelay = TimeSpan.FromMilliseconds(100);
+ TimeSpan findModuleLimit = TimeSpan.FromMilliseconds(10000);
+ TimeSpan adjustFpsDelay = TimeSpan.FromMilliseconds(2000);
+ await unlocker.UnlockAsync(findModuleDelay, findModuleLimit, adjustFpsDelay).ConfigureAwait(false);
+ }
+ else
+ {
+ if (game.Start())
{
- if (game.Start())
- {
- await game.WaitForExitAsync().ConfigureAwait(false);
- }
+ await game.WaitForExitAsync().ConfigureAwait(false);
}
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/IGameService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/IGameService.cs
index 966aed6c62..1302009170 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/IGameService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/IGameService.cs
@@ -22,6 +22,12 @@ internal interface IGameService
/// 游戏路径,当路径无效时会设置并返回
string GetGamePathSkipLocator();
+ ///
+ /// 获取多通道值
+ ///
+ /// 多通道值
+ MultiChannel GetMultiChannel();
+
///
/// 异步启动
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchConfiguration.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchConfiguration.cs
index d28923b1db..9c62a35945 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchConfiguration.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchConfiguration.cs
@@ -14,9 +14,9 @@ internal struct LaunchConfiguration
public bool IsFullScreen { get; set; }
///
- /// Override fullscreen windowed mode. Accepted values are exclusive or borderless.
+ /// 是否为无边框窗口
///
- public string WindowMode { get; private set; }
+ public bool IsBorderless { get; private set; }
///
/// 是否启用解锁帧率
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/MultiChannel.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/MultiChannel.cs
new file mode 100644
index 0000000000..b50a2c3f29
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/MultiChannel.cs
@@ -0,0 +1,31 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Service.Game;
+
+///
+/// 多通道
+///
+public struct MultiChannel
+{
+ ///
+ /// 通道
+ ///
+ public string Channel;
+
+ ///
+ /// 子通道
+ ///
+ public string SubChannel;
+
+ ///
+ /// 构造一个新的多通道
+ ///
+ /// 通道
+ /// 子通道
+ public MultiChannel(string? channel, string? subChannel)
+ {
+ Channel = channel ?? string.Empty;
+ SubChannel = subChannel ?? string.Empty;
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Navigation/INavigationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/INavigationService.cs
index 19ae3f5811..d61470cb21 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Navigation/INavigationService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/INavigationService.cs
@@ -72,4 +72,9 @@ Task NavigateAsync(INavigationAwaiter data, bool syncNa
/// 同步的页面类型
/// 是否同步成功
bool SyncSelectedNavigationViewItemWith(Type pageType);
+
+ ///
+ /// 尽可能尝试返回
+ ///
+ void GoBack();
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationService.cs
index 906507ce77..576a43c559 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationService.cs
@@ -178,6 +178,18 @@ public void Initialize(NavigationView navigationView, Frame frame)
NavigationView.IsPaneOpen = LocalSetting.Get(SettingKeys.IsNavPaneOpen, true);
}
+ ///
+ public void GoBack()
+ {
+ bool canGoBack = Frame?.CanGoBack ?? false;
+
+ if (canGoBack)
+ {
+ Frame!.GoBack();
+ SyncSelectedNavigationViewItemWith(Frame.Content.GetType());
+ }
+ }
+
///
/// 遍历所有子菜单项
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs
index 44931981ce..ac245f8924 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs
@@ -2,7 +2,9 @@
// Licensed under the MIT license.
using CommunityToolkit.Mvvm.Messaging;
+using Microsoft.Extensions.DependencyInjection;
using Snap.Hutao.Context.Database;
+using Snap.Hutao.Core.Database;
using Snap.Hutao.Core.Threading;
using Snap.Hutao.Web.Hoyolab;
using Snap.Hutao.Web.Hoyolab.Bbs.User;
@@ -20,10 +22,7 @@ namespace Snap.Hutao.Service.User;
[Injection(InjectAs.Singleton, typeof(IUserService))]
internal class UserService : IUserService
{
- private readonly AppDbContext appDbContext;
- private readonly UserClient userClient;
- private readonly BindingClient bindingClient;
- private readonly AuthClient authClient;
+ private readonly IServiceScopeFactory scopeFactory;
private readonly IMessenger messenger;
private BindingUser? currentUser;
@@ -32,22 +31,11 @@ internal class UserService : IUserService
///
/// 构造一个新的用户服务
///
- /// 应用程序数据库上下文
- /// 用户客户端
- /// 角色客户端
- /// 验证客户端
+ /// 范围工厂
/// 消息器
- public UserService(
- AppDbContext appDbContext,
- UserClient userClient,
- BindingClient bindingClient,
- AuthClient authClient,
- IMessenger messenger)
+ public UserService(IServiceScopeFactory scopeFactory, IMessenger messenger)
{
- this.appDbContext = appDbContext;
- this.userClient = userClient;
- this.bindingClient = bindingClient;
- this.authClient = authClient;
+ this.scopeFactory = scopeFactory;
this.messenger = messenger;
}
@@ -62,44 +50,54 @@ public BindingUser? Current
return;
}
- // only update when not processing a deletion
- if (value != null)
+ using (IServiceScope scope = scopeFactory.CreateScope())
{
+ AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService();
+
+ // only update when not processing a deletion
+ if (value != null)
+ {
+ if (currentUser != null)
+ {
+ currentUser.IsSelected = false;
+ appDbContext.Users.Update(currentUser.Entity);
+ appDbContext.SaveChanges();
+ }
+ }
+
+ Message.UserChangedMessage message = new() { OldValue = currentUser, NewValue = value };
+
+ // 当删除到无用户时也能正常反应状态
+ currentUser = value;
+
if (currentUser != null)
{
- currentUser.IsSelected = false;
+ currentUser.IsSelected = true;
appDbContext.Users.Update(currentUser.Entity);
appDbContext.SaveChanges();
}
- }
- Message.UserChangedMessage message = new() { OldValue = currentUser, NewValue = value };
-
- // 当删除到无用户时也能正常反应状态
- currentUser = value;
-
- if (currentUser != null)
- {
- currentUser.IsSelected = true;
- appDbContext.Users.Update(currentUser.Entity);
- appDbContext.SaveChanges();
+ messenger.Send(message);
}
-
- messenger.Send(message);
}
}
///
- public Task RemoveUserAsync(BindingUser user)
+ public async Task RemoveUserAsync(BindingUser user)
{
+ await Task.Yield();
Must.NotNull(userCollection!);
// Sync cache
userCollection.Remove(user);
// Sync database
- appDbContext.Users.Remove(user.Entity);
- return appDbContext.SaveChangesAsync();
+ using (IServiceScope scope = scopeFactory.CreateScope())
+ {
+ AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService();
+
+ appDbContext.Users.RemoveAndSave(user.Entity);
+ }
}
///
@@ -109,21 +107,27 @@ public async Task> GetUserCollectionAsync()
{
List users = new();
- foreach (Model.Entity.User entity in appDbContext.Users)
+ using (IServiceScope scope = scopeFactory.CreateScope())
{
- BindingUser? initialized = await BindingUser
- .ResumeAsync(entity, userClient, bindingClient)
- .ConfigureAwait(false);
+ AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService();
+ UserClient userClient = scope.ServiceProvider.GetRequiredService();
+ BindingClient bindingClient = scope.ServiceProvider.GetRequiredService();
- if (initialized != null)
+ foreach (Model.Entity.User entity in appDbContext.Users)
{
- users.Add(initialized);
- }
- else
- {
- // User is unable to be initialized, remove it.
- appDbContext.Users.Remove(entity);
- await appDbContext.SaveChangesAsync().ConfigureAwait(false);
+ BindingUser? initialized = await BindingUser
+ .ResumeAsync(entity, userClient, bindingClient)
+ .ConfigureAwait(false);
+
+ if (initialized != null)
+ {
+ users.Add(initialized);
+ }
+ else
+ {
+ // User is unable to be initialized, remove it.
+ appDbContext.Users.RemoveAndSave(entity);
+ }
}
}
@@ -150,24 +154,27 @@ public async Task> ProcessInputCookieAsync
// 检查 uid 对应用户是否存在
if (UserHelper.TryGetUserByUid(userCollection, uid, out BindingUser? userWithSameUid))
{
- // 检查 stoken 是否存在
- if (cookie.ContainsSToken())
+ using (IServiceScope scope = scopeFactory.CreateScope())
{
- // insert stoken
- userWithSameUid.UpdateSToken(uid, cookie);
- appDbContext.Users.Update(userWithSameUid.Entity);
- appDbContext.SaveChanges();
+ AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService();
- return new(UserOptionResult.Upgraded, uid);
- }
+ // 检查 stoken 是否存在
+ if (cookie.ContainsSToken())
+ {
+ // insert stoken
+ userWithSameUid.UpdateSToken(uid, cookie);
+ appDbContext.Users.UpdateAndSave(userWithSameUid.Entity);
- if (cookie.ContainsLTokenAndCookieToken())
- {
- userWithSameUid.Cookie = cookie;
- appDbContext.Users.Update(userWithSameUid.Entity);
- appDbContext.SaveChanges();
+ return new(UserOptionResult.Upgraded, uid);
+ }
+
+ if (cookie.ContainsLTokenAndCookieToken())
+ {
+ userWithSameUid.Cookie = cookie;
+ appDbContext.Users.UpdateAndSave(userWithSameUid.Entity);
- return new(UserOptionResult.Updated, uid);
+ return new(UserOptionResult.Updated, uid);
+ }
}
}
else if (cookie.ContainsLTokenAndCookieToken())
@@ -184,7 +191,8 @@ private async Task TryAddMultiTokenAsync(Cookie cookie, string uid)
if (cookie.TryGetLoginTicket(out string? loginTicket))
{
// get multitoken
- Dictionary multiToken = await authClient
+ Dictionary multiToken = await Ioc.Default
+ .GetRequiredService()
.GetMultiTokenByLoginTicketAsync(loginTicket, uid, default)
.ConfigureAwait(false);
@@ -198,22 +206,27 @@ private async Task TryAddMultiTokenAsync(Cookie cookie, string uid)
private async Task> TryCreateUserAndAddAsync(ObservableCollection users, Cookie cookie)
{
- BindingUser? newUser = await BindingUser.CreateAsync(cookie, userClient, bindingClient).ConfigureAwait(false);
- if (newUser != null)
+ using (IServiceScope scope = scopeFactory.CreateScope())
{
- // Sync cache
- await ThreadHelper.SwitchToMainThreadAsync();
- users.Add(newUser);
+ AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService();
+ UserClient userClient = scope.ServiceProvider.GetRequiredService();
+ BindingClient bindingClient = scope.ServiceProvider.GetRequiredService();
- // Sync database
- appDbContext.Users.Add(newUser.Entity);
- await appDbContext.SaveChangesAsync().ConfigureAwait(false);
+ BindingUser? newUser = await BindingUser.CreateAsync(cookie, userClient, bindingClient).ConfigureAwait(false);
+ if (newUser != null)
+ {
+ // Sync cache
+ await ThreadHelper.SwitchToMainThreadAsync();
+ users.Add(newUser);
- return new(UserOptionResult.Added, newUser.UserInfo!.Uid);
- }
- else
- {
- return new(UserOptionResult.Invalid, null!);
+ // Sync database
+ appDbContext.Users.AddAndSave(newUser.Entity);
+ return new(UserOptionResult.Added, newUser.UserInfo!.Uid);
+ }
+ else
+ {
+ return new(UserOptionResult.Invalid, null!);
+ }
}
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj
index 4558210a55..05b1ee5862 100644
--- a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj
+++ b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj
@@ -70,6 +70,8 @@
+
+
@@ -148,6 +150,16 @@
+
+
+ MSBuild:Compile
+
+
+
+
+ MSBuild:Compile
+
+
MSBuild:Compile
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Control/StatisticsCard.xaml b/src/Snap.Hutao/Snap.Hutao/View/Control/StatisticsCard.xaml
index 730cb2eedb..de6d74cb36 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Control/StatisticsCard.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/Control/StatisticsCard.xaml
@@ -138,7 +138,8 @@
Style="{StaticResource SubtitleTextBlockStyle}"/>
-
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/GachaLogPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/GachaLogPage.xaml
index 548c31d23a..a268a9682f 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Page/GachaLogPage.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/GachaLogPage.xaml
@@ -241,8 +241,20 @@
+
+
+
+
+
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/LaunchGamePage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/LaunchGamePage.xaml
index 2d400f9edd..de97920b9a 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Page/LaunchGamePage.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/LaunchGamePage.xaml
@@ -21,7 +21,7 @@
-
+
@@ -37,6 +37,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+ Background="{StaticResource SystemControlAcrylicElementMediumHighBrush}">
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/LoginMihoyoBBSPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/LoginMihoyoBBSPage.xaml
new file mode 100644
index 0000000000..c0f0f58b5f
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/LoginMihoyoBBSPage.xaml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/LoginMihoyoBBSPage.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Page/LoginMihoyoBBSPage.xaml.cs
new file mode 100644
index 0000000000..6f8d65383e
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/LoginMihoyoBBSPage.xaml.cs
@@ -0,0 +1,83 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Microsoft.UI.Xaml;
+using Microsoft.Web.WebView2.Core;
+using Snap.Hutao.Core.Threading;
+using Snap.Hutao.Service.Abstraction;
+using Snap.Hutao.Service.Navigation;
+using Snap.Hutao.Service.User;
+using Snap.Hutao.Web.Hoyolab;
+
+namespace Snap.Hutao.View.Page;
+
+///
+/// 登录米游社页面
+///
+public sealed partial class LoginMihoyoBBSPage : Microsoft.UI.Xaml.Controls.Page
+{
+ private const string CookieSite = "https://bbs.mihoyo.com";
+ private const string Website = "https://bbs.mihoyo.com/ys/";
+
+ ///
+ /// 构造一个新的登录米游社页面
+ ///
+ public LoginMihoyoBBSPage()
+ {
+ InitializeComponent();
+ }
+
+ [SuppressMessage("", "VSTHRD100")]
+ private async void OnRootLoaded(object sender, RoutedEventArgs e)
+ {
+ await WebView.EnsureCoreWebView2Async();
+
+ CoreWebView2CookieManager manager = WebView.CoreWebView2.CookieManager;
+ IReadOnlyList cookies = await manager.GetCookiesAsync(CookieSite);
+ foreach (CoreWebView2Cookie item in cookies)
+ {
+ manager.DeleteCookie(item);
+ }
+
+ WebView.CoreWebView2.Navigate(Website);
+ }
+
+ private async Task HandleCurrentCookieAsync()
+ {
+ CoreWebView2CookieManager manager = WebView.CoreWebView2.CookieManager;
+ IReadOnlyList cookies = await manager.GetCookiesAsync(CookieSite);
+
+ Cookie cookie = Cookie.FromCoreWebView2Cookies(cookies);
+ IUserService userService = Ioc.Default.GetRequiredService();
+ (UserOptionResult result, string nickname) = await userService.ProcessInputCookieAsync(cookie).ConfigureAwait(false);
+
+ Ioc.Default.GetRequiredService().GoBack();
+ IInfoBarService infoBarService = Ioc.Default.GetRequiredService();
+
+ switch (result)
+ {
+ case UserOptionResult.Added:
+ infoBarService.Success($"用户 [{nickname}] 添加成功");
+ break;
+ case UserOptionResult.Incomplete:
+ infoBarService.Information($"此 Cookie 不完整,操作失败");
+ break;
+ case UserOptionResult.Invalid:
+ infoBarService.Information($"此 Cookie 无法,操作失败");
+ break;
+ case UserOptionResult.Updated:
+ infoBarService.Success($"用户 [{nickname}] 更新成功");
+ break;
+ case UserOptionResult.Upgraded:
+ infoBarService.Information($"用户 [{nickname}] 升级成功");
+ break;
+ default:
+ throw Must.NeverHappen();
+ }
+ }
+
+ private void CookieButtonClick(object sender, RoutedEventArgs e)
+ {
+ HandleCurrentCookieAsync().SafeForget();
+ }
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/LoginMihoyoUserPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/LoginMihoyoUserPage.xaml
new file mode 100644
index 0000000000..7281df7a45
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/LoginMihoyoUserPage.xaml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/LoginMihoyoUserPage.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Page/LoginMihoyoUserPage.xaml.cs
new file mode 100644
index 0000000000..8b089698d2
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/LoginMihoyoUserPage.xaml.cs
@@ -0,0 +1,82 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Microsoft.UI.Xaml;
+using Microsoft.Web.WebView2.Core;
+using Snap.Hutao.Core.Threading;
+using Snap.Hutao.Service.Abstraction;
+using Snap.Hutao.Service.Navigation;
+using Snap.Hutao.Service.User;
+using Snap.Hutao.Web.Hoyolab;
+
+namespace Snap.Hutao.View.Page;
+
+///
+/// 登录米哈游通行证页面
+///
+public sealed partial class LoginMihoyoUserPage : Microsoft.UI.Xaml.Controls.Page
+{
+ ///
+ /// 构造一个新的登录米哈游通行证页面
+ ///
+ public LoginMihoyoUserPage()
+ {
+ InitializeComponent();
+ }
+
+ [SuppressMessage("", "VSTHRD100")]
+ private async void OnRootLoaded(object sender, RoutedEventArgs e)
+ {
+ await WebView.EnsureCoreWebView2Async();
+ WebView.CoreWebView2.SourceChanged += OnCoreWebView2SourceChanged;
+
+ CoreWebView2CookieManager manager = WebView.CoreWebView2.CookieManager;
+ IReadOnlyList cookies = await manager.GetCookiesAsync("https://user.mihoyo.com");
+ foreach (CoreWebView2Cookie item in cookies)
+ {
+ manager.DeleteCookie(item);
+ }
+
+ WebView.CoreWebView2.Navigate("https://user.mihoyo.com/#/login/password");
+ }
+
+ [SuppressMessage("", "VSTHRD100")]
+ private async void OnCoreWebView2SourceChanged(CoreWebView2 sender, CoreWebView2SourceChangedEventArgs args)
+ {
+ if (sender != null)
+ {
+ if (sender.Source.ToString() == "https://user.mihoyo.com/#/account/home")
+ {
+ await HandleCurrentCookieAsync().ConfigureAwait(false);
+ }
+ }
+ }
+
+ private async Task HandleCurrentCookieAsync()
+ {
+ CoreWebView2CookieManager manager = WebView.CoreWebView2.CookieManager;
+ IReadOnlyList cookies = await manager.GetCookiesAsync("https://user.mihoyo.com");
+ WebView.CoreWebView2.SourceChanged -= OnCoreWebView2SourceChanged;
+
+ Cookie cookie = Cookie.FromCoreWebView2Cookies(cookies);
+ IUserService userService = Ioc.Default.GetRequiredService();
+ (UserOptionResult result, string nickname) = await userService.ProcessInputCookieAsync(cookie).ConfigureAwait(false);
+
+ Ioc.Default.GetRequiredService().GoBack();
+ IInfoBarService infoBarService = Ioc.Default.GetRequiredService();
+
+ if (result == UserOptionResult.Upgraded)
+ {
+ infoBarService.Information($"用户 [{nickname}] 的 Cookie 升级成功");
+ }
+ else
+ {
+ infoBarService.Warning("请先添加对应用户的米游社Cookie");
+ }
+ }
+
+ private void CookieButtonClick(object sender, RoutedEventArgs e)
+ {
+ HandleCurrentCookieAsync().SafeForget();
+ }
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/View/UserView.xaml b/src/Snap.Hutao/Snap.Hutao/View/UserView.xaml
index ec5bce4fa3..60c309c535 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/UserView.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/UserView.xaml
@@ -205,12 +205,16 @@
+ Text="Cookie 操作"/>
-
+
+
+
+
+
+
+
+
knownSchemes = new()
+ {
+ new LaunchScheme(name: "官方服 | 天空岛", channel: "1", subChannel: "1"),
+ new LaunchScheme(name: "渠道服 | 世界树", channel: "14", subChannel: "0"),
+
+ // new LaunchScheme(name: "国际服 | 暂不支持", channel: "1", subChannel: "0"),
+ };
+
+ private LaunchScheme? selectedScheme;
+ private ObservableCollection? gameAccounts;
+ private GameAccount? selectedGameAccount;
+ private bool isFullScreen;
+ private bool isBorderless;
+ private int screenWidth;
+ private int screenHeight;
+ private bool unlockFps;
+ private int targetFps;
+
+ ///
+ /// 构造一个新的启动游戏视图模型
+ ///
+ /// 游戏服务
+ /// 异步命令工厂
+ public LaunchGameViewModel(IGameService gameService, IAsyncRelayCommandFactory asyncRelayCommandFactory)
+ {
+ this.gameService = gameService;
+
+ OpenUICommand = asyncRelayCommandFactory.Create(OpenUIAsync);
+ LaunchCommand = asyncRelayCommandFactory.Create(LaunchAsync);
+ DetectGameAccountCommand = asyncRelayCommandFactory.Create(DetectGameAccountAsync);
+ ModifyGameAccountCommand = asyncRelayCommandFactory.Create(ModifyGameAccountAsync);
+ RemoveGameAccountCommand = asyncRelayCommandFactory.Create(RemoveGameAccountAsync);
+ }
+
///
public CancellationToken CancellationToken { get; set; }
-}
+
+ ///
+ /// 已知的服务器方案
+ ///
+ public List KnownSchemes { get => knownSchemes; }
+
+ ///
+ /// 当前选择的服务器方案
+ ///
+ public LaunchScheme? SelectedScheme { get => selectedScheme; set => SetProperty(ref selectedScheme, value); }
+
+ ///
+ /// 游戏账号集合
+ ///
+ public ObservableCollection? GameAccounts { get => gameAccounts; set => SetProperty(ref gameAccounts, value); }
+
+ ///
+ /// 选中的账号
+ ///
+ public GameAccount? SelectedGameAccount { get => selectedGameAccount; set => SetProperty(ref selectedGameAccount, value); }
+
+ ///
+ /// 全屏
+ ///
+ public bool IsFullScreen { get => isFullScreen; set => SetProperty(ref isFullScreen, value); }
+
+ ///
+ /// 无边框
+ ///
+ public bool IsBorderless { get => isBorderless; set => SetProperty(ref isBorderless, value); }
+
+ ///
+ /// 宽度
+ ///
+ public int ScreenWidth { get => screenWidth; set => SetProperty(ref screenWidth, value); }
+
+ ///
+ /// 高度
+ ///
+ public int ScreenHeight { get => screenHeight; set => SetProperty(ref screenHeight, value); }
+
+ ///
+ /// 解锁帧率
+ ///
+ public bool UnlockFps { get => unlockFps; set => SetProperty(ref unlockFps, value); }
+
+ ///
+ /// 目标帧率
+ ///
+ public int TargetFps { get => targetFps; set => SetProperty(ref targetFps, value); }
+
+ ///
+ /// 打开界面命令
+ ///
+ public ICommand OpenUICommand { get; }
+
+ ///
+ /// 启动游戏命令
+ ///
+ public ICommand LaunchCommand { get; }
+
+ ///
+ /// 检测游戏账号命令
+ ///
+ public ICommand DetectGameAccountCommand { get; }
+
+ ///
+ /// 修改游戏账号命令
+ ///
+ public ICommand ModifyGameAccountCommand { get; }
+
+ ///
+ /// 删除游戏账号命令
+ ///
+ public ICommand RemoveGameAccountCommand { get; }
+
+ private async Task OpenUIAsync()
+ {
+ (bool isOk, string gamePath) = await gameService.GetGamePathAsync().ConfigureAwait(false);
+
+ if (isOk)
+ {
+ MultiChannel multi = gameService.GetMultiChannel();
+ SelectedScheme = KnownSchemes.FirstOrDefault(s => s.Channel == multi.Channel && s.SubChannel == multi.SubChannel);
+ }
+ }
+
+ private async Task LaunchAsync()
+ {
+
+ }
+
+ private async Task DetectGameAccountAsync()
+ {
+
+ }
+
+ private async Task ModifyGameAccountAsync()
+ {
+
+ }
+
+ private async Task RemoveGameAccountAsync()
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/UserViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/UserViewModel.cs
index 2f76298c83..171dc8a9a7 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/UserViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/UserViewModel.cs
@@ -8,8 +8,10 @@
using Snap.Hutao.Factory.Abstraction;
using Snap.Hutao.Model.Binding;
using Snap.Hutao.Service.Abstraction;
+using Snap.Hutao.Service.Navigation;
using Snap.Hutao.Service.User;
using Snap.Hutao.View.Dialog;
+using Snap.Hutao.View.Page;
using Snap.Hutao.Web.Hoyolab;
using System.Collections.ObjectModel;
@@ -40,7 +42,8 @@ public UserViewModel(IUserService userService, IInfoBarService infoBarService, I
OpenUICommand = asyncRelayCommandFactory.Create(OpenUIAsync);
AddUserCommand = asyncRelayCommandFactory.Create(AddUserAsync);
- UpgradeToStokenCommand = asyncRelayCommandFactory.Create(UpgradeByLoginTicketAsync);
+ LoginMihoyoUserCommand = new RelayCommand(LoginMihoyoUser);
+ LoginMihoyoBBSCommand = new RelayCommand(LoginMihoyoBBS);
RemoveUserCommand = asyncRelayCommandFactory.Create(RemoveUserAsync);
CopyCookieCommand = new RelayCommand(CopyCookie);
}
@@ -78,7 +81,12 @@ public User? SelectedUser
///
/// 登录米哈游通行证升级到Stoken命令
///
- public ICommand UpgradeToStokenCommand { get; }
+ public ICommand LoginMihoyoUserCommand { get; }
+
+ ///
+ /// 登录米游社命令
+ ///
+ public ICommand LoginMihoyoBBSCommand { get; }
///
/// 移除用户命令
@@ -132,26 +140,14 @@ private async Task AddUserAsync()
}
}
- private async Task UpgradeByLoginTicketAsync()
+ private void LoginMihoyoUser()
{
- // Get cookie from user input
- MainWindow mainWindow = Ioc.Default.GetRequiredService();
- (bool isOk, Cookie addition) = await new UserAutoCookieDialog(mainWindow).GetInputCookieAsync().ConfigureAwait(false);
-
- // User confirms the input
- if (isOk)
- {
- (UserOptionResult result, string nickname) = await userService.ProcessInputCookieAsync(addition).ConfigureAwait(false);
+ Ioc.Default.GetRequiredService().Navigate(INavigationAwaiter.Default);
+ }
- if (result == UserOptionResult.Upgraded)
- {
- infoBarService.Information($"用户 [{nickname}] 的 Cookie 升级成功");
- }
- else
- {
- infoBarService.Warning("请先添加对应用户的米游社Cookie");
- }
- }
+ private void LoginMihoyoBBS()
+ {
+ Ioc.Default.GetRequiredService().Navigate(INavigationAwaiter.Default);
}
private async Task RemoveUserAsync(User? user)
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/RankInfo.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/RankInfo.cs
index c733daae1d..87f17bae47 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/RankInfo.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/RankInfo.cs
@@ -11,10 +11,10 @@ public class RankInfo
///
/// 造成伤害
///
- public ItemRate Damage { get; set; } = default!;
+ public RankValue Damage { get; set; } = default!;
///
/// 受到伤害
///
- public ItemRate TakeDamage { get; set; } = default!;
-}
+ public RankValue TakeDamage { get; set; } = default!;
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/RankValue.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/RankValue.cs
new file mode 100644
index 0000000000..1c7770c1d3
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/RankValue.cs
@@ -0,0 +1,35 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Web.Hutao.Model;
+
+///
+/// 伤害值
+///
+public class RankValue : ItemRate
+{
+ ///
+ /// 构造一个新的伤害值
+ ///
+ /// 物品
+ /// 伤害
+ /// 率
+ /// 角色率
+ [JsonConstructor]
+ public RankValue(int item, int value, double rate, double rateOnAvatar)
+ : base(item, rate)
+ {
+ Value = value;
+ RateOnAvatar = rateOnAvatar;
+ }
+
+ ///
+ /// 伤害值
+ ///
+ public int Value { get; set; }
+
+ ///
+ /// 角色比率
+ ///
+ public double RateOnAvatar { get; set; }
+}
\ No newline at end of file