add cache to minimal deltas

This commit is contained in:
qhy040404
2024-06-11 12:55:54 +08:00
parent 9ef0d8c57d
commit 5f4b68d538
2 changed files with 84 additions and 111 deletions

View File

@@ -1,13 +1,9 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.Metadata.Abstraction;
using Snap.Hutao.Model.Metadata.Item;
using Snap.Hutao.Model.Primitive;
using Snap.Hutao.Service.Cultivation;
using Snap.Hutao.Service.Metadata;
using Snap.Hutao.Service.Metadata.ContextAbstraction;
using Snap.Hutao.Service.Notification;
using Snap.Hutao.Service.User;
@@ -15,9 +11,6 @@ using Snap.Hutao.ViewModel.Cultivation;
using Snap.Hutao.ViewModel.User;
using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
using Snap.Hutao.Web.Response;
using System.Runtime.InteropServices;
using MetadataAvatar = Snap.Hutao.Model.Metadata.Avatar.Avatar;
using MetadataWeapon = Snap.Hutao.Model.Metadata.Weapon.Weapon;
namespace Snap.Hutao.Service.Inventory;
@@ -25,9 +18,9 @@ namespace Snap.Hutao.Service.Inventory;
[Injection(InjectAs.Singleton, typeof(IInventoryService))]
internal sealed partial class InventoryService : IInventoryService
{
private readonly MinimalPromotionDelta minimalPromotionDelta;
private readonly IServiceScopeFactory serviceScopeFactory;
private readonly IInventoryDbService inventoryDbService;
private readonly IMetadataService metadataService;
private readonly IInfoBarService infoBarService;
private readonly IUserService userService;
@@ -56,13 +49,7 @@ internal sealed partial class InventoryService : IInventoryService
/// <inheritdoc/>
public async ValueTask RefreshInventoryAsync(CultivateProject project)
{
List<ICultivationItemsAccess> cultivationItemsEntryList =
[
.. await metadataService.GetAvatarListAsync().ConfigureAwait(false),
.. await metadataService.GetWeaponListAsync().ConfigureAwait(false),
];
cultivationItemsEntryList = MinimalPromotionDelta.Find(cultivationItemsEntryList);
List<AvatarPromotionDelta> deltas = await minimalPromotionDelta.GetAsync().ConfigureAwait(false);
BatchConsumption? batchConsumption = default;
using (IServiceScope scope = serviceScopeFactory.CreateScope())
@@ -76,7 +63,7 @@ internal sealed partial class InventoryService : IInventoryService
CalculateClient calculateClient = scope.ServiceProvider.GetRequiredService<CalculateClient>();
Response<BatchConsumption>? resp = await calculateClient
.BatchComputeAsync(userAndUid, GeneratePromotionDeltas(cultivationItemsEntryList), true)
.BatchComputeAsync(userAndUid, deltas, true)
.ConfigureAwait(false);
if (!resp.IsOk())
@@ -93,97 +80,4 @@ internal sealed partial class InventoryService : IInventoryService
await inventoryDbService.AddInventoryItemRangeByProjectIdAsync(items.SelectList(item => InventoryItem.From(project.InnerId, item.Id, (uint)((int)item.Num - item.LackNum)))).ConfigureAwait(false);
}
}
private static List<AvatarPromotionDelta> GeneratePromotionDeltas(List<ICultivationItemsAccess> cultivatables)
{
List<MetadataAvatar> avatars = [];
List<MetadataWeapon> weapons = [];
HashSet<MaterialId> materialIds = [];
while (cultivatables.Count > 0)
{
ICultivationItemsAccess bestItem = cultivatables.OrderByDescending(item => item.CultivationItems.Count(material => !materialIds.Contains(material))).First();
if (bestItem.CultivationItems.All(materialIds.Contains))
{
break;
}
switch (bestItem)
{
case MetadataAvatar avatar:
avatars.Add(avatar);
break;
case MetadataWeapon weapon:
weapons.Add(weapon);
break;
default:
throw HutaoException.NotSupported();
}
foreach (ref readonly MaterialId materialId in CollectionsMarshal.AsSpan(bestItem.CultivationItems))
{
materialIds.Add(materialId);
}
cultivatables.Remove(bestItem);
}
List<AvatarPromotionDelta> deltas = [];
for (int i = 0; i < Math.Max(avatars.Count, weapons.Count); i++)
{
MetadataAvatar? avatar = avatars.ElementAtOrDefault(i);
MetadataWeapon? weapon = weapons.ElementAtOrDefault(i);
if (avatar is not null)
{
AvatarPromotionDelta delta = new()
{
AvatarId = avatar.Id,
AvatarLevelCurrent = 1,
AvatarLevelTarget = 90,
SkillList = avatar.SkillDepot.CompositeSkillsNoInherents().SelectList(skill => new PromotionDelta()
{
Id = skill.GroupId,
LevelCurrent = 1,
LevelTarget = 10,
}),
};
if (weapon is not null)
{
delta.Weapon = new()
{
Id = weapon.Id,
LevelCurrent = 1,
LevelTarget = 90,
};
}
deltas.Add(delta);
continue;
}
if (weapon is not null)
{
AvatarPromotionDelta delta = new()
{
Weapon = new()
{
Id = weapon.Id,
LevelCurrent = 1,
LevelTarget = 90,
},
};
deltas.Add(delta);
continue;
}
}
return deltas;
}
}

View File

@@ -2,16 +2,43 @@
// Licensed under the MIT license.
using Google.OrTools.LinearSolver;
using Microsoft.Extensions.Caching.Memory;
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Model.Metadata.Abstraction;
using Snap.Hutao.Model.Primitive;
using Snap.Hutao.Service.Metadata;
using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
using System.Runtime.InteropServices;
using MetadataAvatar = Snap.Hutao.Model.Metadata.Avatar.Avatar;
using MetadataWeapon = Snap.Hutao.Model.Metadata.Weapon.Weapon;
namespace Snap.Hutao.Service.Inventory;
internal static class MinimalPromotionDelta
[ConstructorGenerated]
[Injection(InjectAs.Singleton)]
internal sealed partial class MinimalPromotionDelta
{
public static List<ICultivationItemsAccess> Find(List<ICultivationItemsAccess> cultivationItems)
private const string CacheKey = $"{nameof(MinimalPromotionDelta)}.Cache";
private readonly IMetadataService metadataService;
private readonly IMemoryCache memoryCache;
public async ValueTask<List<AvatarPromotionDelta>> GetAsync()
{
if (memoryCache.TryGetRequiredValue(CacheKey, out List<AvatarPromotionDelta>? cache))
{
return cache;
}
List<ICultivationItemsAccess> cultivationItemsEntryList =
[
.. await metadataService.GetAvatarListAsync().ConfigureAwait(false),
.. (await metadataService.GetWeaponListAsync().ConfigureAwait(false)).Where(w => w.Quality >= Model.Intrinsic.QualityType.QUALITY_BLUE),
];
return memoryCache.Set(CacheKey, GeneratePromotionDeltas(Minimize(cultivationItemsEntryList)));
}
private static List<ICultivationItemsAccess> Minimize(List<ICultivationItemsAccess> cultivationItems)
{
using (Solver? solver = Solver.CreateSolver("SCIP"))
{
@@ -54,4 +81,56 @@ internal static class MinimalPromotionDelta
return results;
}
}
private static List<AvatarPromotionDelta> GeneratePromotionDeltas(List<ICultivationItemsAccess> cultivationItems)
{
List<AvatarPromotionDelta> deltas = [];
foreach (ref readonly ICultivationItemsAccess item in CollectionsMarshal.AsSpan(cultivationItems))
{
switch (item)
{
case MetadataAvatar avatar:
deltas.Add(new()
{
AvatarId = avatar.Id,
AvatarLevelCurrent = 1,
AvatarLevelTarget = 90,
SkillList = avatar.SkillDepot.CompositeSkillsNoInherents().SelectList(skill => new PromotionDelta()
{
Id = skill.GroupId,
LevelCurrent = 1,
LevelTarget = 10,
}),
});
break;
case MetadataWeapon weapon:
if (deltas.FirstOrDefault(d => d.Weapon is null) is { } delta)
{
delta.Weapon = new()
{
Id = weapon.Id,
LevelCurrent = 1,
LevelTarget = 90,
};
break;
}
deltas.Add(new()
{
Weapon = new()
{
Id = weapon.Id,
LevelCurrent = 1,
LevelTarget = 90,
},
});
break;
}
}
return deltas;
}
}