From 7cf106ec503b2d3215294c5698c0f2b84d963391 Mon Sep 17 00:00:00 2001
From: Lightczx <1686188646@qq.com>
Date: Wed, 24 Apr 2024 17:12:34 +0800
Subject: [PATCH] avatar info optimization
---
.../Core/Diagnostics/MeasureExecutionToken.cs | 4 +-
.../Snap.Hutao/Core/IO/StreamReaderWriter.cs | 40 ++++++++
.../Snap.Hutao/Core/Logging/LogArgument.cs | 5 +
src/Snap.Hutao/Snap.Hutao/Program.cs | 1 +
.../Builder/AvatarViewBuilderExtension.cs | 2 +-
.../Factory/Builder/IWeaponViewBuilder.cs | 11 +++
.../Factory/Builder/WeaponViewBuilder.cs | 9 ++
.../Builder/WeaponViewBuilderExtension.cs | 98 +++++++++++++++++++
.../Factory/SummaryAvatarFactory.cs | 61 +++++-------
.../Factory/SummaryAvatarProperties.cs | 38 +++----
.../AvatarInfo/Factory/SummaryFactory.cs | 9 +-
.../Factory/SummaryFactoryMetadataContext.cs | 3 +-
.../AvatarInfo/Factory/SummaryHelper.cs | 15 +--
.../Factory/SummaryReliquaryFactory.cs | 9 +-
.../IMetadataDictionaryIdReliquarySource.cs | 12 +++
.../MetadataServiceContextExtension.cs | 5 +
.../Service/Metadata/MetadataService.cs | 44 +++++----
.../MetadataServiceDictionaryExtension.cs | 25 ++---
.../AvatarProperty/{Equip.cs => EquipView.cs} | 2 +-
.../ViewModel/AvatarProperty/ReliquaryView.cs | 2 +-
.../ViewModel/AvatarProperty/WeaponView.cs | 5 +-
21 files changed, 282 insertions(+), 118 deletions(-)
create mode 100644 src/Snap.Hutao/Snap.Hutao/Core/IO/StreamReaderWriter.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/Builder/IWeaponViewBuilder.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/Builder/WeaponViewBuilder.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/Builder/WeaponViewBuilderExtension.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryIdReliquarySource.cs
rename src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/{Equip.cs => EquipView.cs} (91%)
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Diagnostics/MeasureExecutionToken.cs b/src/Snap.Hutao/Snap.Hutao/Core/Diagnostics/MeasureExecutionToken.cs
index 4cc4692b..e5fe4cd2 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Diagnostics/MeasureExecutionToken.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Diagnostics/MeasureExecutionToken.cs
@@ -1,5 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
+using Snap.Hutao.Core.Logging;
+
namespace Snap.Hutao.Core.Diagnostics;
internal readonly struct MeasureExecutionToken : IDisposable
@@ -17,6 +19,6 @@ internal readonly struct MeasureExecutionToken : IDisposable
public void Dispose()
{
- logger.LogDebug("{Caller} toke {Time} ms", callerName, stopwatch.GetElapsedTime().TotalMilliseconds);
+ logger.LogColorizedDebug(("{Caller} toke {Time} ms", ConsoleColor.Gray), (callerName, ConsoleColor.Yellow), (stopwatch.GetElapsedTime().TotalMilliseconds, ConsoleColor.DarkGreen));
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/IO/StreamReaderWriter.cs b/src/Snap.Hutao/Snap.Hutao/Core/IO/StreamReaderWriter.cs
new file mode 100644
index 00000000..70690a23
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Core/IO/StreamReaderWriter.cs
@@ -0,0 +1,40 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using System.IO;
+
+namespace Snap.Hutao.Core.IO;
+
+internal sealed class StreamReaderWriter : IDisposable
+{
+ private readonly StreamReader reader;
+ private readonly StreamWriter writer;
+
+ public StreamReaderWriter(StreamReader reader, StreamWriter writer)
+ {
+ this.reader = reader;
+ this.writer = writer;
+ }
+
+ public StreamReader Reader { get => reader; }
+
+ public StreamWriter Writer { get => writer; }
+
+ ///
+ public ValueTask ReadLineAsync(CancellationToken token)
+ {
+ return reader.ReadLineAsync(token);
+ }
+
+ ///
+ public Task WriteAsync(string value)
+ {
+ return writer.WriteAsync(value);
+ }
+
+ public void Dispose()
+ {
+ writer.Dispose();
+ reader.Dispose();
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Logging/LogArgument.cs b/src/Snap.Hutao/Snap.Hutao/Core/Logging/LogArgument.cs
index 9ab67810..ee2e21d1 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Logging/LogArgument.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Logging/LogArgument.cs
@@ -21,6 +21,11 @@ internal readonly struct LogArgument
return new(argument);
}
+ public static implicit operator LogArgument(double argument)
+ {
+ return new(argument);
+ }
+
public static implicit operator LogArgument((object? Argument, ConsoleColor Foreground) tuple)
{
return new(tuple.Argument, tuple.Foreground);
diff --git a/src/Snap.Hutao/Snap.Hutao/Program.cs b/src/Snap.Hutao/Snap.Hutao/Program.cs
index 110500f0..8240cc83 100644
--- a/src/Snap.Hutao/Snap.Hutao/Program.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Program.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using Microsoft.UI.Xaml;
+using Snap.Hutao.Core;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using WinRT;
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/Builder/AvatarViewBuilderExtension.cs b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/Builder/AvatarViewBuilderExtension.cs
index 03ab9f7e..30c5806d 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/Builder/AvatarViewBuilderExtension.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/Builder/AvatarViewBuilderExtension.cs
@@ -12,7 +12,7 @@ namespace Snap.Hutao.Service.AvatarInfo.Factory.Builder;
internal static class AvatarViewBuilderExtension
{
- public static TBuilder ApplyCostumeIconOrDefault(this TBuilder builder, Web.Enka.Model.AvatarInfo avatarInfo, Avatar avatar)
+ public static TBuilder SetCostumeIconOrDefault(this TBuilder builder, Web.Enka.Model.AvatarInfo avatarInfo, Avatar avatar)
where TBuilder : IAvatarViewBuilder
{
if (avatarInfo.CostumeId.TryGetValue(out CostumeId id))
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/Builder/IWeaponViewBuilder.cs b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/Builder/IWeaponViewBuilder.cs
new file mode 100644
index 00000000..d3eaf724
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/Builder/IWeaponViewBuilder.cs
@@ -0,0 +1,11 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Core.Abstraction;
+
+namespace Snap.Hutao.Service.AvatarInfo.Factory.Builder;
+
+internal interface IWeaponViewBuilder : IBuilder
+{
+ ViewModel.AvatarProperty.WeaponView WeaponView { get; }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/Builder/WeaponViewBuilder.cs b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/Builder/WeaponViewBuilder.cs
new file mode 100644
index 00000000..b1e099e6
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/Builder/WeaponViewBuilder.cs
@@ -0,0 +1,9 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Service.AvatarInfo.Factory.Builder;
+
+internal sealed class WeaponViewBuilder : IWeaponViewBuilder
+{
+ public ViewModel.AvatarProperty.WeaponView WeaponView { get; } = new();
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/Builder/WeaponViewBuilderExtension.cs b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/Builder/WeaponViewBuilderExtension.cs
new file mode 100644
index 00000000..152829f8
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/Builder/WeaponViewBuilderExtension.cs
@@ -0,0 +1,98 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Core.Abstraction.Extension;
+using Snap.Hutao.Model;
+using Snap.Hutao.Model.Intrinsic;
+using Snap.Hutao.Model.Metadata.Converter;
+using Snap.Hutao.Model.Primitive;
+using Snap.Hutao.Web.Enka.Model;
+
+namespace Snap.Hutao.Service.AvatarInfo.Factory.Builder;
+
+internal static class WeaponViewBuilderExtension
+{
+ public static TBuilder SetAffixLevelNumber(this TBuilder builder, uint affixLevelNumber)
+ where TBuilder : IWeaponViewBuilder
+ {
+ return builder.Configure(b => b.WeaponView.AffixLevelNumber = affixLevelNumber);
+ }
+
+ public static TBuilder SetAffixDescription(this TBuilder builder, string? affixDescription)
+ where TBuilder : IWeaponViewBuilder
+ {
+ return builder.Configure(b => b.WeaponView.AffixDescription = affixDescription ?? string.Empty);
+ }
+
+ public static TBuilder SetAffixName(this TBuilder builder, string affixName)
+ where TBuilder : IWeaponViewBuilder
+ {
+ return builder.Configure(b => b.WeaponView.AffixName = affixName);
+ }
+
+ public static TBuilder SetDescription(this TBuilder builder, string description)
+ where TBuilder : IWeaponViewBuilder
+ {
+ return builder.Configure(b => b.WeaponView.Description = description);
+ }
+
+ public static TBuilder SetIcon(this TBuilder builder, Uri icon)
+ where TBuilder : IWeaponViewBuilder
+ {
+ return builder.Configure(b => b.WeaponView.Icon = icon);
+ }
+
+ public static TBuilder SetId(this TBuilder builder, WeaponId id)
+ where TBuilder : IWeaponViewBuilder
+ {
+ return builder.Configure(b => b.WeaponView.Id = id);
+ }
+
+ public static TBuilder SetLevel(this TBuilder builder, string level)
+ where TBuilder : IWeaponViewBuilder
+ {
+ return builder.Configure(b => b.WeaponView.Level = level);
+ }
+
+ public static TBuilder SetLevelNumber(this TBuilder builder, uint levelNumber)
+ where TBuilder : IWeaponViewBuilder
+ {
+ return builder.Configure(b => b.WeaponView.LevelNumber = levelNumber);
+ }
+
+ public static TBuilder SetMainProperty(this TBuilder builder, WeaponStat? mainStat)
+ where TBuilder : IWeaponViewBuilder
+ {
+ return builder.SetMainProperty(mainStat is not null ? FightPropertyFormat.ToNameValue(mainStat.AppendPropId, mainStat.StatValue) : NameValueDefaults.String);
+ }
+
+ public static TBuilder SetMainProperty(this TBuilder builder, NameValue mainProperty)
+ where TBuilder : IWeaponViewBuilder
+ {
+ return builder.Configure(b => b.WeaponView.MainProperty = mainProperty);
+ }
+
+ public static TBuilder SetName(this TBuilder builder, string name)
+ where TBuilder : IWeaponViewBuilder
+ {
+ return builder.Configure(b => b.WeaponView.Name = name);
+ }
+
+ public static TBuilder SetQuality(this TBuilder builder, QualityType quality)
+ where TBuilder : IWeaponViewBuilder
+ {
+ return builder.Configure(b => b.WeaponView.Quality = quality);
+ }
+
+ public static TBuilder SetSubProperty(this TBuilder builder, NameDescription subProperty)
+ where TBuilder : IWeaponViewBuilder
+ {
+ return builder.Configure(b => b.WeaponView.SubProperty = subProperty);
+ }
+
+ public static TBuilder SetWeaponType(this TBuilder builder, WeaponType weaponType)
+ where TBuilder : IWeaponViewBuilder
+ {
+ return builder.Configure(b => b.WeaponView.WeaponType = weaponType);
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryAvatarFactory.cs b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryAvatarFactory.cs
index 1be0eaee..e67cce99 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryAvatarFactory.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryAvatarFactory.cs
@@ -6,15 +6,13 @@ using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Intrinsic.Format;
using Snap.Hutao.Model.Metadata.Converter;
using Snap.Hutao.Service.AvatarInfo.Factory.Builder;
+using Snap.Hutao.ViewModel.AvatarProperty;
using Snap.Hutao.Web.Enka.Model;
using System.Runtime.InteropServices;
using EntityAvatarInfo = Snap.Hutao.Model.Entity.AvatarInfo;
using MetadataAvatar = Snap.Hutao.Model.Metadata.Avatar.Avatar;
using MetadataWeapon = Snap.Hutao.Model.Metadata.Weapon.Weapon;
using ModelAvatarInfo = Snap.Hutao.Web.Enka.Model.AvatarInfo;
-using PropertyAvatar = Snap.Hutao.ViewModel.AvatarProperty.AvatarView;
-using PropertyReliquary = Snap.Hutao.ViewModel.AvatarProperty.ReliquaryView;
-using PropertyWeapon = Snap.Hutao.ViewModel.AvatarProperty.WeaponView;
namespace Snap.Hutao.Service.AvatarInfo.Factory;
@@ -37,17 +35,17 @@ internal sealed class SummaryAvatarFactory
calculatorRefreshTime = avatarInfo.CalculatorRefreshTime;
}
- public static PropertyAvatar Create(SummaryFactoryMetadataContext context, EntityAvatarInfo avatarInfo)
+ public static AvatarView Create(SummaryFactoryMetadataContext context, EntityAvatarInfo avatarInfo)
{
return new SummaryAvatarFactory(context, avatarInfo).Create();
}
- public PropertyAvatar Create()
+ public AvatarView Create()
{
ReliquaryAndWeapon reliquaryAndWeapon = ProcessEquip(avatarInfo.EquipList.EmptyIfNull());
MetadataAvatar avatar = context.IdAvatarMap[avatarInfo.AvatarId];
- PropertyAvatar propertyAvatar = new AvatarViewBuilder()
+ AvatarView propertyAvatar = new AvatarViewBuilder()
.SetId(avatar.Id)
.SetName(avatar.Name)
.SetQuality(avatar.Quality)
@@ -65,7 +63,7 @@ internal sealed class SummaryAvatarFactory
.SetShowcaseRefreshTimeFormat(showcaseRefreshTime, SH.FormatServiceAvatarInfoSummaryShowcaseRefreshTimeFormat, SH.ServiceAvatarInfoSummaryShowcaseNotRefreshed)
.SetGameRecordRefreshTimeFormat(gameRecordRefreshTime, SH.FormatServiceAvatarInfoSummaryGameRecordRefreshTimeFormat, SH.ServiceAvatarInfoSummaryGameRecordNotRefreshed)
.SetCalculatorRefreshTimeFormat(calculatorRefreshTime, SH.FormatServiceAvatarInfoSummaryCalculatorRefreshTimeFormat, SH.ServiceAvatarInfoSummaryCalculatorNotRefreshed)
- .ApplyCostumeIconOrDefault(avatarInfo, avatar)
+ .SetCostumeIconOrDefault(avatarInfo, avatar)
.AvatarView;
return propertyAvatar;
@@ -73,8 +71,8 @@ internal sealed class SummaryAvatarFactory
private ReliquaryAndWeapon ProcessEquip(List equipments)
{
- List reliquaryList = [];
- PropertyWeapon? weapon = null;
+ List reliquaryList = [];
+ WeaponView? weapon = null;
foreach (ref readonly Equip equip in CollectionsMarshal.AsSpan(equipments))
{
@@ -92,7 +90,7 @@ internal sealed class SummaryAvatarFactory
return new(reliquaryList, weapon);
}
- private PropertyWeapon CreateWeapon(Equip equip)
+ private WeaponView CreateWeapon(Equip equip)
{
MetadataWeapon weapon = context.IdWeaponMap[equip.ItemId];
@@ -118,36 +116,29 @@ internal sealed class SummaryAvatarFactory
ArgumentNullException.ThrowIfNull(equip.Weapon);
- return new()
- {
- // NameIconDescription
- Name = weapon.Name,
- Icon = EquipIconConverter.IconNameToUri(weapon.Icon),
- Description = weapon.Description,
-
- // EquipBase
- Level = $"Lv.{equip.Weapon.Level.Value}",
- Quality = weapon.Quality,
- MainProperty = mainStat is not null
- ? FightPropertyFormat.ToNameValue(mainStat.AppendPropId, mainStat.StatValue)
- : NameValueDefaults.String,
-
- // Weapon
- Id = weapon.Id,
- LevelNumber = equip.Weapon.Level,
- SubProperty = subProperty,
- AffixLevelNumber = affixLevel + 1,
- AffixName = weapon.Affix?.Name ?? string.Empty,
- AffixDescription = weapon.Affix?.Descriptions.Single(a => a.Level == affixLevel).Description ?? string.Empty,
- };
+ return new WeaponViewBuilder()
+ .SetName(weapon.Name)
+ .SetIcon(EquipIconConverter.IconNameToUri(weapon.Icon))
+ .SetDescription(weapon.Description)
+ .SetLevel($"Lv.{equip.Weapon.Level.Value}")
+ .SetQuality(weapon.Quality)
+ .SetMainProperty(mainStat)
+ .SetId(weapon.Id)
+ .SetLevelNumber(equip.Weapon.Level)
+ .SetSubProperty(subProperty)
+ .SetAffixLevelNumber(affixLevel + 1)
+ .SetAffixName(weapon.Affix?.Name ?? string.Empty)
+ .SetAffixDescription(weapon.Affix?.Descriptions.Single(a => a.Level == affixLevel).Description)
+ .SetWeaponType(weapon.WeaponType)
+ .WeaponView;
}
private readonly struct ReliquaryAndWeapon
{
- public readonly List Reliquaries;
- public readonly PropertyWeapon? Weapon;
+ public readonly List Reliquaries;
+ public readonly WeaponView? Weapon;
- public ReliquaryAndWeapon(List reliquaries, PropertyWeapon? weapon)
+ public ReliquaryAndWeapon(List reliquaries, WeaponView? weapon)
{
Reliquaries = reliquaries;
Weapon = weapon;
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryAvatarProperties.cs b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryAvatarProperties.cs
index 0df0cc30..5b2e54dd 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryAvatarProperties.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryAvatarProperties.cs
@@ -13,11 +13,6 @@ namespace Snap.Hutao.Service.AvatarInfo.Factory;
[HighQuality]
internal static class SummaryAvatarProperties
{
- ///
- /// 创建角色属性
- ///
- /// 属性映射
- /// 列表
public static List Create(Dictionary? fightPropMap)
{
if (fightPropMap is null)
@@ -25,32 +20,27 @@ internal static class SummaryAvatarProperties
return [];
}
- AvatarProperty hpProp = ToAvatarProperty(FightProperty.FIGHT_PROP_BASE_HP, fightPropMap);
- AvatarProperty atkProp = ToAvatarProperty(FightProperty.FIGHT_PROP_BASE_ATTACK, fightPropMap);
- AvatarProperty defProp = ToAvatarProperty(FightProperty.FIGHT_PROP_BASE_DEFENSE, fightPropMap);
- AvatarProperty emProp = FightPropertyFormat.ToAvatarProperty(FightProperty.FIGHT_PROP_ELEMENT_MASTERY, fightPropMap);
- AvatarProperty critRateProp = FightPropertyFormat.ToAvatarProperty(FightProperty.FIGHT_PROP_CRITICAL, fightPropMap);
- AvatarProperty critDMGProp = FightPropertyFormat.ToAvatarProperty(FightProperty.FIGHT_PROP_CRITICAL_HURT, fightPropMap);
- AvatarProperty chargeEffProp = FightPropertyFormat.ToAvatarProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY, fightPropMap);
-
- List properties = new(9) { hpProp, atkProp, defProp, emProp, critRateProp, critDMGProp, chargeEffProp };
+ List properties =
+ [
+ ToAvatarProperty(FightProperty.FIGHT_PROP_BASE_HP, fightPropMap),
+ ToAvatarProperty(FightProperty.FIGHT_PROP_BASE_ATTACK, fightPropMap),
+ ToAvatarProperty(FightProperty.FIGHT_PROP_BASE_DEFENSE, fightPropMap),
+ FightPropertyFormat.ToAvatarProperty(FightProperty.FIGHT_PROP_ELEMENT_MASTERY, fightPropMap),
+ FightPropertyFormat.ToAvatarProperty(FightProperty.FIGHT_PROP_CRITICAL, fightPropMap),
+ FightPropertyFormat.ToAvatarProperty(FightProperty.FIGHT_PROP_CRITICAL_HURT, fightPropMap),
+ FightPropertyFormat.ToAvatarProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY, fightPropMap)
+ ];
// 元素伤害
- if (TryGetBonusFightProperty(fightPropMap, out FightProperty bonusProperty, out float value))
+ if (TryGetBonusFightProperty(fightPropMap, out FightProperty bonusProperty, out float value) && value > 0)
{
- if (value > 0)
- {
- properties.Add(FightPropertyFormat.ToAvatarProperty(bonusProperty, value));
- }
+ properties.Add(FightPropertyFormat.ToAvatarProperty(bonusProperty, value));
}
// 物伤 可以和其他元素伤害并存,所以分别判断
- if (fightPropMap.TryGetValue(FightProperty.FIGHT_PROP_PHYSICAL_ADD_HURT, out float addValue))
+ if (fightPropMap.TryGetValue(FightProperty.FIGHT_PROP_PHYSICAL_ADD_HURT, out float addValue) && addValue > 0)
{
- if (addValue > 0)
- {
- properties.Add(FightPropertyFormat.ToAvatarProperty(FightProperty.FIGHT_PROP_PHYSICAL_ADD_HURT, addValue));
- }
+ properties.Add(FightPropertyFormat.ToAvatarProperty(FightProperty.FIGHT_PROP_PHYSICAL_ADD_HURT, addValue));
}
return properties;
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryFactory.cs b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryFactory.cs
index a0c5ce88..7ff774a5 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryFactory.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryFactory.cs
@@ -28,11 +28,12 @@ internal sealed partial class SummaryFactory : ISummaryFactory
IOrderedEnumerable avatars = avatarInfos
.Where(a => !AvatarIds.IsPlayer(a.Info.AvatarId))
.Select(a => SummaryAvatarFactory.Create(context, a))
- .OrderByDescending(a => a.LevelNumber)
- .ThenByDescending(a => a.FetterLevel)
- .ThenBy(a => a.Element);
+ .OrderByDescending(a => a.Quality)
+ .ThenByDescending(a => a.LevelNumber)
+ .ThenBy(a => a.Element)
+ .ThenBy(a => a.Weapon?.WeaponType)
+ .ThenByDescending(a => a.FetterLevel);
- // TODO: thenby weapon type
return new()
{
Avatars = [.. avatars],
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryFactoryMetadataContext.cs b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryFactoryMetadataContext.cs
index df3040e7..6434f797 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryFactoryMetadataContext.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryFactoryMetadataContext.cs
@@ -18,6 +18,7 @@ internal sealed class SummaryFactoryMetadataContext : IMetadataContext,
IMetadataDictionaryIdReliquaryAffixWeightSource,
IMetadataDictionaryIdReliquaryMainPropertySource,
IMetadataDictionaryIdReliquarySubAffixSource,
+ IMetadataDictionaryIdReliquarySource,
IMetadataListReliquaryMainAffixLevelSource
{
public Dictionary IdAvatarMap { get; set; } = default!;
@@ -32,5 +33,5 @@ internal sealed class SummaryFactoryMetadataContext : IMetadataContext,
public List ReliquaryMainAffixLevels { get; set; } = default!;
- public List Reliquaries { get; set; } = default!;
+ public Dictionary IdReliquaryMap { get; set; } = default!;
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryHelper.cs b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryHelper.cs
index 6039d8fd..98e5eeca 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryHelper.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryHelper.cs
@@ -1,6 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
+using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Model.Primitive;
namespace Snap.Hutao.Service.AvatarInfo.Factory;
@@ -11,11 +12,6 @@ namespace Snap.Hutao.Service.AvatarInfo.Factory;
[HighQuality]
internal static class SummaryHelper
{
- ///
- /// 获取副属性对应的最大属性的Id
- ///
- /// 属性Id
- /// 最大属性Id
public static ReliquarySubAffixId GetAffixMaxId(in ReliquarySubAffixId appendId)
{
// axxxxx -> a
@@ -27,18 +23,13 @@ internal static class SummaryHelper
1 => 2,
2 => 3,
3 or 4 or 5 => 4,
- _ => throw Must.NeverHappen(),
+ _ => throw HutaoException.Throw($"不支持的 ReliquarySubAffixId: {appendId}"),
};
// axxxxb -> axxxx -> axxxx0 -> axxxxm
return ((appendId / 10) * 10) + max;
}
- ///
- /// 获取百分比属性副词条分数
- ///
- /// id
- /// 分数
public static float GetPercentSubAffixScore(in ReliquarySubAffixId appendId)
{
// 圣遗物相同类型副词条强化档位一共为 4/3/2 档
@@ -65,7 +56,7 @@ internal static class SummaryHelper
(1, 0) => 100F,
(1, 1) => 80F,
- _ => throw Must.NeverHappen($"Unexpected AppendId: {appendId.Value} Delta: {delta}"),
+ _ => throw HutaoException.Throw($"Unexpected AppendId: {appendId} Delta: {delta}"),
};
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryReliquaryFactory.cs b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryReliquaryFactory.cs
index ebb4129a..3998abd5 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryReliquaryFactory.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryReliquaryFactory.cs
@@ -13,9 +13,6 @@ using ModelAvatarInfo = Snap.Hutao.Web.Enka.Model.AvatarInfo;
namespace Snap.Hutao.Service.AvatarInfo.Factory;
-///
-/// 圣遗物工厂
-///
[HighQuality]
internal sealed class SummaryReliquaryFactory
{
@@ -37,7 +34,7 @@ internal sealed class SummaryReliquaryFactory
public ReliquaryView Create()
{
- MetadataReliquary reliquary = metadataContext.Reliquaries.Single(r => r.Ids.Contains(equip.ItemId));
+ MetadataReliquary reliquary = metadataContext.IdReliquaryMap[equip.ItemId];
ArgumentNullException.ThrowIfNull(equip.Reliquary);
List subProperty = equip.Reliquary.AppendPropIdList.EmptyIfNull().SelectList(CreateSubProperty);
@@ -74,10 +71,10 @@ internal sealed class SummaryReliquaryFactory
return result;
}
- private static int GetSecondaryAffixCount(MetadataReliquary reliquary, Web.Enka.Model.Reliquary enkaReliquary)
+ private static int GetSecondaryAffixCount(MetadataReliquary metaReliquary, Web.Enka.Model.Reliquary enkaReliquary)
{
// 强化词条个数
- return (reliquary.RankLevel, enkaReliquary.Level.Value) switch
+ return (metaReliquary.RankLevel, enkaReliquary.Level.Value) switch
{
(QualityType.QUALITY_ORANGE, > 20U) => 5,
(QualityType.QUALITY_ORANGE, > 16U) => 4,
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryIdReliquarySource.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryIdReliquarySource.cs
new file mode 100644
index 00000000..fb504da9
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryIdReliquarySource.cs
@@ -0,0 +1,12 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Model.Metadata.Reliquary;
+using Snap.Hutao.Model.Primitive;
+
+namespace Snap.Hutao.Service.Metadata.ContextAbstraction;
+
+internal interface IMetadataDictionaryIdReliquarySource
+{
+ public Dictionary IdReliquaryMap { get; set; }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/MetadataServiceContextExtension.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/MetadataServiceContextExtension.cs
index d50f85ac..8ec47d27 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/MetadataServiceContextExtension.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/MetadataServiceContextExtension.cs
@@ -62,6 +62,11 @@ internal static class MetadataServiceContextExtension
dictionaryIdMaterialSource.IdMaterialMap = await metadataService.GetIdToMaterialMapAsync(token).ConfigureAwait(false);
}
+ if (context is IMetadataDictionaryIdReliquarySource dictionaryIdReliquarySource)
+ {
+ dictionaryIdReliquarySource.IdReliquaryMap = await metadataService.GetIdToReliquaryMapAsync(token).ConfigureAwait(false);
+ }
+
if (context is IMetadataDictionaryIdReliquaryAffixWeightSource dictionaryIdReliquaryAffixWeightSource)
{
dictionaryIdReliquaryAffixWeightSource.IdReliquaryAffixWeightMap = await metadataService.GetIdToReliquaryAffixWeightMapAsync(token).ConfigureAwait(false);
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.cs
index 43f6e26c..75454d80 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.cs
@@ -6,6 +6,7 @@ using Snap.Hutao.Core;
using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
using Snap.Hutao.Core.Diagnostics;
using Snap.Hutao.Core.ExceptionService;
+using Snap.Hutao.Core.IO;
using Snap.Hutao.Core.IO.Hashing;
using Snap.Hutao.Core.Setting;
using Snap.Hutao.Service.Notification;
@@ -29,7 +30,7 @@ internal sealed partial class MetadataService : IMetadataService, IMetadataServi
private readonly TaskCompletionSource initializeCompletionSource = new();
- private readonly IHttpClientFactory httpClientFactory;
+ private readonly IServiceScopeFactory serviceScopeFactory;
private readonly ILogger logger;
private readonly MetadataOptions metadataOptions;
private readonly IInfoBarService infoBarService;
@@ -119,12 +120,16 @@ internal sealed partial class MetadataService : IMetadataService, IMetadataServi
Dictionary? metadataFileHashs;
try
{
- // Download meta check file
- using (HttpClient httpClient = httpClientFactory.CreateClient(nameof(MetadataService)))
+ using (IServiceScope scope = serviceScopeFactory.CreateScope())
{
- metadataFileHashs = await httpClient
- .GetFromJsonAsync>(metadataOptions.GetLocalizedRemoteFile(MetaFileName), options, token)
- .ConfigureAwait(false);
+ IHttpClientFactory httpClientFactory = scope.ServiceProvider.GetRequiredService();
+ using (HttpClient httpClient = httpClientFactory.CreateClient(nameof(MetadataService)))
+ {
+ // Download meta check file
+ metadataFileHashs = await httpClient
+ .GetFromJsonAsync>(metadataOptions.GetLocalizedRemoteFile(MetaFileName), options, token)
+ .ConfigureAwait(false);
+ }
}
if (metadataFileHashs is null)
@@ -180,26 +185,27 @@ internal sealed partial class MetadataService : IMetadataService, IMetadataServi
private async ValueTask DownloadMetadataSourceFilesAsync(string fileFullName, CancellationToken token)
{
Stream sourceStream;
- using (HttpClient httpClient = httpClientFactory.CreateClient(nameof(MetadataService)))
+ using (IServiceScope scope = serviceScopeFactory.CreateScope())
{
- sourceStream = await httpClient
- .GetStreamAsync(metadataOptions.GetLocalizedRemoteFile(fileFullName), token)
- .ConfigureAwait(false);
+ IHttpClientFactory httpClientFactory = scope.ServiceProvider.GetRequiredService();
+ using (HttpClient httpClient = httpClientFactory.CreateClient(nameof(MetadataService)))
+ {
+ sourceStream = await httpClient
+ .GetStreamAsync(metadataOptions.GetLocalizedRemoteFile(fileFullName), token)
+ .ConfigureAwait(false);
+ }
}
// Write stream while convert LF to CRLF
- using (StreamReader streamReader = new(sourceStream))
+ using (StreamReaderWriter readerWriter = new(new(sourceStream), File.CreateText(metadataOptions.GetLocalizedLocalFile(fileFullName))))
{
- using (StreamWriter streamWriter = File.CreateText(metadataOptions.GetLocalizedLocalFile(fileFullName)))
+ while (await readerWriter.ReadLineAsync(token).ConfigureAwait(false) is { } line)
{
- while (await streamReader.ReadLineAsync(token).ConfigureAwait(false) is { } line)
- {
- await streamWriter.WriteAsync(line).ConfigureAwait(false);
+ await readerWriter.WriteAsync(line).ConfigureAwait(false);
- if (!streamReader.EndOfStream)
- {
- await streamWriter.WriteAsync(StringLiterals.CRLF).ConfigureAwait(false);
- }
+ if (!readerWriter.Reader.EndOfStream)
+ {
+ await readerWriter.WriteAsync(StringLiterals.CRLF).ConfigureAwait(false);
}
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataServiceDictionaryExtension.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataServiceDictionaryExtension.cs
index b7720d1e..364808a5 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataServiceDictionaryExtension.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataServiceDictionaryExtension.cs
@@ -25,22 +25,12 @@ internal static class MetadataServiceDictionaryExtension
public static ValueTask> GetExtendedEquipAffixIdToReliquarySetMapAsync(this IMetadataService metadataService, CancellationToken token = default)
{
- return metadataService.FromCacheAsDictionaryAsync(
- FileNameReliquarySet,
- list => list.SelectMany(set => set.EquipAffixIds.Select(id => (Id: id, Set: set))),
- tuple => tuple.Id,
- tuple => tuple.Set,
- token);
+ return metadataService.FromCacheAsDictionaryAsync(FileNameReliquarySet, (List list) => list.SelectMany(set => set.EquipAffixIds, (set, id) => (Id: id, Set: set)), token);
}
public static ValueTask>> GetGroupIdToTowerLevelGroupMapAsync(this IMetadataService metadataService, CancellationToken token = default)
{
- return metadataService.FromCacheAsDictionaryAsync, TowerLevelGroupId, List>(
- FileNameTowerLevel,
- list => list.GroupBy(l => l.GroupId),
- g => g.Key,
- g => g.ToList(),
- token);
+ return metadataService.FromCacheAsDictionaryAsync(FileNameTowerLevel, (List list) => list.GroupBy(l => l.GroupId), g => g.Key, g => g.ToList(), token);
}
public static ValueTask> GetIdToAchievementMapAsync(this IMetadataService metadataService, CancellationToken token = default)
@@ -81,6 +71,11 @@ internal static class MetadataServiceDictionaryExtension
return metadataService.FromCacheAsDictionaryAsync(FileNameMaterial, a => a.Id, token);
}
+ public static ValueTask> GetIdToReliquaryMapAsync(this IMetadataService metadataService, CancellationToken token = default)
+ {
+ return metadataService.FromCacheAsDictionaryAsync(FileNameReliquary, (List list) => list.SelectMany(r => r.Ids, (r, i) => (Index: i, Reliquary: r)), token);
+ }
+
public static ValueTask> GetIdToReliquaryAffixWeightMapAsync(this IMetadataService metadataService, CancellationToken token = default)
{
return metadataService.FromCacheAsDictionaryAsync(FileNameReliquaryAffixWeight, r => r.AvatarId, token);
@@ -177,6 +172,12 @@ internal static class MetadataServiceDictionaryExtension
return metadataService.MemoryCache.Set(cacheKey, dict);
}
+ private static ValueTask> FromCacheAsDictionaryAsync(this IMetadataService metadataService, string fileName, Func, IEnumerable<(TKey Key, TValue Value)>> listSelector, CancellationToken token)
+ where TKey : notnull
+ {
+ return FromCacheAsDictionaryAsync(metadataService, fileName, listSelector, kvp => kvp.Key, kvp => kvp.Value, token);
+ }
+
private static async ValueTask> FromCacheAsDictionaryAsync(this IMetadataService metadataService, string fileName, Func, IEnumerable> listSelector, Func keySelector, Func valueSelector, CancellationToken token)
where TKey : notnull
{
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/Equip.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/EquipView.cs
similarity index 91%
rename from src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/Equip.cs
rename to src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/EquipView.cs
index e797f7db..708045d0 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/Equip.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/EquipView.cs
@@ -10,7 +10,7 @@ namespace Snap.Hutao.ViewModel.AvatarProperty;
/// 装备基类
///
[HighQuality]
-internal abstract class Equip : NameIconDescription
+internal abstract class EquipView : NameIconDescription
{
///
/// 等级
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/ReliquaryView.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/ReliquaryView.cs
index 3d14a34a..c8a2dc24 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/ReliquaryView.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/ReliquaryView.cs
@@ -7,7 +7,7 @@ namespace Snap.Hutao.ViewModel.AvatarProperty;
/// 圣遗物
///
[HighQuality]
-internal sealed class ReliquaryView : Equip
+internal sealed class ReliquaryView : EquipView
{
///
/// 初始词条
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/WeaponView.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/WeaponView.cs
index 495b0b9d..08a59244 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/WeaponView.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/WeaponView.cs
@@ -3,6 +3,7 @@
using Snap.Hutao.Model;
using Snap.Hutao.Model.Calculable;
+using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Primitive;
namespace Snap.Hutao.ViewModel.AvatarProperty;
@@ -11,7 +12,7 @@ namespace Snap.Hutao.ViewModel.AvatarProperty;
/// 武器
///
[HighQuality]
-internal sealed class WeaponView : Equip, ICalculableSource
+internal sealed class WeaponView : EquipView, ICalculableSource
{
///
/// 副属性
@@ -53,6 +54,8 @@ internal sealed class WeaponView : Equip, ICalculableSource
///
internal uint MaxLevel { get => Model.Metadata.Weapon.Weapon.GetMaxLevelByQuality(Quality); }
+ internal WeaponType WeaponType { get; set; }
+
///
public ICalculableWeapon ToCalculable()
{