// WingsEmu // // Developed by NosWings Team using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using PhoenixLib.Extensions; using PhoenixLib.Logging; using WingsAPI.Communication; using WingsAPI.Communication.DbServer.AccountService; using WingsAPI.Communication.DbServer.CharacterService; using WingsAPI.Communication.Sessions; using WingsAPI.Communication.Sessions.Model; using WingsAPI.Communication.Sessions.Request; using WingsAPI.Communication.Sessions.Response; using WingsAPI.Data.Account; using WingsAPI.Data.Character; using WingsEmu.Core.Extensions; using WingsEmu.DTOs.Account; using WingsEmu.DTOs.Inventory; using WingsEmu.DTOs.Mates; using WingsEmu.Game; using WingsEmu.Game.Items; using WingsEmu.Game.Managers; using WingsEmu.Game.Networking; using WingsEmu.Health; using WingsEmu.Packets.ClientPackets; using WingsEmu.Packets.Enums; namespace WingsEmu.Plugins.PacketHandling.CharScreen; public class EntryPointPacketHandler : GenericCharScreenPacketHandlerBase { private static readonly SemaphoreSlim _semaphoreSlim = new(1, 1); private readonly IAccountService _accountService; private readonly ICharacterService _characterService; private readonly IGameItemInstanceFactory _itemInstanceFactory; private readonly IMaintenanceManager _maintenanceManager; private readonly IServerManager _serverManager; private readonly ISessionManager _sessionManager; private readonly ISessionService _sessionService; public EntryPointPacketHandler(ISessionService sessionService, ICharacterService characterService, IMaintenanceManager maintenanceManager, IAccountService accountService, ISessionManager sessionManager, IServerManager serverManager, IGameItemInstanceFactory itemInstanceFactory) { _sessionService = sessionService; _characterService = characterService; _maintenanceManager = maintenanceManager; _accountService = accountService; _sessionManager = sessionManager; _serverManager = serverManager; _itemInstanceFactory = itemInstanceFactory; } public async Task EntryPointAsync(IClientSession session, EntryPointPacket packet) { await _semaphoreSlim.WaitAsync(); try { if (session.Account == null) { SessionResponse sessionResponse = await _sessionService.GetSessionByAccountName(new GetSessionByAccountNameRequest() { AccountName = packet.SessionName }); if (sessionResponse.ResponseType != RpcResponseType.SUCCESS) { Log.Debug($"Can't get session with ID {packet.SessionName}"); session.ForceDisconnect(); return; } Session sharedSession = sessionResponse.Session; if (sharedSession.State != SessionState.ServerSelection) { Log.Debug($"Incorrect state for {sharedSession.Id}"); session.ForceDisconnect(); return; } if (session.SessionId != sessionResponse.Session.EncryptionKey) { session.ForceDisconnect(); return; } SessionResponse sessionResponseAccount = await _sessionService.GetSessionByAccountId(new GetSessionByAccountIdRequest { AccountId = sharedSession.AccountId }); if (sessionResponseAccount is null) { session.ForceDisconnect(); return; } if (sessionResponseAccount.Session.EncryptionKey != session.SessionId) { session.ForceDisconnect(); return; } if (sessionResponseAccount.Session.State != SessionState.ServerSelection) { session.ForceDisconnect(); return; } if (_maintenanceManager.IsMaintenanceActive && sharedSession.Authority < AuthorityType.GameMaster) { Log.Debug("[ENTRY_POINT] Maintenance is active"); session.ForceDisconnect(); return; } if (_sessionManager.SessionsCount >= _serverManager.AccountLimit && sharedSession.Authority < AuthorityType.Moderator) { Log.Debug("[ENTRY_POINT] Account limit reached"); session.ForceDisconnect(); return; } AccountBanGetResponse banResponse = null; try { banResponse = await _accountService.GetAccountBan(new AccountBanGetRequest { AccountId = sharedSession.AccountId }); } catch (Exception e) { Log.Error($"[ENTRY_POINT][SESSION_ID: '{session.SessionId.ToString()}'] Unexpected error: ", e); } if (banResponse?.ResponseType != RpcResponseType.SUCCESS) { Log.Warn($"[ENTRY_POINT][SESSION_ID: '{session.SessionId.ToString()}'] Failed to get account ban for accountId: '{sharedSession.AccountId}'"); session.ForceDisconnect(); return; } AccountBanDto characterPenalty = banResponse.AccountBanDto; if (characterPenalty != null) { Log.Info($"[ENTRY_POINT][SESSION_ID: '{session.SessionId.ToString()}'] connected from {session.IpAddress} while being banned"); session.ForceDisconnect(); return; } AccountLoadResponse accountResponse = await _accountService.LoadAccountById(new AccountLoadByIdRequest { AccountId = sharedSession.AccountId }); if (accountResponse.ResponseType != RpcResponseType.SUCCESS) { Log.Debug("[ENTRY_POINT] Failed to load account"); session.ForceDisconnect(); return; } AccountDTO account = accountResponse.AccountDto; if (!string.Equals(account.Password, packet.Password.ToSha512(), StringComparison.Ordinal)) { Log.Debug("[ENTRY_POINT] Incorrect password"); session.ForceDisconnect(); return; } var accountObject = new Account { Id = account.Id, Name = account.Name, MasterAccountId = account.MasterAccountId, Authority = account.Authority, BankMoney = account.BankMoney, Password = account.Password, Language = account.Language }; Log.Warn($"INITIALIZE_ACCOUNT : {account.Name}"); SessionResponse response = await _sessionService.ConnectToWorldServer(new ConnectToWorldServerRequest { AccountId = account.Id, ChannelId = _serverManager.ChannelId, ServerGroup = _serverManager.ServerGroup }); if (response is not { ResponseType: RpcResponseType.SUCCESS }) { session.ForceDisconnect(); return; } if (response.Session == null) { session.ForceDisconnect(); return; } session.InitializeAccount(accountObject, response.Session); } DbServerGetCharactersResponse response2 = await _characterService.GetCharacters(new DbServerGetCharactersRequest { AccountId = session.Account.Id }); Log.Info($"[ACCOUNT_ARRIVED] {session.Account.Name}"); // load characterlist packet for each character in CharacterDTO session.SendPacket("clist_start 0"); if (response2.Characters == null) { session.SendPacket("clist_end"); return; } foreach (CharacterDTO character in response2.Characters) { List inventory = character.EquippedStuffs; var equipment = new Dictionary(); foreach (CharacterInventoryItemDto equipmentEntry in inventory) { if (equipmentEntry.InventoryType != InventoryType.EquippedItems) { continue; } GameItemInstance instance = _itemInstanceFactory.CreateItem(equipmentEntry.ItemInstance); if (instance == null) { continue; } equipment[instance.GameItem.EquipmentSlot] = instance; } string petlist = string.Empty; List mates = character.NosMates; for (int i = 0; i < 26; i++) { //0.2105.1102.319.0.632.0.333.0.318.0.317.0.9.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1 petlist += $"{(i != 0 ? "." : "")}{(mates.Count > i ? $"{mates[i].Skin}.{mates[i].NpcMonsterVNum}" : "-1")}"; } // 1 1 before long string of -1.-1 = act completion session.SendPacket( $"clist {character.Slot} {character.Name} 0 {(byte)character.Gender} {(byte)character.HairStyle} {(byte)character.HairColor} 0 {(byte)character.Class} {character.Level} {character.HeroLevel} {(character.HideHat ? 0 : equipment.GetOrDefault(EquipmentType.Hat)?.ItemVNum ?? -1)}.{equipment.GetOrDefault(EquipmentType.Armor)?.ItemVNum ?? -1}.{equipment.GetOrDefault(EquipmentType.WeaponSkin)?.ItemVNum ?? equipment.GetOrDefault(EquipmentType.MainWeapon)?.ItemVNum ?? -1}.{equipment.GetOrDefault(EquipmentType.SecondaryWeapon)?.ItemVNum ?? -1}.{equipment.GetOrDefault(EquipmentType.Mask)?.ItemVNum ?? -1}.{equipment.GetOrDefault(EquipmentType.Fairy)?.ItemVNum ?? -1}.{equipment.GetOrDefault(EquipmentType.CostumeSuit)?.ItemVNum ?? -1}.{(character.HideHat ? 0 : equipment.GetOrDefault(EquipmentType.CostumeHat)?.ItemVNum ?? -1)} {character.JobLevel} 1 1 {petlist} {(equipment.GetOrDefault(EquipmentType.Hat)?.GameItem.IsColorable == true ? equipment.GetOrDefault(EquipmentType.Hat).Design : 0)} 0"); } session.SendPacket("clist_end"); } finally { _semaphoreSlim.Release(); } } protected override async Task HandlePacketAsync(IClientSession session, EntryPointPacket packet) { await EntryPointAsync(session, packet); } }