public static async Task <Player> Instantiate(IRakConnection connection, Zone zone, ObjectId id) { // Set up a cancellation token for the connection disconnecting. // This may happen before the object starts. var cancellationToken = new CancellationTokenSource(); connection.Disconnected += (disconnectionReason) => { cancellationToken.Cancel(); return(Task.CompletedTask); }; // Create base game object var instance = Instantiate <Player>( zone, position: zone.SpawnPosition, rotation: zone.SpawnRotation, scale: 1, objectId: id, lot: 1 ); await instance.LoadAsync(connection, cancellationToken.Token); return(instance); }
public async Task RenameCharacter(CharacterRenameRequest packet, IRakConnection connection) { var session = UchuServer.SessionCache.GetSession(connection.EndPoint); await using var ctx = new UchuContext(); // Check if the name already exists and return proper response if so if (ctx.Characters.Any(c => c.Name == packet.Name || c.CustomName == packet.Name)) { connection.Send(new CharacterRenameResponse { ResponseId = CharacterRenamingResponse.NameAlreadyInUse } ); return; } // If the name is free, update accordingly and notify the client var chr = await ctx.Characters.FindAsync(packet.CharacterId); chr.CustomName = packet.Name; chr.NameRejected = false; chr.LastActivity = DateTimeOffset.Now.ToUnixTimeSeconds(); await ctx.SaveChangesAsync(); connection.Send(new CharacterRenameResponse { ResponseId = CharacterRenamingResponse.Success } ); await SendCharacterList(connection, session.UserId); }
public static void Send(this IRakConnection @this, ISerializable serializable) { if (@this == null) { throw new ArgumentNullException(nameof(@this), ResourceStrings.RakConnectionExtensions_Send_ConnectionNullException); } if (serializable == null) { throw new ArgumentNullException(nameof(serializable), ResourceStrings.RakConnectionExtensions_Send_StreamNullException); } Logger.Debug($"Sending {serializable}"); using var stream = new MemoryStream(); using var writer = new BitWriter(stream); writer.Write(serializable); try { @this.Send(stream.ToArray()); } catch (IOException e) { Logger.Error(e); } }
public async Task RenameCharacter(CharacterRenameRequest packet, IRakConnection connection) { var session = Server.SessionCache.GetSession(connection.EndPoint); await using var ctx = new UchuContext(); if (ctx.Characters.Any(c => c.Name == packet.Name || c.CustomName == packet.Name)) { connection.Send(new CharacterRenameResponse { ResponseId = CharacterRenamingResponse.NameAlreadyInUse } ); return; } var chr = await ctx.Characters.FindAsync(packet.CharacterId); chr.CustomName = packet.Name; chr.NameRejected = false; chr.LastActivity = DateTimeOffset.Now.ToUnixTimeSeconds(); await ctx.SaveChangesAsync(); connection.Send(new CharacterRenameResponse { ResponseId = CharacterRenamingResponse.Success } ); await SendCharacterList(connection, session.UserId); }
public async Task JoinWorld(JoinWorldRequest packet, IRakConnection connection) { UchuServer.SessionCache.SetCharacter(connection.EndPoint, packet.CharacterId); await using var ctx = new UchuContext(); var character = await ctx.Characters.FirstAsync(c => c.Id == packet.CharacterId); character.LastActivity = DateTimeOffset.Now.ToUnixTimeSeconds(); await ctx.SaveChangesAsync(); var zone = (ZoneId)character.LastZone; var requestZone = (ZoneId)(zone == 0 ? 1000 : zone); // We don't want to lock up the server on a world server request, as it may take time. var _ = Task.Run(async() => { // Request world server. var server = await ServerHelper.RequestWorldServerAsync(UchuServer, requestZone); if (server == default) { // If there's no server available, error var session = UchuServer.SessionCache.GetSession(connection.EndPoint); await SendCharacterList(connection, session.UserId); return; } // Send to world server. connection.Send(new ServerRedirectionPacket { Address = UchuServer.Host, Port = (ushort)server.Port }); }); }
public async Task ClientLoadCompleteHandler(ClientLoadCompletePacket packet, IRakConnection connection) { Logger.Information($"{connection.EndPoint}'s client load completed..."); var session = UchuServer.SessionCache.GetSession(connection.EndPoint); await using var ctx = new UchuContext(); var character = await ctx.Characters .Include(c => c.Flags) .Include(c => c.Items) .Include(c => c.User) .Include(c => c.Missions) .ThenInclude(m => m.Tasks) .ThenInclude(m => m.Values) .SingleAsync(c => c.Id == session.CharacterId); var zoneId = (ZoneId)character.LastZone; if (zoneId == 0) { zoneId = 1000; character.LastZone = zoneId; await ctx.SaveChangesAsync(); } Logger.Information("[55%] Setting session zone."); UchuServer.SessionCache.SetZone(connection.EndPoint, zoneId); // Zone should already be initialized at this point. Logger.Information("[55%] Getting zone from worldserver."); var zone = await((WorldUchuServer)UchuServer).GetZoneAsync(zoneId); // Send the character init XML data for this world to the client Logger.Information("[55%] Sending XML client info."); await SendCharacterXmlDataToClient(character, connection, session); Logger.Information("[55%] Constructing player."); var player = await Player.ConstructAsync(character, connection, zone); Logger.Information("[55%] Checking rocket landing conditions."); if (character.LandingByRocket) { Logger.Information("[55%] Player landed by rocket, saving changes."); character.LandingByRocket = false; await ctx.SaveChangesAsync(); } Logger.Information("[55%] Player is ready to join world."); player.Message(new PlayerReadyMessage { Associate = player }); Logger.Information("[55%] Server is done loading object."); player.Message(new DoneLoadingObjectsMessage { Associate = player }); }
public static Player FindPlayer(this UchuServer @this, IRakConnection connection) { if (connection == null) { return(null); } return(((WorldUchuServer)@this).Zones.SelectMany(z => z.Players).FirstOrDefault( player => player.Connection.EndPoint.Equals(connection.EndPoint) )); }
public static void Send(this IRakConnection @this, ISerializable serializable) { Logger.Information($"Sending {serializable}"); using var stream = new MemoryStream(); using var writer = new BitWriter(stream); writer.Write(serializable); @this.Send(stream.ToArray()); }
public async Task ValidateClientHandler(SessionInfoPacket packet, IRakConnection connection) { Logger.Information($"{connection.EndPoint}'s validating client for world!"); if (!Server.SessionCache.IsKey(packet.SessionKey)) { await connection.CloseAsync(); Logger.Warning($"{connection} attempted to connect with an invalid session key"); return; } Server.SessionCache.RegisterKey(connection.EndPoint, packet.SessionKey); var session = Server.SessionCache.GetSession(connection.EndPoint); await using var ctx = new UchuContext(); // Try to find the player, disconnect if the player is invalid var character = await ctx.Characters.FindAsync(session.CharacterId); if (character == null) { Logger.Warning( $"{connection} attempted to connect to world with an invalid character {session.CharacterId}" ); connection.Send(new DisconnectNotifyPacket { DisconnectId = DisconnectId.CharacterNotFound }); return; } // Initialize zone for player var zoneId = (ZoneId)character.LastZone; if (zoneId == ZoneId.VentureExplorerCinematic) { zoneId = ZoneId.VentureExplorer; } var worldServer = (WorldServer)Server; var zone = await worldServer.GetZoneAsync(zoneId); Server.SessionCache.SetZone(connection.EndPoint, zoneId); connection.Send(new WorldInfoPacket { ZoneId = zoneId, Checksum = zone.Checksum, SpawnPosition = zone.ZoneInfo.LuzFile.SpawnPoint }); }
public void ValidateClient(SessionInfoPacket packet, IRakConnection connection) { if (!Server.SessionCache.IsKey(packet.SessionKey)) { Task.Run(connection.CloseAsync); Logger.Warning($"{connection} attempted to connect with an invalid session key"); return; } Server.SessionCache.RegisterKey(connection.EndPoint, packet.SessionKey); }
public async Task ValidateClient(SessionInfoPacket packet, IRakConnection connection) { if (packet == null) { throw new ArgumentNullException(nameof(packet), ResourceStrings.ClientHandler_ValidateClient_PacketNullException); } await UchuServer.ValidateUserAsync(connection, packet.Username, packet.SessionKey) .ConfigureAwait(false); }
private static async Task SendCharacterList(IRakConnection connection, long userId) { await using var ctx = new UchuContext(); var user = await ctx.Users.Include(u => u.Characters).ThenInclude(c => c.Items) .SingleAsync(u => u.UserId == userId); var charCount = user.Characters.Count; var chars = new CharacterListResponse.Character[charCount]; user.Characters.Sort((u2, u1) => DateTimeOffset.Compare(DateTimeOffset.FromUnixTimeSeconds(u1.LastActivity), DateTimeOffset.FromUnixTimeSeconds(u2.LastActivity))); for (var i = 0; i < charCount; i++) { var chr = user.Characters[i]; var items = chr.Items.Where(itm => itm.IsEquipped).Select(itm => (uint)itm.LOT).ToArray(); chars[i] = new CharacterListResponse.Character { CharacterId = chr.CharacterId, Name = chr.Name, UnnaprovedName = chr.CustomName, NameRejected = chr.NameRejected, FreeToPlay = chr.FreeToPlay, ShirtColor = (uint)chr.ShirtColor, ShirtStyle = (uint)chr.ShirtStyle, PantsColor = (uint)chr.PantsColor, HairStyle = (uint)chr.HairStyle, HairColor = (uint)chr.HairColor, Lh = (uint)chr.Lh, Rh = (uint)chr.Rh, EyebrowStyle = (uint)chr.EyebrowStyle, EyeStyle = (uint)chr.EyeStyle, MouthStyle = (uint)chr.MouthStyle, LastZone = (ZoneId)chr.LastZone, LastInstance = (ushort)chr.LastInstance, LastClone = (uint)chr.LastClone, LastActivity = (ulong)chr.LastActivity, ItemCount = (ushort)items.Length, Items = items }; } connection.Send(new CharacterListResponse { CharacterCount = (byte)charCount, CharacterIndex = (byte)user.CharacterIndex, Characters = chars }); Logger.Debug($"Sent character list to {connection}"); }
public async Task ValidateClientHandler(SessionInfoPacket packet, IRakConnection connection) { Logger.Information($"{connection.EndPoint}'s validating client for world!"); var verified = await UchuServer.ValidateUserAsync(connection, packet.Username, packet.SessionKey); if (!verified) { return; } var session = UchuServer.SessionCache.GetSession(connection.EndPoint); await using var ctx = new UchuContext(); // Try to find the player, disconnect if the player is invalid var character = await ctx.Characters.FindAsync(session.CharacterId); if (character == null) { Logger.Warning( $"{connection} attempted to connect to world with an invalid character {session.CharacterId}" ); connection.Send(new DisconnectNotifyPacket { DisconnectId = DisconnectId.CharacterNotFound }); return; } // Initialize zone for player var zoneId = (ZoneId)character.LastZone; if (zoneId == 0) { zoneId = 1000; } var worldServer = (WorldUchuServer)UchuServer; var zone = await worldServer.GetZoneAsync(zoneId); UchuServer.SessionCache.SetZone(connection.EndPoint, zoneId); connection.Send(new WorldInfoPacket { ZoneId = zoneId, Checksum = zone.Checksum, SpawnPosition = zone.SpawnPosition }); }
public async Task JoinWorld(JoinWorldRequest packet, IRakConnection connection) { Server.SessionCache.SetCharacter(connection.EndPoint, packet.CharacterId); await using var ctx = new UchuContext(); var character = await ctx.Characters.FirstAsync(c => c.CharacterId == packet.CharacterId); character.LastActivity = DateTimeOffset.Now.ToUnixTimeSeconds(); await ctx.SaveChangesAsync(); var zone = (ZoneId)character.LastZone; var requestZone = zone == ZoneId.VentureExplorerCinematic ? ZoneId.VentureExplorer : zone; // // We don't want to lock up the server on a world server request, as it may take time. // var _ = Task.Run(async() => { // // Request world server. // var server = await ServerHelper.RequestWorldServerAsync(requestZone); if (server == default) { // // Error // var session = Server.SessionCache.GetSession(connection.EndPoint); await SendCharacterList(connection, session.UserId); return; } // // Send to world server. // connection.Send(new ServerRedirectionPacket { Address = Server.GetHost(), Port = (ushort)server.Port }); }); }
public static void Send(this IRakConnection @this, MemoryStream stream) { if (@this == null) { throw new ArgumentNullException(nameof(@this), ResourceStrings.RakConnectionExtensions_Send_ConnectionNullException); } if (stream == null) { throw new ArgumentNullException(nameof(stream), ResourceStrings.RakConnectionExtensions_Send_StreamNullException); } @this.Send(stream.ToArray()); }
public void Handshake(HandshakePacket packet, IRakConnection connection) { if (packet.GameVersion != 171022) { Logger.Warning($"Handshake attempted with client of Game version: {packet.GameVersion}"); return; } const int port = 21836; connection.Send(new HandshakePacket { ConnectionType = Server.Port == port ? 0x01u : 0x04u, Address = Server.GetHost() }); }
/// <summary> /// Sends the character initialization packet for a character to the current connection /// </summary> /// <param name="character">The character to generate the initialization data for</param> /// <param name="connection">The connection to send the initialization data to</param> /// <param name="session">The session cache for the connection</param> private async Task SendCharacterXmlDataToClient(Character character, IRakConnection connection, Session session) { // Get the XML data for this character for the initial character packet var xmlData = GenerateCharacterXmlData(character); await using var ms = new MemoryStream(); await using var writer = new StreamWriter(ms, Encoding.UTF8); Serializer.Serialize(writer, xmlData); var bytes = ms.ToArray(); var xml = new byte[bytes.Length - 3]; Buffer.BlockCopy(bytes, 3, xml, 0, bytes.Length - 3); var ldf = new LegoDataDictionary {
public async Task ClientMailPacketHandler(ClientMailPacket packet, IRakConnection connection) { var player = UchuServer.FindPlayer(connection); if (player == default) { Logger.Error($"Could not find player for: {connection}"); return; } switch (packet.Id) { case ClientMailPacketId.Send: await SendHandler(packet.MailStruct as MailSend, player); break; case ClientMailPacketId.DataRequest: await DataRequestHandler(player); break; case ClientMailPacketId.AttachmentCollected: await AttachmentCollectHandler(packet.MailStruct as MailAttachmentCollected, player); break; case ClientMailPacketId.Delete: await DeleteHandler(packet.MailStruct as MailDelete, player); break; case ClientMailPacketId.Read: await ReadHandler(packet.MailStruct as MailRead, player); break; case ClientMailPacketId.NotificationRequest: await NotificationRequestHandler(player); break; default: throw new ArgumentOutOfRangeException(); } }
public async Task CharacterList(CharacterListRequest packet, IRakConnection connection) { // Remove the player from the zone. var session = UchuServer.SessionCache.GetSession(connection.EndPoint); if (UchuServer is WorldUchuServer worldUchuServer) { var player = worldUchuServer.FindPlayer(connection); if (player != null) { await player.DestroyAsync(); } } // Send the character list. await SendCharacterList(connection, session.UserId); }
public void FriendsListRequestHandler(GetFriendListPacket packet, IRakConnection connection) { var session = Server.SessionCache.GetSession(connection.EndPoint); var zone = ((WorldServer)Server).Zones.FirstOrDefault(z => (int)z.ZoneId == session.ZoneId); if (zone == default) { Logger.Error($"Invalid ZoneId for {connection}"); return; } using var ctx = new UchuContext(); var character = ctx.Characters.First(c => c.CharacterId == session.CharacterId); var relations = ctx.Friends.Where(f => f.FriendId == character.CharacterId || f.FriendTwoId == character.CharacterId ).ToArray(); var friends = new List <FriendListPacket.Friend>(); foreach (var characterFriend in relations) { if (!characterFriend.IsAccepted) { continue; } var friendId = characterFriend.FriendTwoId == character.CharacterId ? characterFriend.FriendId : characterFriend.FriendTwoId; var friend = ctx.Characters.First(c => c.CharacterId == friendId); var player = zone.Players.FirstOrDefault(p => p.ObjectId == friend.CharacterId); friends.Add(new FriendListPacket.Friend { IsBestFriend = characterFriend.IsBestFriend, IsFreeToPlay = friend.FreeToPlay, IsOnline = player != default, PlayerId = player?.ObjectId ?? -1, PlayerName = friend.Name, ZoneId = (ZoneId)friend.LastZone, WorldClone = (uint)friend.LastClone, WorldInstance = (ushort)friend.LastInstance });
/// <summary> /// Sends a struct (packet) to a given connection. /// </summary> /// <param name="this">RakNet connection to send over.</param> /// <param name="packet">Packet to send.</param> /// <typeparam name="T">Type of the packet.</typeparam> /// <exception cref="ArgumentNullException">Connection is null.</exception> public static void Send <T>(this IRakConnection @this, T packet) where T : struct { if (@this == null) { throw new ArgumentNullException(nameof(@this), ResourceStrings.RakConnectionExtensions_Send_ConnectionNullException); } Logger.Debug($"Sending {packet}"); try { @this.Send(StructPacketParser.WritePacket(packet).ToArray()); } catch (IOException e) { Logger.Error(e); } }
public async Task PositionHandler(PositionUpdatePacket packet, IRakConnection connection) { var player = Server.FindPlayer(connection); if (player?.Transform == default) { Logger.Warning($"{connection} is not logged in but sent a Position Update packet."); return; } // // The server is a slave to the position update packets it gets from the client right now. // player.Transform.Position = packet.Position; player.Transform.Rotation = packet.Rotation; var physics = player.GetComponent <ControllablePhysicsComponent>(); physics.HasPosition = true; physics.IsOnGround = packet.IsOnGround; physics.NegativeAngularVelocity = packet.NegativeAngularVelocity; physics.HasVelocity = packet.HasVelocity; physics.Velocity = packet.Velocity; physics.HasAngularVelocity = packet.HasAngularVelocity; physics.AngularVelocity = packet.AngularVelocity; physics.Platform = default; //player.Zone.GameObjects.FirstOrDefault(g => g.ObjectId == packet.PlatformObjectId); physics.PlatformPosition = packet.PlatformPosition; Zone.SendSerialization(player, player.Zone.Players.Where( p => p != player ).ToArray()); player.UpdateView(); await player.OnPositionUpdate.InvokeAsync(packet.Position, packet.Rotation); }
public async Task DeleteCharacter(CharacterDeleteRequest packet, IRakConnection connection) { await using var ctx = new UchuContext(); try { ctx.Characters.Remove(await ctx.Characters.FindAsync(packet.CharacterId)); await ctx.SaveChangesAsync(); connection.Send(new CharacterDeleteResponse()); } catch (Exception e) { Logger.Error($"Character deletion failed for {connection}'s character {packet.CharacterId}\n{e}"); connection.Send(new CharacterDeleteResponse { Success = false }); } }
public void Handshake(HandshakePacket packet, IRakConnection connection) { if (packet == null) { throw new ArgumentNullException(nameof(packet), ResourceStrings.GeneralHandler_Handshake_PacketNullException); } if (packet.GameVersion != 171022) { Logger.Warning($"Handshake attempted with client of Game version: {packet.GameVersion}"); } connection.Send(new HandshakePacket { ConnectionType = UchuServer.Port == UchuServer.Config.Networking.AuthenticationPort ? 0x01u : 0x04u, Address = UchuServer.Host }); }
public void AddConnection(IRakConnection connection) { if (!_connections.TryAdd(connection.EndPoint, connection)) { return; } foreach (var replica in _replicas.Keys) { SendConstruction(replica, false, new[] { connection.EndPoint }); } connection.Disconnected += reason => { _connections.TryRemove(connection.EndPoint, out _); return(Task.CompletedTask); }; }
public async Task FriendsListRequestHandler(GetFriendListPacket packet, IRakConnection connection) { var session = UchuServer.SessionCache.GetSession(connection.EndPoint); var zone = ((WorldUchuServer)UchuServer).Zones.FirstOrDefault(z => (int)z.ZoneId == session.ZoneId); if (zone == default) { Logger.Error($"Invalid ZoneId for {connection}"); return; } await using var ctx = new UchuContext(); var character = await ctx.Characters.FirstAsync(c => c.Id == session.CharacterId); var relations = await ctx.Friends.Where(f => f.FriendA == character.Id || f.FriendB == character.Id ).ToArrayAsync(); var friends = new List <FriendListPacket.Friend>(); foreach (var characterFriend in relations) { var friendId = characterFriend.FriendA == character.Id ? characterFriend.FriendB : characterFriend.FriendA; var friend = ctx.Characters.First(c => c.Id == friendId); var player = zone.Players.FirstOrDefault(p => p.Id == friend.Id); friends.Add(new FriendListPacket.Friend { IsBestFriend = characterFriend.BestFriend, IsFreeToPlay = friend.FreeToPlay, IsOnline = player != default, PlayerId = player?.Id ?? -1, PlayerName = friend.Name, ZoneId = (ZoneId)friend.LastZone, WorldClone = (uint)friend.LastClone, WorldInstance = (ushort)friend.LastInstance });
public async Task PositionHandler(PositionUpdatePacket packet, IRakConnection connection) { var player = UchuServer.FindPlayer(connection); if (player?.Transform == default) { return; } // The server is a slave to the position update packets it gets from the client right now. player.Transform.Position = packet.Position; player.Transform.Rotation = packet.Rotation; var physics = player.GetComponent <ControllablePhysicsComponent>(); physics.HasPosition = true; physics.IsOnGround = packet.IsOnGround; physics.NegativeAngularVelocity = packet.NegativeAngularVelocity; physics.HasVelocity = (packet.Velocity != Vector3.Zero); physics.Velocity = packet.Velocity; physics.HasAngularVelocity = (packet.AngularVelocity != Vector3.Zero); physics.AngularVelocity = packet.AngularVelocity; physics.Platform = packet.PlatformObjectId; physics.PlatformPosition = packet.PlatformPosition; Zone.SendSerialization(player, player.Zone.Players.Where( p => p != player ).ToArray()); player.UpdateView(); await player.OnPositionUpdate.InvokeAsync(packet.Position, packet.Rotation); }
public void Handshake(HandshakePacket packet, IRakConnection connection) { if (packet == null) { throw new ArgumentNullException(nameof(packet), ResourceStrings.GeneralHandler_Handshake_PacketNullException); } if (packet.GameVersion != 171022) { Logger.Warning($"Handshake attempted with client of Game version: {packet.GameVersion}"); } // TODO: Use resource / setting const int port = 21836; connection.Send(new HandshakePacket { ConnectionType = UchuServer.Port == port ? 0x01u : 0x04u, Address = UchuServer.Host }); }
public async Task AddFriendRequestHandler(AddFriendRequestPacket packet, IRakConnection connection) { var session = Server.SessionCache.GetSession(connection.EndPoint); var zone = ((WorldServer)Server).Zones.FirstOrDefault(z => z.ZoneId == session.ZoneId); if (zone == default) { Logger.Error($"Invalid ZoneId for {connection}"); return; } var player = zone.Players.First(p => p.Connection.Equals(connection)); await using var ctx = new UchuContext(); var friend = await ctx.Characters.FirstOrDefaultAsync(c => c.Name == packet.PlayerName); if (friend == default) { Logger.Information( $"{player.Name} is trying to be friends with a none existent player {packet.PlayerName}" ); player.Message(new NotifyFriendRequestResponsePacket { PlayerName = packet.PlayerName, Response = ServerFriendRequestResponse.InvalidName }); return; } var character = ctx.Characters.First(c => c.Id == session.CharacterId); var alreadyFriends = await ctx.Friends.Where(f => f.FriendA == character.Id || f.FriendB == character.Id ).AnyAsync(); if (alreadyFriends) { player.Message(new NotifyFriendRequestResponsePacket { PlayerName = packet.PlayerName, Response = ServerFriendRequestResponse.AlreadyFriends }); return; } var isPending = await ctx.FriendRequests.Where(f => f.Sender == character.Id && f.Receiver == friend.Id ).AnyAsync(); if (isPending) { player.Message(new NotifyFriendRequestResponsePacket { PlayerName = packet.PlayerName, Response = ServerFriendRequestResponse.PendingApproval }); return; } var request = new FriendRequest { Sender = character.Id, Receiver = friend.Id, BestFriend = packet.IsRequestingBestFriend }; await ctx.FriendRequests.AddAsync(request); }
public async Task LoginRequestHandler(ClientLoginInfoPacket packet, IRakConnection connection) { await using var ctx = new UchuContext(); var info = new ServerLoginInfoPacket { CharacterInstanceAddress = Server.GetHost(), CharacterInstancePort = ushort.MaxValue, ChatInstanceAddress = Server.GetHost(), ChatInstancePort = 2004 }; var characterSpecification = await ServerHelper.GetServerByType(ServerType.Character); if (characterSpecification == default) { info.LoginCode = LoginCode.InsufficientPermissions; info.Error = new ServerLoginInfoPacket.ErrorMessage { Message = "No character server instance is running. Please try again later." }; } else { info.CharacterInstancePort = (ushort)characterSpecification.Port; if (!await ctx.Users.AnyAsync(u => u.Username == packet.Username)) { info.LoginCode = LoginCode.InsufficientPermissions; info.Error = new ServerLoginInfoPacket.ErrorMessage { Message = "We have no records of that Username and Password combination. Please try again." }; } else { var user = await ctx.Users.SingleAsync(u => u.Username == packet.Username); if (user != null && BCrypt.Net.BCrypt.EnhancedVerify(packet.Password, user.Password)) { if (user.Banned) { info.LoginCode = LoginCode.InsufficientPermissions; info.Error = new ServerLoginInfoPacket.ErrorMessage { Message = $"This account has been banned by an admin. Reason:\n{user.BannedReason ?? "Unknown"}" }; } else if (!string.IsNullOrWhiteSpace(user.CustomLockout)) { info.LoginCode = LoginCode.InsufficientPermissions; info.Error = new ServerLoginInfoPacket.ErrorMessage { Message = user.CustomLockout }; } else { var key = Server.SessionCache.CreateSession(user.UserId); info.LoginCode = LoginCode.Success; info.UserKey = key; // // I don't intend, nor do I see anyone else, using these. // Except maybe FirstTimeOnSubscription for the fancy screen. // info.FreeToPlay = user.FreeToPlay; info.FirstLoginWithSubscription = user.FirstTimeOnSubscription; // // No longer the first time on subscription // user.FirstTimeOnSubscription = false; await ctx.SaveChangesAsync(); } } else { info.LoginCode = LoginCode.InsufficientPermissions; info.Error = new ServerLoginInfoPacket.ErrorMessage { Message = "We have no records of that Username and Password combination. Please try again." }; } } } connection.Send(info); }