diff --git a/Server/PacketHandlers/ChannelSelectionHandler.cs b/Server/PacketHandlers/ChannelSelectionHandler.cs index fce3549..6ad66cb 100644 --- a/Server/PacketHandlers/ChannelSelectionHandler.cs +++ b/Server/PacketHandlers/ChannelSelectionHandler.cs @@ -1,9 +1,11 @@ // Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. +using DotNext.Collections.Generic; using Microsoft.EntityFrameworkCore; using NetCoreServer; using Server.DB; using Server.DB.Documents; +using Wonderking.Game.Data.Character; using Wonderking.Packets.Incoming; using Wonderking.Packets.Outgoing; using Wonderking.Packets.Outgoing.Data; @@ -27,35 +29,19 @@ public class ChannelSelectionHandler : IPacketHandler var account = await _wonderkingContext.Accounts .FirstOrDefaultAsync(a => a.Id == authSession.AccountId); - if (account != null && account.Characters.Count > 0) + + var amountOfCharacter = await _wonderkingContext.Characters + .Where(c => c.Account.Id == authSession.AccountId) + .CountAsync(); + + if (account != null && amountOfCharacter > 0) { responsePacket = new ChannelSelectionResponsePacket { ChannelIsFullFlag = 0, Endpoint = "127.0.0.1", Port = 2000, - Characters = await _wonderkingContext.Characters.AsNoTracking() - .Where(c => c.Account.Id == authSession.AccountId) - .Select(c => - new CharacterData - { - Name = c.Name, - Job = c.JobData, - Gender = c.Gender, - Level = c.Level, - // TODO: Calculate instead of clamping based on max experience for level - Experience = Math.Clamp(c.Experience, 0, 100), - Stats = c.BaseStats, - Health = c.Health, - Mana = c.Mana, - EquippedItems = GetItemIDsByInventoryTab(c.InventoryItems - .Where(item => item.InventoryTab == InventoryTab.WornEquipment) - .Select(item => new Tuple(item.ItemId, item.Slot)).AsEnumerable()), - EquippedCashItems = GetItemIDsByInventoryTab(c.InventoryItems - .Where(item => item.InventoryTab == InventoryTab.WornCashEquipment) - .Select(item => new Tuple(item.ItemId, item.Slot)).AsEnumerable()) - }) - .ToArrayAsync() + Characters = await GetCharacterDataAsync(account.Id).ToArrayAsync() }; guildNameResponsePacket.GuildNames = await _wonderkingContext.Characters @@ -82,21 +68,74 @@ public class ChannelSelectionHandler : IPacketHandler } } - private static ushort[] GetItemIDsByInventoryTab(IEnumerable> items) + private static ushort[] GetItemIDsByInventoryTab(IEnumerable items) { var ids = new ushort[20]; ids.AsSpan().Clear(); foreach (var item in items) { - if (item.Item2 > 20) + if (item.Slot > 20) { continue; } - ids[item.Item2] = item.Item1; + ids[item.Slot] = item.ItemId; } return ids; } + + private async IAsyncEnumerable GetCharacterDataAsync(Guid accountId) + { + await foreach (var c in _getCharacters(_wonderkingContext, accountId)) + { + yield return new CharacterData + { + Name = c.Name, + Job = c.JobData, + Gender = c.Gender, + Level = c.Level, + // TODO: Calculate instead of clamping based on max experience for level + Experience = Math.Clamp(c.Experience, 0, 100), + Stats = c.BaseStats, + Health = c.Health, + Mana = c.Mana, + EquippedItems = + GetItemIDsByInventoryTab(c.InventoryItems.Where(i => + i.InventoryTab == InventoryTab.WornEquipment)), + EquippedCashItems = GetItemIDsByInventoryTab(c.InventoryItems.Where(i => + i.InventoryTab == InventoryTab.WornCashEquipment)) + }; + } + } + + private static readonly Func> _getCharacters = + EF.CompileAsyncQuery((WonderkingContext context, Guid accountId) => + context.Characters.AsNoTracking().AsSplitQuery() + .Where(c => c.Account.Id == accountId) + .Select(c => new CharacterDataProjection( + c.Name, + c.JobData, + c.Gender, + c.Level, + c.Experience, + c.BaseStats, + c.Health, + c.Mana, + c.InventoryItems.Select(i => new InventoryItemProjection(i.ItemId, i.Slot, i.InventoryTab)) + )).Take(3)); + + private sealed record InventoryItemProjection(ushort ItemId, byte Slot, InventoryTab InventoryTab); + + private sealed record CharacterDataProjection( + string Name, + JobData JobData, + Gender Gender, + ushort Level, + long Experience, + BaseStats BaseStats, + int Health, + int Mana, + IEnumerable InventoryItems); }