diff --git a/NineChronicles.DataProvider.Executable/Commands/MySqlMigration.cs b/NineChronicles.DataProvider.Executable/Commands/MySqlMigration.cs index 505fc03a..dd9b660f 100644 --- a/NineChronicles.DataProvider.Executable/Commands/MySqlMigration.cs +++ b/NineChronicles.DataProvider.Executable/Commands/MySqlMigration.cs @@ -1,3 +1,5 @@ +using Nekoyume.Model.EnumType; + namespace NineChronicles.DataProvider.Executable.Commands { using System; @@ -393,7 +395,7 @@ private void ProcessTasks(Task>[] taskArray, IB // check if address is already in _avatarCheck if (!_avatarCheck.Contains(avatarAddress.ToString())) { - _avatarList.Add(AvatarData.GetAvatarInfo(outputState, ae.InputContext.Signer, hasAction.AvatarAddress, hasAction.RuneInfos, _blockTimeOffset)); + _avatarList.Add(AvatarData.GetAvatarInfo(outputState, ae.InputContext.Signer, hasAction.AvatarAddress, hasAction.RuneInfos, _blockTimeOffset, BattleType.Adventure)); _avatarCheck.Add(avatarAddress.ToString()); } @@ -471,7 +473,7 @@ private void ProcessTasks(Task>[] taskArray, IB if (action is HackAndSlashSweep hasSweep) { var start = DateTimeOffset.UtcNow; - _avatarList.Add(AvatarData.GetAvatarInfo(outputState, ae.InputContext.Signer, hasSweep.avatarAddress, hasSweep.runeInfos, _blockTimeOffset)); + _avatarList.Add(AvatarData.GetAvatarInfo(outputState, ae.InputContext.Signer, hasSweep.avatarAddress, hasSweep.runeInfos, _blockTimeOffset, BattleType.Adventure)); _hackAndSlashSweepList.Add(HackAndSlashSweepData.GetHackAndSlashSweepInfo( inputState, outputState, @@ -819,7 +821,7 @@ private void ProcessTasks(Task>[] taskArray, IB if (action is BattleArena battleArena) { var start = DateTimeOffset.UtcNow; - _avatarList.Add(AvatarData.GetAvatarInfo(outputState, ae.InputContext.Signer, battleArena.myAvatarAddress, battleArena.runeInfos, _blockTimeOffset)); + _avatarList.Add(AvatarData.GetAvatarInfo(outputState, ae.InputContext.Signer, battleArena.myAvatarAddress, battleArena.runeInfos, _blockTimeOffset, BattleType.Adventure)); _battleArenaList.Add(BattleArenaData.GetBattleArenaInfo( inputState, outputState, @@ -1052,7 +1054,7 @@ private void ProcessTasks(Task>[] taskArray, IB } } - _avatarList.Add(AvatarData.GetAvatarInfo(outputState, ae.InputContext.Signer, raid.AvatarAddress, raid.RuneInfos, _blockTimeOffset)); + _avatarList.Add(AvatarData.GetAvatarInfo(outputState, ae.InputContext.Signer, raid.AvatarAddress, raid.RuneInfos, _blockTimeOffset, BattleType.Adventure)); var worldBossListSheet = sheets.GetSheet(); int raidId = worldBossListSheet.FindRaidIdByBlockIndex(ae.InputContext.BlockIndex); diff --git a/NineChronicles.DataProvider.Tests/WorldBossRankingQueryTest.cs b/NineChronicles.DataProvider.Tests/WorldBossRankingQueryTest.cs index 39f7c263..fb91eabf 100644 --- a/NineChronicles.DataProvider.Tests/WorldBossRankingQueryTest.cs +++ b/NineChronicles.DataProvider.Tests/WorldBossRankingQueryTest.cs @@ -107,7 +107,80 @@ public async Task WorldBossTotalUsers() var result = await ExecuteAsync(query); var count = (int)((Dictionary) ((ExecutionNode) result.Data).ToValue())["worldBossTotalUsers"]; Assert.Equal(200, count); + } + + [Fact] + public async Task WorldBossRanking_Sort() + { + var avatarAddresses = new List
+ { + new PrivateKey().Address, + new PrivateKey().Address, + new PrivateKey().Address, + }; + var targetAvatarAddress = avatarAddresses[0]; + var queryAddress = targetAvatarAddress.ToString(); + var query = $@"query {{ + worldBossRanking(raidId: 1, avatarAddress: ""{queryAddress}"") {{ + blockIndex + rankingInfo {{ + ranking + avatarName + address + }} + }} + }}"; + for (int idx = 0; idx < 2; idx++) + { + for (int i = 0; i < 3; i++) + { + var avatarAddress = avatarAddresses[i]; + var model = new RaiderModel( + idx + 1, + i.ToString(), + idx, + idx + 1, + i + 2, + GameConfig.DefaultAvatarArmorId, + i, + avatarAddress.ToHex(), + 0 + ); + Context.Raiders.Add(model); + } + } + var block = new BlockModel + { + Index = 1L, + Hash = "4582250d0da33b06779a8475d283d5dd210c683b9b999d74d03fac4f58fa6bce", + Miner = "47d082a115c63e7b58b1532d20e631538eafadde", + Difficulty = 0L, + Nonce = "dff109a0abf1762673ed", + PreviousHash = "asd", + ProtocolVersion = 1, + PublicKey = ByteUtil.Hex(new PrivateKey().PublicKey.ToImmutableArray(false)), + StateRootHash = "ce667fcd0b69076d9ff7e7755daa2f35cb0488e4c47978468dfbd6b88fca8a90", + TotalDifficulty = 0L, + TxCount = 1, + TxHash = "fd47c10ffbee8ff2da8fa08cec3072de06a72f73693f5d3399b093b0877fa954", + TimeStamp = DateTimeOffset.UtcNow + }; + Context.Blocks.Add(block); + + await Context.SaveChangesAsync(); + var result = await ExecuteAsync(query); + var data = (Dictionary)((Dictionary)((ExecutionNode)result.Data).ToValue())["worldBossRanking"]; + Assert.Equal(1L, data["blockIndex"]); + var models = (object[]) data["rankingInfo"]; + Assert.Equal(3, models.Length); + for (int j = 0; j < 3; j++) + { + var model = (Dictionary)models[j]; + Assert.Equal(j + 1, model["ranking"]); + Assert.Equal(avatarAddresses[j].ToHex(), model["address"]); + + } } public void Dispose() diff --git a/NineChronicles.DataProvider/DataRendering/AvatarData.cs b/NineChronicles.DataProvider/DataRendering/AvatarData.cs index c1ecaad3..ed3fbf34 100644 --- a/NineChronicles.DataProvider/DataRendering/AvatarData.cs +++ b/NineChronicles.DataProvider/DataRendering/AvatarData.cs @@ -27,7 +27,8 @@ public static AvatarModel GetAvatarInfo( Address signer, Address avatarAddress, List runeInfos, - DateTimeOffset blockTime) + DateTimeOffset blockTime, + BattleType battleType) { AvatarState avatarState = outputStates.GetAvatarState(avatarAddress); var collectionExist = outputStates.TryGetCollectionState(avatarAddress, out var collectionState); @@ -52,29 +53,43 @@ public static AvatarModel GetAvatarInfo( var itemSlotState = outputStates.TryGetLegacyState(itemSlotStateAddress, out List rawItemSlotState) ? new ItemSlotState(rawItemSlotState) : new ItemSlotState(BattleType.Adventure); - var equipmentInventory = avatarState.inventory.Equipments; - var equipmentList = itemSlotState.Equipments - .Select(guid => equipmentInventory.FirstOrDefault(x => x.ItemId == guid)) - .Where(item => item != null).ToList(); - - var costumeInventory = avatarState.inventory.Costumes; - var costumeList = itemSlotState.Costumes - .Select(guid => costumeInventory.FirstOrDefault(x => x.ItemId == guid)) - .Where(item => item != null).ToList(); - var runeOptionSheet = sheets.GetSheet(); - var runeOptions = new List(); + var equipmentList = SetEquipments(avatarState, itemSlotState, battleType); + var costumeList = SetCostumes(avatarState, itemSlotState, battleType); var runeStates = outputStates.GetRuneState(avatarAddress, out _); + var runeAddresses = RuneSlotState.DeriveAddress(avatarAddress, battleType); + var runeSlotState = outputStates.TryGetLegacyState(runeAddresses, out List rawRuneSlotState) + ? new RuneSlotState(rawRuneSlotState) + : new RuneSlotState(BattleType.Adventure); + var runeSlotStates = new List(); + runeSlotStates.Add(runeSlotState); + var runes = SetRunes(runeSlotStates, battleType); + + var equippedRuneStates = new List(); + var runeIds = runes[battleType].GetRuneSlot() + .Where(slot => slot.RuneId.HasValue) + .Select(slot => slot.RuneId!.Value); + + foreach (var runeId in runeIds) + { + var runeState = runeStates.Runes!.FirstOrDefault(x => x.Value.RuneId == runeId); + if (runeStates.Runes != null) + { + equippedRuneStates.Add(runeState.Value); + } + } - foreach (var runeState in runeStates.Runes.Values) + var runeOptions = new List(); + var runeOptionSheet = sheets.GetSheet(); + foreach (var runeState in equippedRuneStates) { if (!runeOptionSheet.TryGetValue(runeState.RuneId, out var optionRow)) { - throw new SheetRowNotFoundException("RuneOptionSheet", runeState.RuneId); + continue; } if (!optionRow.LevelOptionMap.TryGetValue(runeState.Level, out var option)) { - throw new SheetRowNotFoundException("RuneOptionSheet", runeState.Level); + continue; } runeOptions.Add(option); @@ -125,8 +140,8 @@ public static AvatarModel GetAvatarInfo( sheets.GetSheet()); var avatarCp = CPHelper.TotalCP( - equipmentList, - costumeList, + equipmentList[battleType], + costumeList[battleType], runeOptions, avatarState.level, characterRow, @@ -158,5 +173,57 @@ public static AvatarModel GetAvatarInfo( return avatarModel; } + + private static Dictionary> SetEquipments( + AvatarState avatarState, + ItemSlotState itemSlotStates, + BattleType battleType) + { + Dictionary> equipments = new (); + equipments.Add(BattleType.Adventure, new List()); + equipments.Add(BattleType.Arena, new List()); + equipments.Add(BattleType.Raid, new List()); + var equipmentList = itemSlotStates.Equipments + .Select(guid => + avatarState.inventory.Equipments.FirstOrDefault(x => x.ItemId == guid)) + .Where(item => item != null).ToList(); + equipments[battleType] = equipmentList!; + + return equipments; + } + + private static Dictionary> SetCostumes( + AvatarState avatarState, + ItemSlotState itemSlotStates, + BattleType battleType) + { + Dictionary> costumes = new (); + costumes.Add(BattleType.Adventure, new List()); + costumes.Add(BattleType.Arena, new List()); + costumes.Add(BattleType.Raid, new List()); + var costumeList = itemSlotStates.Costumes + .Select(guid => + avatarState.inventory.Costumes.FirstOrDefault(x => x.ItemId == guid)) + .Where(item => item != null).ToList(); + costumes[battleType] = costumeList!; + + return costumes; + } + + private static Dictionary SetRunes( + List runeSlotStates, + BattleType battleType) + { + Dictionary runes = new (); + runes.Add(BattleType.Adventure, new RuneSlotState(BattleType.Adventure)); + runes.Add(BattleType.Arena, new RuneSlotState(BattleType.Arena)); + runes.Add(BattleType.Raid, new RuneSlotState(BattleType.Raid)); + foreach (var state in runeSlotStates) + { + runes[battleType] = state; + } + + return runes; + } } } diff --git a/NineChronicles.DataProvider/NineChronicles.DataProvider.csproj b/NineChronicles.DataProvider/NineChronicles.DataProvider.csproj index feb1b51f..e2a5d835 100644 --- a/NineChronicles.DataProvider/NineChronicles.DataProvider.csproj +++ b/NineChronicles.DataProvider/NineChronicles.DataProvider.csproj @@ -2,7 +2,6 @@ net6 - 8.0 true enable true diff --git a/NineChronicles.DataProvider/RenderSubscriber.cs b/NineChronicles.DataProvider/RenderSubscriber.cs index c4f886c1..d2ef6ad6 100644 --- a/NineChronicles.DataProvider/RenderSubscriber.cs +++ b/NineChronicles.DataProvider/RenderSubscriber.cs @@ -408,7 +408,7 @@ protected override Task ExecuteAsync(CancellationToken stoppingToken) var start = DateTimeOffset.UtcNow; var inputState = new World(_blockChainStates.GetWorldState(ev.PreviousState)); var outputState = new World(_blockChainStates.GetWorldState(ev.OutputState)); - _avatarList.Add(AvatarData.GetAvatarInfo(outputState, ev.Signer, has.AvatarAddress, has.RuneInfos, _blockTimeOffset)); + _avatarList.Add(AvatarData.GetAvatarInfo(outputState, ev.Signer, has.AvatarAddress, has.RuneInfos, _blockTimeOffset, BattleType.Adventure)); _hasList.Add(HackAndSlashData.GetHackAndSlashInfo(inputState, outputState, ev.Signer, has.AvatarAddress, has.StageId, has.Id, ev.BlockIndex, _blockTimeOffset)); if (has.StageBuffId.HasValue) { @@ -435,7 +435,7 @@ protected override Task ExecuteAsync(CancellationToken stoppingToken) var start = DateTimeOffset.UtcNow; var inputState = new World(_blockChainStates.GetWorldState(ev.PreviousState)); var outputState = new World(_blockChainStates.GetWorldState(ev.OutputState)); - _avatarList.Add(AvatarData.GetAvatarInfo(outputState, ev.Signer, hasSweep.avatarAddress, hasSweep.runeInfos, _blockTimeOffset)); + _avatarList.Add(AvatarData.GetAvatarInfo(outputState, ev.Signer, hasSweep.avatarAddress, hasSweep.runeInfos, _blockTimeOffset, BattleType.Adventure)); _hasSweepList.Add(HackAndSlashSweepData.GetHackAndSlashSweepInfo( inputState, outputState, @@ -992,7 +992,7 @@ protected override Task ExecuteAsync(CancellationToken stoppingToken) var start = DateTimeOffset.UtcNow; var inputState = new World(_blockChainStates.GetWorldState(ev.PreviousState)); var outputState = new World(_blockChainStates.GetWorldState(ev.OutputState)); - _avatarList.Add(AvatarData.GetAvatarInfo(outputState, ev.Signer, battleArena.myAvatarAddress, battleArena.runeInfos, _blockTimeOffset)); + _avatarList.Add(AvatarData.GetAvatarInfo(outputState, ev.Signer, battleArena.myAvatarAddress, battleArena.runeInfos, _blockTimeOffset, BattleType.Adventure)); _battleArenaList.Add(BattleArenaData.GetBattleArenaInfo( inputState, outputState, @@ -1327,7 +1327,7 @@ protected override Task ExecuteAsync(CancellationToken stoppingToken) } } - _avatarList.Add(AvatarData.GetAvatarInfo(outputState, ev.Signer, ev.Action.AvatarAddress, ev.Action.RuneInfos, _blockTimeOffset)); + _avatarList.Add(AvatarData.GetAvatarInfo(outputState, ev.Signer, ev.Action.AvatarAddress, ev.Action.RuneInfos, _blockTimeOffset, BattleType.Adventure)); var worldBossListSheet = sheets.GetSheet(); int raidId = worldBossListSheet.FindRaidIdByBlockIndex(ev.BlockIndex); @@ -1598,7 +1598,7 @@ private void ProcessAgentAvatarData(ActionEvaluation ev) : new RuneSlotState(BattleType.Adventure); var runeSlotInfos = runeSlotState.GetEquippedRuneSlotInfos(); - _avatarList.Add(AvatarData.GetAvatarInfo(outputState, ev.Signer, avatarAddress, runeSlotInfos, _blockTimeOffset)); + _avatarList.Add(AvatarData.GetAvatarInfo(outputState, ev.Signer, avatarAddress, runeSlotInfos, _blockTimeOffset, BattleType.Adventure)); } catch (Exception ex) { diff --git a/NineChronicles.DataProvider/Store/MySqlStore.cs b/NineChronicles.DataProvider/Store/MySqlStore.cs index d6e8b905..70752965 100644 --- a/NineChronicles.DataProvider/Store/MySqlStore.cs +++ b/NineChronicles.DataProvider/Store/MySqlStore.cs @@ -2297,7 +2297,7 @@ public List GetWorldBossRanking(int raidId, int? queryOff { using NineChroniclesContext? ctx = _dbContextFactory.CreateDbContext(); var query = ctx.Set() - .FromSqlRaw(@"SELECT `AvatarName`, `HighScore`, `TotalScore`, `Cp`, `Level`, `Address`, `IconId`, row_number() over(ORDER BY `TotalScore` DESC) as `Ranking` FROM `Raiders` WHERE `RaidId` = {0}", raidId); + .FromSqlRaw(@"SELECT `AvatarName`, `HighScore`, `TotalScore`, `Cp`, `Level`, `Address`, `IconId`, row_number() over(ORDER BY `TotalScore` DESC, `UpdatedAt`) as `Ranking` FROM `Raiders` WHERE `RaidId` = {0}", raidId); if (queryOffset.HasValue) { query = query.Skip(queryOffset.Value);