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 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 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 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 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 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 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 }); } }
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 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 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 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 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 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 ClientLoadCompleteHandler(ClientLoadCompletePacket packet, IRakConnection connection) { Logger.Information($"{connection.EndPoint}'s client load completed..."); var session = Server.SessionCache.GetSession(connection.EndPoint); await using var ctx = new UchuContext(); var character = await ctx.Characters .Include(c => c.Items) .Include(c => c.User) .Include(c => c.Missions) .ThenInclude(m => m.Tasks).ThenInclude(m => m.Values) .SingleAsync(c => c.CharacterId == session.CharacterId); var zoneId = (ZoneId)character.LastZone; if (zoneId == ZoneId.VentureExplorerCinematic) { zoneId = ZoneId.VentureExplorer; character.LastZone = (int)zoneId; await ctx.SaveChangesAsync(); } Server.SessionCache.SetZone(connection.EndPoint, zoneId); // Zone should already be initialized at this point. var zone = await((WorldServer)Server).GetZoneAsync(zoneId); // Send the character init XML data for this world to the client await SendCharacterXmlDataToClient(character, connection, session); var player = await Player.ConstructAsync(character, connection, zone); if (character.LandingByRocket) { character.LandingByRocket = false; await ctx.SaveChangesAsync(); } player.Message(new PlayerReadyMessage { Associate = player }); player.Message(new DoneLoadingObjectsMessage { Associate = player }); var relations = ctx.Friends.Where(f => f.FriendTwoId == character.CharacterId ).ToArray(); foreach (var friend in relations.Where(f => !f.RequestHasBeenSent)) { connection.Send(new NotifyFriendRequestPacket { FriendName = (await ctx.Characters.SingleAsync(c => c.CharacterId == friend.FriendTwoId)).Name, IsBestFriendRequest = friend.RequestingBestFriend }); } }
public static void Send(this IRakConnection @this, MemoryStream stream) { @this.Send(stream.ToArray()); }
public async Task CharacterCreate(CharacterCreateRequest packet, IRakConnection connection) { var session = Server.SessionCache.GetSession(connection.EndPoint); uint shirtLot; uint pantsLot; await using (var ctx = new CdClientContext()) { // // Shirt // var shirtColor = await ctx.BrickColorsTable.FirstOrDefaultAsync(c => c.Id == packet.ShirtColor); var shirtName = $"{(shirtColor != null ? shirtColor.Description : "Bright Red")} Shirt {packet.ShirtStyle}"; var shirt = ctx.ObjectsTable.ToArray().FirstOrDefault(o => string.Equals(o.Name, shirtName, StringComparison.CurrentCultureIgnoreCase)); shirtLot = (uint)(shirt != null ? shirt.Id : 4049); // Select 'Bright Red Shirt 1' if not found. // // Pants // var pantsColor = await ctx.BrickColorsTable.FirstOrDefaultAsync(c => c.Id == packet.PantsColor); var pantsName = $"{(pantsColor != null ? pantsColor.Description : "Bright Red")} Pants"; var pants = ctx.ObjectsTable.ToArray().FirstOrDefault(o => string.Equals(o.Name, pantsName, StringComparison.CurrentCultureIgnoreCase)); pantsLot = (uint)(pants != null ? pants.Id : 2508); // Select 'Bright Red Pants' if not found. } var first = (await Server.Resources.ReadTextAsync("names/minifigname_first.txt")).Split('\n'); var middle = (await Server.Resources.ReadTextAsync("names/minifigname_middle.txt")).Split('\n'); var last = (await Server.Resources.ReadTextAsync("names/minifigname_last.txt")).Split('\n'); var name = ( first[packet.Predefined.First] + middle[packet.Predefined.Middle] + last[packet.Predefined.Last] ).Replace("\r", ""); await using (var ctx = new UchuContext()) { if (ctx.Characters.Any(c => c.Name == packet.CharacterName)) { Logger.Debug($"{connection} character create rejected due to duplicate name"); connection.Send(new CharacterCreateResponse { ResponseId = CharacterCreationResponse.CustomNameInUse } ); return; } if (ctx.Characters.Any(c => c.Name == name)) { Logger.Debug($"{connection} character create rejected due to duplicate pre-made name"); connection.Send(new CharacterCreateResponse { ResponseId = CharacterCreationResponse.PredefinedNameInUse } ); return; } var user = await ctx.Users.Include(u => u.Characters).SingleAsync(u => u.UserId == session.UserId); user.Characters.Add(new Character { CharacterId = IdUtilities.GenerateObjectId(), Name = name, CustomName = packet.CharacterName, ShirtColor = packet.ShirtColor, ShirtStyle = packet.ShirtStyle, PantsColor = packet.PantsColor, HairStyle = packet.HairStyle, HairColor = packet.HairColor, Lh = packet.Lh, Rh = packet.Rh, EyebrowStyle = packet.EyebrowStyle, EyeStyle = packet.EyeStyle, MouthStyle = packet.MouthStyle, LastZone = (int)ZoneId.VentureExplorerCinematic, LastInstance = 0, LastClone = 0, InventorySize = 20, LastActivity = DateTimeOffset.Now.ToUnixTimeSeconds(), Items = new List <InventoryItem> { new InventoryItem { InventoryItemId = IdUtilities.GenerateObjectId(), LOT = (int)shirtLot, Slot = 0, Count = 1, InventoryType = (int)InventoryType.Items, IsEquipped = true }, new InventoryItem { InventoryItemId = IdUtilities.GenerateObjectId(), LOT = (int)pantsLot, Slot = 1, Count = 1, InventoryType = (int)InventoryType.Items, IsEquipped = true } }, CurrentImagination = 0, MaximumImagination = 0 }); Logger.Debug( $"{user.Username} created character \"{packet.CharacterName}\" with the pre-made name of \"{name}\""); await ctx.SaveChangesAsync(); connection.Send(new CharacterCreateResponse { ResponseId = CharacterCreationResponse.Success } ); await SendCharacterList(connection, session.UserId); } }
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); }
public async Task LoginRequestHandler(ClientLoginInfoPacket packet, IRakConnection connection) { await using var ctx = new UchuContext(); var info = new ServerLoginInfoPacket { CharacterInstanceAddress = UchuServer.Host, CharacterInstancePort = ushort.MaxValue, ChatInstanceAddress = UchuServer.Host, ChatInstancePort = 2004 }; var characterSpecification = await UchuServer.Api.RunCommandAsync <InstanceInfoResponse>( UchuServer.MasterApi, $"instance/basic?t={(int) ServerType.Character}" ).ConfigureAwait(false); if (!characterSpecification.Success) { Logger.Error(characterSpecification.FailedReason); info.LoginCode = LoginCode.InsufficientPermissions; info.Error = new ErrorMessage { Message = "No character server instance is running. Please try again later." }; } else { info.CharacterInstancePort = (ushort)characterSpecification.Info.Port; if (!await ctx.Users.AnyAsync(u => string.Equals(u.Username.ToUpper(), packet.Username.ToUpper()) && !u.Sso)) { info.LoginCode = LoginCode.InsufficientPermissions; info.Error = new ErrorMessage { Message = "We have no records of that Username and Password combination. Please try again." }; } else { var user = await ctx.Users.FirstAsync(u => string.Equals(u.Username.ToUpper(), packet.Username.ToUpper()) && !u.Sso); if (user != null && BCrypt.Net.BCrypt.EnhancedVerify(packet.Password, user.Password)) { if (user.Banned) { info.LoginCode = LoginCode.InsufficientPermissions; info.Error = new 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 ErrorMessage { Message = user.CustomLockout }; } else { var key = UchuServer.SessionCache.CreateSession(user.Id); 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 ErrorMessage { Message = "We have no records of that Username and Password combination. Please try again." }; } } } connection.Send(info); }