From 13ae843c8d58d1d01c1e11fe65bc4f2101bce7a1 Mon Sep 17 00:00:00 2001 From: flashwave Date: Thu, 16 Feb 2023 23:33:48 +0100 Subject: [PATCH] No longer keep track of connections within the ChatUser class. --- SharpChat/ChatChannel.cs | 5 - SharpChat/ChatConnection.cs | 6 +- SharpChat/ChatContext.cs | 116 +++++++++++++----- SharpChat/ChatUser.cs | 50 -------- SharpChat/Commands/AFKCommand.cs | 2 +- SharpChat/Commands/BanListCommand.cs | 4 +- SharpChat/Commands/BroadcastCommand.cs | 4 +- SharpChat/Commands/CreateChannelCommand.cs | 14 +-- SharpChat/Commands/DeleteChannelCommand.cs | 8 +- SharpChat/Commands/DeleteMessageCommand.cs | 6 +- SharpChat/Commands/JoinChannelCommand.cs | 4 +- SharpChat/Commands/KickBanCommand.cs | 19 +-- SharpChat/Commands/NickCommand.cs | 8 +- SharpChat/Commands/PardonAddressCommand.cs | 10 +- SharpChat/Commands/PardonUserCommand.cs | 10 +- SharpChat/Commands/PasswordChannelCommand.cs | 4 +- SharpChat/Commands/RankChannelCommand.cs | 6 +- SharpChat/Commands/RemoteAddressCommand.cs | 8 +- SharpChat/Commands/ShutdownRestartCommand.cs | 2 +- SharpChat/Commands/SilenceApplyCommand.cs | 16 +-- SharpChat/Commands/SilenceRevokeCommand.cs | 12 +- SharpChat/Commands/WhisperCommand.cs | 8 +- SharpChat/Commands/WhoCommand.cs | 8 +- SharpChat/IPacketTarget.cs | 1 - SharpChat/PacketHandlers/AuthHandler.cs | 31 +++-- SharpChat/PacketHandlers/PingHandler.cs | 14 ++- .../PacketHandlers/SendMessageHandler.cs | 4 +- SharpChat/SockChatServer.cs | 31 ++--- 28 files changed, 202 insertions(+), 209 deletions(-) diff --git a/SharpChat/ChatChannel.cs b/SharpChat/ChatChannel.cs index 2c6bb5e..14a6219 100644 --- a/SharpChat/ChatChannel.cs +++ b/SharpChat/ChatChannel.cs @@ -46,11 +46,6 @@ namespace SharpChat { user.LeaveChannel(this); } - public void Send(IServerPacket packet) { - foreach(ChatUser user in Users) - user.Send(packet); - } - public IEnumerable GetUsers(IEnumerable exclude = null) { IEnumerable users = Users.OrderByDescending(x => x.Rank); diff --git a/SharpChat/ChatConnection.cs b/SharpChat/ChatConnection.cs index 9d5e6c4..8d34de2 100644 --- a/SharpChat/ChatConnection.cs +++ b/SharpChat/ChatConnection.cs @@ -17,7 +17,7 @@ namespace SharpChat { public string Id { get; private set; } public bool IsDisposed { get; private set; } - public DateTimeOffset LastPing { get; set; } = DateTimeOffset.MinValue; + public DateTimeOffset LastPing { get; set; } = DateTimeOffset.Now; public ChatUser User { get; set; } private int CloseCode { get; set; } = 1000; @@ -39,6 +39,10 @@ namespace SharpChat { } } + public bool IsAlive => !IsDisposed && !HasTimedOut; + + public bool IsAuthed => IsAlive && User is not null; + public ChatConnection(IWebSocketConnection sock) { Socket = sock; Id = RNG.SecureRandomString(ID_LENGTH); diff --git a/SharpChat/ChatContext.cs b/SharpChat/ChatContext.cs index eede29f..0c850d5 100644 --- a/SharpChat/ChatContext.cs +++ b/SharpChat/ChatContext.cs @@ -5,6 +5,7 @@ using SharpChat.Packet; using System; using System.Collections.Generic; using System.Linq; +using System.Net; namespace SharpChat { public class ChatContext { @@ -25,19 +26,22 @@ namespace SharpChat { } public void Update() { - lock(UsersAccess) - foreach(ChatUser user in Users) { - IEnumerable timedOut = user.GetDeadConnections(); - - foreach(ChatConnection conn in timedOut) { - user.RemoveConnection(conn); + lock(ConnectionsAccess) { + foreach(ChatConnection conn in Connections) + if(!conn.IsDisposed && conn.HasTimedOut) { conn.Dispose(); - Logger.Write($"Nuked session {conn.Id} from {user.Username} (timeout)"); + Logger.Write($"Nuked connection {conn.Id} associated with {conn.User}."); } - if(!user.HasConnections) - UserLeave(null, user, UserDisconnectReason.TimeOut); - } + Connections.RemoveWhere(conn => conn.IsDisposed); + + lock(UsersAccess) + foreach(ChatUser user in Users) + if(!Connections.Any(conn => conn.User == user)) { + UserLeave(null, user, UserDisconnectReason.TimeOut); + Logger.Write($"Timed out {user} (no more connections)."); + } + } } public ChatConnection GetConnection(IWebSocketConnection sock) { @@ -46,18 +50,24 @@ namespace SharpChat { public void BanUser(ChatUser user, TimeSpan duration, UserDisconnectReason reason = UserDisconnectReason.Kicked) { if(duration > TimeSpan.Zero) - user.Send(new ForceDisconnectPacket(ForceDisconnectReason.Banned, DateTimeOffset.Now + duration)); + SendTo(user, new ForceDisconnectPacket(ForceDisconnectReason.Banned, DateTimeOffset.Now + duration)); else - user.Send(new ForceDisconnectPacket(ForceDisconnectReason.Kicked)); + SendTo(user, new ForceDisconnectPacket(ForceDisconnectReason.Kicked)); + + lock(ConnectionsAccess) { + foreach(ChatConnection conn in Connections) + if(conn.User == user) + conn.Dispose(); + Connections.RemoveWhere(conn => conn.IsDisposed); + } - user.Close(); UserLeave(user.Channel, user, reason); } public void HandleJoin(ChatUser user, ChatChannel chan, ChatConnection conn, int maxMsgLength) { lock(EventsAccess) { if(!chan.HasUser(user)) { - chan.Send(new UserConnectPacket(DateTimeOffset.Now, user)); + SendTo(chan, new UserConnectPacket(DateTimeOffset.Now, user)); Events.AddEvent(new UserConnectEvent(DateTimeOffset.Now, user, chan)); } @@ -94,28 +104,27 @@ namespace SharpChat { lock(EventsAccess) { chan.UserLeave(user); - chan.Send(new UserDisconnectPacket(DateTimeOffset.Now, user, reason)); + SendTo(chan, new UserDisconnectPacket(DateTimeOffset.Now, user, reason)); Events.AddEvent(new UserDisconnectEvent(DateTimeOffset.Now, user, chan, reason)); } } public void SwitchChannel(ChatUser user, ChatChannel chan, string password) { if(user.CurrentChannel == chan) { - //user.Send(true, "samechan", chan.Name); - user.ForceChannel(); + ForceChannel(user); return; } if(!user.Can(ChatUserPermissions.JoinAnyChannel) && chan.Owner != user) { if(chan.Rank > user.Rank) { - user.Send(new LegacyCommandResponse(LCR.CHANNEL_INSUFFICIENT_HIERARCHY, true, chan.Name)); - user.ForceChannel(); + SendTo(user, new LegacyCommandResponse(LCR.CHANNEL_INSUFFICIENT_HIERARCHY, true, chan.Name)); + ForceChannel(user); return; } if(chan.Password != password) { - user.Send(new LegacyCommandResponse(LCR.CHANNEL_INVALID_PASSWORD, true, chan.Name)); - user.ForceChannel(); + SendTo(user, new LegacyCommandResponse(LCR.CHANNEL_INVALID_PASSWORD, true, chan.Name)); + ForceChannel(user); return; } } @@ -131,18 +140,18 @@ namespace SharpChat { ChatChannel oldChan = user.CurrentChannel; lock(EventsAccess) { - oldChan.Send(new UserChannelLeavePacket(user)); + SendTo(oldChan, new UserChannelLeavePacket(user)); Events.AddEvent(new UserChannelLeaveEvent(DateTimeOffset.Now, user, oldChan)); - chan.Send(new UserChannelJoinPacket(user)); + SendTo(chan, new UserChannelJoinPacket(user)); Events.AddEvent(new UserChannelJoinEvent(DateTimeOffset.Now, user, chan)); - user.Send(new ContextClearPacket(chan, ContextClearMode.MessagesUsers)); - user.Send(new ContextUsersPacket(chan.GetUsers(new[] { user }))); + SendTo(user, new ContextClearPacket(chan, ContextClearMode.MessagesUsers)); + SendTo(user, new ContextUsersPacket(chan.GetUsers(new[] { user }))); foreach(IChatEvent msg in Events.GetTargetEventLog(chan.Name)) - user.Send(new ContextMessagePacket(msg)); + SendTo(user, new ContextMessagePacket(msg)); - user.ForceChannel(chan); + ForceChannel(user, chan); oldChan.UserLeave(user); chan.UserJoin(user); } @@ -153,9 +162,50 @@ namespace SharpChat { } public void Send(IServerPacket packet) { - lock(UsersAccess) - foreach(ChatUser user in Users) - user.Send(packet); + if(packet == null) + throw new ArgumentNullException(nameof(packet)); + + lock(ConnectionsAccess) + foreach(ChatConnection conn in Connections) + if(conn.IsAuthed) + conn.Send(packet); + } + + public void SendTo(ChatUser user, IServerPacket packet) { + if(user == null) + throw new ArgumentNullException(nameof(user)); + if(packet == null) + throw new ArgumentNullException(nameof(packet)); + + lock(ConnectionsAccess) + foreach(ChatConnection conn in Connections) + if(conn.IsAlive && conn.User == user) + conn.Send(packet); + } + + public void SendTo(ChatChannel channel, IServerPacket packet) { + if(channel == null) + throw new ArgumentNullException(nameof(channel)); + if(packet == null) + throw new ArgumentNullException(nameof(packet)); + + lock(ConnectionsAccess) { + IEnumerable conns = Connections.Where(c => c.IsAuthed && channel.HasUser(c.User)); + foreach(ChatConnection conn in conns) + conn.Send(packet); + } + } + + public IPAddress[] GetRemoteAddresses(ChatUser user) { + lock(ConnectionsAccess) + return Connections.Where(c => c.IsAlive && c.User == user).Select(c => c.RemoteAddress).Distinct().ToArray(); + } + + public void ForceChannel(ChatUser user, ChatChannel chan = null) { + if(user == null) + throw new ArgumentNullException(nameof(user)); + + SendTo(user, new UserChannelForceJoinPacket(chan ?? user.CurrentChannel)); } public void UpdateChannel(ChatChannel channel, string name = null, bool? temporary = null, int? hierarchy = null, string password = null) { @@ -187,10 +237,10 @@ namespace SharpChat { // Users that no longer have access to the channel/gained access to the channel by the hierarchy change should receive delete and create packets respectively lock(UsersAccess) foreach(ChatUser user in Users.Where(u => u.Rank >= channel.Rank)) { - user.Send(new ChannelUpdatePacket(prevName, channel)); + SendTo(user, new ChannelUpdatePacket(prevName, channel)); if(nameUpdated) - user.ForceChannel(); + ForceChannel(user); } } @@ -213,7 +263,7 @@ namespace SharpChat { // Broadcast deletion of channel lock(UsersAccess) foreach(ChatUser user in Users.Where(u => u.Rank >= channel.Rank)) - user.Send(new ChannelDeletePacket(channel)); + SendTo(user, new ChannelDeletePacket(channel)); } } } diff --git a/SharpChat/ChatUser.cs b/SharpChat/ChatUser.cs index 7828789..61a329c 100644 --- a/SharpChat/ChatUser.cs +++ b/SharpChat/ChatUser.cs @@ -1,9 +1,7 @@ using SharpChat.Misuzu; -using SharpChat.Packet; using System; using System.Collections.Generic; using System.Linq; -using System.Net; using System.Text; namespace SharpChat { @@ -83,7 +81,6 @@ namespace SharpChat { public class ChatUser : BasicUser, IPacketTarget { public DateTimeOffset SilencedUntil { get; set; } - private readonly List Connections = new(); private readonly List Channels = new(); public readonly ChatRateLimiter RateLimiter = new(); @@ -98,12 +95,6 @@ namespace SharpChat { public bool IsSilenced => DateTimeOffset.UtcNow - SilencedUntil <= TimeSpan.Zero; - public bool HasConnections => Connections.Where(c => !c.HasTimedOut && !c.IsDisposed).Any(); - - public int ConnectionCount => Connections.Where(c => !c.HasTimedOut && !c.IsDisposed).Count(); - - public IEnumerable RemoteAddresses => Connections.Select(c => c.RemoteAddress); - public ChatUser() {} public ChatUser(MisuzuAuthInfo auth) { @@ -125,26 +116,6 @@ namespace SharpChat { SilencedUntil = auth.SilencedUntil; } - public void Send(IServerPacket packet) { - foreach(ChatConnection conn in Connections) - conn.Send(packet); - } - - public void Close() { - foreach(ChatConnection conn in Connections) - conn.Dispose(); - Connections.Clear(); - } - - public void ForceChannel(ChatChannel chan = null) { - Send(new UserChannelForceJoinPacket(chan ?? CurrentChannel)); - } - - public void FocusChannel(ChatChannel chan) { - if(InChannel(chan)) - CurrentChannel = chan; - } - public bool InChannel(ChatChannel chan) { return Channels.Contains(chan); } @@ -165,27 +136,6 @@ namespace SharpChat { return Channels.ToList(); } - public void AddConnection(ChatConnection conn) { - if(conn == null) - return; - conn.User = this; - - Connections.Add(conn); - } - - public void RemoveConnection(ChatConnection conn) { - if(conn == null) - return; - if(!conn.IsDisposed) // this could be possible - conn.User = null; - - Connections.Remove(conn); - } - - public IEnumerable GetDeadConnections() { - return Connections.Where(x => x.HasTimedOut || x.IsDisposed).ToList(); - } - public bool NameEquals(string name) { return string.Equals(name, Username, StringComparison.InvariantCultureIgnoreCase) || string.Equals(name, Nickname, StringComparison.InvariantCultureIgnoreCase) diff --git a/SharpChat/Commands/AFKCommand.cs b/SharpChat/Commands/AFKCommand.cs index 67ff58d..9c2778a 100644 --- a/SharpChat/Commands/AFKCommand.cs +++ b/SharpChat/Commands/AFKCommand.cs @@ -22,7 +22,7 @@ namespace SharpChat.Commands { ctx.User.Status = ChatUserStatus.Away; ctx.User.StatusMessage = statusText; - ctx.Channel.Send(new UserUpdatePacket(ctx.User)); + ctx.Chat.SendTo(ctx.Channel, new UserUpdatePacket(ctx.User)); } } } diff --git a/SharpChat/Commands/BanListCommand.cs b/SharpChat/Commands/BanListCommand.cs index 6371356..02c67c2 100644 --- a/SharpChat/Commands/BanListCommand.cs +++ b/SharpChat/Commands/BanListCommand.cs @@ -18,12 +18,12 @@ namespace SharpChat.Commands { public void Dispatch(ChatCommandContext ctx) { if(!ctx.User.Can(ChatUserPermissions.BanUser | ChatUserPermissions.KickUser)) { - ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); return; } Task.Run(async () => { - ctx.User.Send(new BanListPacket( + ctx.Chat.SendTo(ctx.User, new BanListPacket( await Misuzu.GetBanListAsync() )); }).Wait(); diff --git a/SharpChat/Commands/BroadcastCommand.cs b/SharpChat/Commands/BroadcastCommand.cs index 37fc090..6ffa24c 100644 --- a/SharpChat/Commands/BroadcastCommand.cs +++ b/SharpChat/Commands/BroadcastCommand.cs @@ -9,11 +9,11 @@ namespace SharpChat.Commands { public void Dispatch(ChatCommandContext ctx) { if(!ctx.User.Can(ChatUserPermissions.Broadcast)) { - ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); return; } - ctx.Chat.Send(new LegacyCommandResponse(LCR.BROADCAST, false, string.Join(' ', ctx.Args))); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.BROADCAST, false, string.Join(' ', ctx.Args))); } } } diff --git a/SharpChat/Commands/CreateChannelCommand.cs b/SharpChat/Commands/CreateChannelCommand.cs index 6a3a0c0..018d8b0 100644 --- a/SharpChat/Commands/CreateChannelCommand.cs +++ b/SharpChat/Commands/CreateChannelCommand.cs @@ -9,7 +9,7 @@ namespace SharpChat.Commands { public void Dispatch(ChatCommandContext ctx) { if(ctx.User.Can(ChatUserPermissions.CreateChannel)) { - ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); return; } @@ -17,7 +17,7 @@ namespace SharpChat.Commands { bool createChanHasHierarchy; if(!ctx.Args.Any() || (createChanHasHierarchy = firstArg.All(char.IsDigit) && ctx.Args.Length < 2)) { - ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR)); return; } @@ -27,20 +27,20 @@ namespace SharpChat.Commands { createChanHierarchy = 0; if(createChanHierarchy > ctx.User.Rank) { - ctx.User.Send(new LegacyCommandResponse(LCR.INSUFFICIENT_HIERARCHY)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.INSUFFICIENT_HIERARCHY)); return; } string createChanName = string.Join('_', ctx.Args.Skip(createChanHasHierarchy ? 1 : 0)); if(!ChatChannel.CheckName(createChanName)) { - ctx.User.Send(new LegacyCommandResponse(LCR.CHANNEL_NAME_INVALID)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.CHANNEL_NAME_INVALID)); return; } lock(ctx.Chat.ChannelsAccess) { if(ctx.Chat.Channels.Any(c => c.NameEquals(createChanName))) { - ctx.User.Send(new LegacyCommandResponse(LCR.CHANNEL_ALREADY_EXISTS, true, createChanName)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.CHANNEL_ALREADY_EXISTS, true, createChanName)); return; } @@ -54,11 +54,11 @@ namespace SharpChat.Commands { ctx.Chat.Channels.Add(createChan); lock(ctx.Chat.UsersAccess) { foreach(ChatUser ccu in ctx.Chat.Users.Where(u => u.Rank >= ctx.Channel.Rank)) - ccu.Send(new ChannelCreatePacket(ctx.Channel)); + ctx.Chat.SendTo(ccu, new ChannelCreatePacket(ctx.Channel)); } ctx.Chat.SwitchChannel(ctx.User, createChan, createChan.Password); - ctx.User.Send(new LegacyCommandResponse(LCR.CHANNEL_CREATED, false, createChan.Name)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.CHANNEL_CREATED, false, createChan.Name)); } } } diff --git a/SharpChat/Commands/DeleteChannelCommand.cs b/SharpChat/Commands/DeleteChannelCommand.cs index 925f159..c975729 100644 --- a/SharpChat/Commands/DeleteChannelCommand.cs +++ b/SharpChat/Commands/DeleteChannelCommand.cs @@ -12,7 +12,7 @@ namespace SharpChat.Commands { public void Dispatch(ChatCommandContext ctx) { if(!ctx.Args.Any() || string.IsNullOrWhiteSpace(ctx.Args.FirstOrDefault())) { - ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR)); return; } @@ -22,18 +22,18 @@ namespace SharpChat.Commands { delChan = ctx.Chat.Channels.FirstOrDefault(c => c.NameEquals(delChanName)); if(delChan == null) { - ctx.User.Send(new LegacyCommandResponse(LCR.CHANNEL_NOT_FOUND, true, delChanName)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.CHANNEL_NOT_FOUND, true, delChanName)); return; } if(!ctx.User.Can(ChatUserPermissions.DeleteChannel) && delChan.Owner != ctx.User) { - ctx.User.Send(new LegacyCommandResponse(LCR.CHANNEL_DELETE_FAILED, true, delChan.Name)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.CHANNEL_DELETE_FAILED, true, delChan.Name)); return; } lock(ctx.Chat.ChannelsAccess) ctx.Chat.RemoveChannel(delChan); - ctx.User.Send(new LegacyCommandResponse(LCR.CHANNEL_DELETED, false, delChan.Name)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.CHANNEL_DELETED, false, delChan.Name)); } } } diff --git a/SharpChat/Commands/DeleteMessageCommand.cs b/SharpChat/Commands/DeleteMessageCommand.cs index d97c2b2..f79b7b8 100644 --- a/SharpChat/Commands/DeleteMessageCommand.cs +++ b/SharpChat/Commands/DeleteMessageCommand.cs @@ -15,14 +15,14 @@ namespace SharpChat.Commands { bool deleteAnyMessage = ctx.User.Can(ChatUserPermissions.DeleteAnyMessage); if(!deleteAnyMessage && !ctx.User.Can(ChatUserPermissions.DeleteOwnMessage)) { - ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); return; } string firstArg = ctx.Args.FirstOrDefault(); if(string.IsNullOrWhiteSpace(firstArg) || !firstArg.All(char.IsDigit) || !long.TryParse(firstArg, out long delSeqId)) { - ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR)); return; } @@ -30,7 +30,7 @@ namespace SharpChat.Commands { IChatEvent delMsg = ctx.Chat.Events.GetEvent(delSeqId); if(delMsg == null || delMsg.Sender.Rank > ctx.User.Rank || (!deleteAnyMessage && delMsg.Sender.UserId != ctx.User.UserId)) { - ctx.User.Send(new LegacyCommandResponse(LCR.MESSAGE_DELETE_ERROR)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.MESSAGE_DELETE_ERROR)); return; } diff --git a/SharpChat/Commands/JoinChannelCommand.cs b/SharpChat/Commands/JoinChannelCommand.cs index edf4623..a2b90cc 100644 --- a/SharpChat/Commands/JoinChannelCommand.cs +++ b/SharpChat/Commands/JoinChannelCommand.cs @@ -14,8 +14,8 @@ namespace SharpChat.Commands { joinChan = ctx.Chat.Channels.FirstOrDefault(c => c.NameEquals(joinChanStr)); if(joinChan == null) { - ctx.User.Send(new LegacyCommandResponse(LCR.CHANNEL_NOT_FOUND, true, joinChanStr)); - ctx.User.ForceChannel(); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.CHANNEL_NOT_FOUND, true, joinChanStr)); + ctx.Chat.ForceChannel(ctx.User); return; } diff --git a/SharpChat/Commands/KickBanCommand.cs b/SharpChat/Commands/KickBanCommand.cs index 5e611ed..9064840 100644 --- a/SharpChat/Commands/KickBanCommand.cs +++ b/SharpChat/Commands/KickBanCommand.cs @@ -21,7 +21,7 @@ namespace SharpChat.Commands { bool isBanning = ctx.NameEquals("ban"); if(!ctx.User.Can(isBanning ? ChatUserPermissions.BanUser : ChatUserPermissions.KickUser)) { - ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); return; } @@ -32,19 +32,19 @@ namespace SharpChat.Commands { lock(ctx.Chat.UsersAccess) if(banUserTarget == null || (banUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(banUserTarget))) == null) { - ctx.User.Send(new LegacyCommandResponse(LCR.USER_NOT_FOUND, true, banUser == null ? "User" : banUserTarget)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USER_NOT_FOUND, true, banUser == null ? "User" : banUserTarget)); return; } if(banUser == ctx.User || banUser.Rank >= ctx.User.Rank) { - ctx.User.Send(new LegacyCommandResponse(LCR.KICK_NOT_ALLOWED, true, banUser.DisplayName)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.KICK_NOT_ALLOWED, true, banUser.DisplayName)); return; } TimeSpan duration = isBanning ? TimeSpan.MaxValue : TimeSpan.Zero; if(!string.IsNullOrWhiteSpace(banDurationStr) && double.TryParse(banDurationStr, out double durationSeconds)) { if(durationSeconds < 0) { - ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR)); return; } @@ -60,18 +60,19 @@ namespace SharpChat.Commands { string banReason = string.Join(' ', ctx.Args.Skip(banReasonIndex)); Task.Run(async () => { + string userId = banUser.UserId.ToString(); + string userIp = ctx.Chat.GetRemoteAddresses(banUser).FirstOrDefault()?.ToString() ?? string.Empty; + // obviously it makes no sense to only check for one ip address but that's current misuzu limitations - MisuzuBanInfo fbi = await Misuzu.CheckBanAsync( - banUser.UserId.ToString(), banUser.RemoteAddresses.First().ToString() - ); + MisuzuBanInfo fbi = await Misuzu.CheckBanAsync(userId, userIp); if(fbi.IsBanned && !fbi.HasExpired) { - ctx.User.Send(new LegacyCommandResponse(LCR.KICK_NOT_ALLOWED, true, banUser.DisplayName)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.KICK_NOT_ALLOWED, true, banUser.DisplayName)); return; } await Misuzu.CreateBanAsync( - banUser.UserId.ToString(), banUser.RemoteAddresses.First().ToString(), + userId, userIp, ctx.User.UserId.ToString(), ctx.Connection.RemoteAddress.ToString(), duration, banReason ); diff --git a/SharpChat/Commands/NickCommand.cs b/SharpChat/Commands/NickCommand.cs index e8f7eb1..9ee260e 100644 --- a/SharpChat/Commands/NickCommand.cs +++ b/SharpChat/Commands/NickCommand.cs @@ -11,7 +11,7 @@ namespace SharpChat.Commands { bool setOthersNick = ctx.User.Can(ChatUserPermissions.SetOthersNickname); if(!setOthersNick && !ctx.User.Can(ChatUserPermissions.SetOwnNickname)) { - ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); return; } ChatUser targetUser = null; @@ -26,7 +26,7 @@ namespace SharpChat.Commands { targetUser ??= ctx.User; if(ctx.Args.Length < offset) { - ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR)); return; } @@ -44,13 +44,13 @@ namespace SharpChat.Commands { lock(ctx.Chat.UsersAccess) if(!string.IsNullOrWhiteSpace(nickStr) && ctx.Chat.Users.Any(u => u.NameEquals(nickStr))) { - ctx.User.Send(new LegacyCommandResponse(LCR.NAME_IN_USE, true, nickStr)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.NAME_IN_USE, true, nickStr)); return; } string previousName = targetUser == ctx.User ? (targetUser.Nickname ?? targetUser.Username) : null; targetUser.Nickname = nickStr; - ctx.Channel.Send(new UserUpdatePacket(targetUser, previousName)); + ctx.Chat.SendTo(ctx.Channel, new UserUpdatePacket(targetUser, previousName)); } } } diff --git a/SharpChat/Commands/PardonAddressCommand.cs b/SharpChat/Commands/PardonAddressCommand.cs index 6a218df..55a8860 100644 --- a/SharpChat/Commands/PardonAddressCommand.cs +++ b/SharpChat/Commands/PardonAddressCommand.cs @@ -20,13 +20,13 @@ namespace SharpChat.Commands { public void Dispatch(ChatCommandContext ctx) { if(!ctx.User.Can(ChatUserPermissions.BanUser | ChatUserPermissions.KickUser)) { - ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); return; } string unbanAddrTarget = ctx.Args.FirstOrDefault(); if(string.IsNullOrWhiteSpace(unbanAddrTarget) || !IPAddress.TryParse(unbanAddrTarget, out IPAddress unbanAddr)) { - ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR)); return; } @@ -36,15 +36,15 @@ namespace SharpChat.Commands { MisuzuBanInfo banInfo = await Misuzu.CheckBanAsync(ipAddr: unbanAddrTarget); if(!banInfo.IsBanned || banInfo.HasExpired) { - ctx.User.Send(new LegacyCommandResponse(LCR.USER_NOT_BANNED, true, unbanAddrTarget)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USER_NOT_BANNED, true, unbanAddrTarget)); return; } bool wasBanned = await Misuzu.RevokeBanAsync(banInfo, MisuzuClient.BanRevokeKind.RemoteAddress); if(wasBanned) - ctx.User.Send(new LegacyCommandResponse(LCR.USER_UNBANNED, false, unbanAddrTarget)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USER_UNBANNED, false, unbanAddrTarget)); else - ctx.User.Send(new LegacyCommandResponse(LCR.USER_NOT_BANNED, true, unbanAddrTarget)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USER_NOT_BANNED, true, unbanAddrTarget)); }).Wait(); } } diff --git a/SharpChat/Commands/PardonUserCommand.cs b/SharpChat/Commands/PardonUserCommand.cs index 5b691a0..e7cb6ee 100644 --- a/SharpChat/Commands/PardonUserCommand.cs +++ b/SharpChat/Commands/PardonUserCommand.cs @@ -19,14 +19,14 @@ namespace SharpChat.Commands { public void Dispatch(ChatCommandContext ctx) { if(!ctx.User.Can(ChatUserPermissions.BanUser | ChatUserPermissions.KickUser)) { - ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); return; } bool unbanUserTargetIsName = true; string unbanUserTarget = ctx.Args.FirstOrDefault(); if(string.IsNullOrWhiteSpace(unbanUserTarget)) { - ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR)); return; } @@ -46,15 +46,15 @@ namespace SharpChat.Commands { MisuzuBanInfo banInfo = await Misuzu.CheckBanAsync(unbanUserTarget, userIdIsName: unbanUserTargetIsName); if(!banInfo.IsBanned || banInfo.HasExpired) { - ctx.User.Send(new LegacyCommandResponse(LCR.USER_NOT_BANNED, true, unbanUserTarget)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USER_NOT_BANNED, true, unbanUserTarget)); return; } bool wasBanned = await Misuzu.RevokeBanAsync(banInfo, MisuzuClient.BanRevokeKind.UserId); if(wasBanned) - ctx.User.Send(new LegacyCommandResponse(LCR.USER_UNBANNED, false, unbanUserTarget)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USER_UNBANNED, false, unbanUserTarget)); else - ctx.User.Send(new LegacyCommandResponse(LCR.USER_NOT_BANNED, true, unbanUserTarget)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USER_NOT_BANNED, true, unbanUserTarget)); }).Wait(); } } diff --git a/SharpChat/Commands/PasswordChannelCommand.cs b/SharpChat/Commands/PasswordChannelCommand.cs index e28122f..4e91b03 100644 --- a/SharpChat/Commands/PasswordChannelCommand.cs +++ b/SharpChat/Commands/PasswordChannelCommand.cs @@ -9,7 +9,7 @@ namespace SharpChat.Commands { public void Dispatch(ChatCommandContext ctx) { if(!ctx.User.Can(ChatUserPermissions.SetChannelPassword) || ctx.Channel.Owner != ctx.User) { - ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); return; } @@ -20,7 +20,7 @@ namespace SharpChat.Commands { lock(ctx.Chat.ChannelsAccess) ctx.Chat.UpdateChannel(ctx.Channel, password: chanPass); - ctx.User.Send(new LegacyCommandResponse(LCR.CHANNEL_PASSWORD_CHANGED, false)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.CHANNEL_PASSWORD_CHANGED, false)); } } } diff --git a/SharpChat/Commands/RankChannelCommand.cs b/SharpChat/Commands/RankChannelCommand.cs index 77c03e5..15f80c0 100644 --- a/SharpChat/Commands/RankChannelCommand.cs +++ b/SharpChat/Commands/RankChannelCommand.cs @@ -11,18 +11,18 @@ namespace SharpChat.Commands { public void Dispatch(ChatCommandContext ctx) { if(!ctx.User.Can(ChatUserPermissions.SetChannelHierarchy) || ctx.Channel.Owner != ctx.User) { - ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); return; } if(!ctx.Args.Any() || !int.TryParse(ctx.Args.First(), out int chanHierarchy) || chanHierarchy > ctx.User.Rank) { - ctx.User.Send(new LegacyCommandResponse(LCR.INSUFFICIENT_HIERARCHY)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.INSUFFICIENT_HIERARCHY)); return; } lock(ctx.Chat.ChannelsAccess) ctx.Chat.UpdateChannel(ctx.Channel, hierarchy: chanHierarchy); - ctx.User.Send(new LegacyCommandResponse(LCR.CHANNEL_HIERARCHY_CHANGED, false)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.CHANNEL_HIERARCHY_CHANGED, false)); } } } diff --git a/SharpChat/Commands/RemoteAddressCommand.cs b/SharpChat/Commands/RemoteAddressCommand.cs index ce59faa..eddce85 100644 --- a/SharpChat/Commands/RemoteAddressCommand.cs +++ b/SharpChat/Commands/RemoteAddressCommand.cs @@ -11,7 +11,7 @@ namespace SharpChat.Commands { public void Dispatch(ChatCommandContext ctx) { if(!ctx.User.Can(ChatUserPermissions.SeeIPAddress)) { - ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, "/ip")); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, "/ip")); return; } @@ -20,12 +20,12 @@ namespace SharpChat.Commands { lock(ctx.Chat.UsersAccess) if(string.IsNullOrWhiteSpace(ipUserStr) || (ipUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(ipUserStr))) == null) { - ctx.User.Send(new LegacyCommandResponse(LCR.USER_NOT_FOUND, true, ipUserStr ?? "User")); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USER_NOT_FOUND, true, ipUserStr ?? "User")); return; } - foreach(IPAddress ip in ipUser.RemoteAddresses.Distinct().ToArray()) - ctx.User.Send(new LegacyCommandResponse(LCR.IP_ADDRESS, false, ipUser.Username, ip)); + foreach(IPAddress ip in ctx.Chat.GetRemoteAddresses(ipUser)) + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.IP_ADDRESS, false, ipUser.Username, ip)); } } } diff --git a/SharpChat/Commands/ShutdownRestartCommand.cs b/SharpChat/Commands/ShutdownRestartCommand.cs index 75a28e0..627f127 100644 --- a/SharpChat/Commands/ShutdownRestartCommand.cs +++ b/SharpChat/Commands/ShutdownRestartCommand.cs @@ -19,7 +19,7 @@ namespace SharpChat.Commands { public void Dispatch(ChatCommandContext ctx) { if(ctx.User.UserId != 1) { - ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); return; } diff --git a/SharpChat/Commands/SilenceApplyCommand.cs b/SharpChat/Commands/SilenceApplyCommand.cs index 9585fa4..64bdcf6 100644 --- a/SharpChat/Commands/SilenceApplyCommand.cs +++ b/SharpChat/Commands/SilenceApplyCommand.cs @@ -10,7 +10,7 @@ namespace SharpChat.Commands { public void Dispatch(ChatCommandContext ctx) { if(!ctx.User.Can(ChatUserPermissions.SilenceUser)) { - ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); return; } @@ -19,22 +19,22 @@ namespace SharpChat.Commands { lock(ctx.Chat.UsersAccess) if(string.IsNullOrWhiteSpace(silUserStr) || (silUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(silUserStr))) == null) { - ctx.User.Send(new LegacyCommandResponse(LCR.USER_NOT_FOUND, true, silUserStr ?? "User")); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USER_NOT_FOUND, true, silUserStr ?? "User")); return; } if(silUser == ctx.User) { - ctx.User.Send(new LegacyCommandResponse(LCR.SILENCE_SELF)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.SILENCE_SELF)); return; } if(silUser.Rank >= ctx.User.Rank) { - ctx.User.Send(new LegacyCommandResponse(LCR.SILENCE_HIERARCHY)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.SILENCE_HIERARCHY)); return; } if(silUser.IsSilenced) { - ctx.User.Send(new LegacyCommandResponse(LCR.SILENCE_ALREADY)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.SILENCE_ALREADY)); return; } @@ -42,7 +42,7 @@ namespace SharpChat.Commands { if(ctx.Args.Length > 1) { if(!double.TryParse(ctx.Args.ElementAt(1), out double silenceSeconds)) { - ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR)); return; } @@ -50,8 +50,8 @@ namespace SharpChat.Commands { } silUser.SilencedUntil = silenceUntil; - silUser.Send(new LegacyCommandResponse(LCR.SILENCED, false)); - ctx.User.Send(new LegacyCommandResponse(LCR.TARGET_SILENCED, false, silUser.DisplayName)); + ctx.Chat.SendTo(silUser, new LegacyCommandResponse(LCR.SILENCED, false)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.TARGET_SILENCED, false, silUser.DisplayName)); } } } diff --git a/SharpChat/Commands/SilenceRevokeCommand.cs b/SharpChat/Commands/SilenceRevokeCommand.cs index 7d7e1bb..f0ee1e3 100644 --- a/SharpChat/Commands/SilenceRevokeCommand.cs +++ b/SharpChat/Commands/SilenceRevokeCommand.cs @@ -10,7 +10,7 @@ namespace SharpChat.Commands { public void Dispatch(ChatCommandContext ctx) { if(!ctx.User.Can(ChatUserPermissions.SilenceUser)) { - ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); return; } @@ -19,23 +19,23 @@ namespace SharpChat.Commands { lock(ctx.Chat.UsersAccess) if(string.IsNullOrWhiteSpace(unsilUserStr) || (unsilUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(unsilUserStr))) == null) { - ctx.User.Send(new LegacyCommandResponse(LCR.USER_NOT_FOUND, true, unsilUserStr ?? "User")); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USER_NOT_FOUND, true, unsilUserStr ?? "User")); return; } if(unsilUser.Rank >= ctx.User.Rank) { - ctx.User.Send(new LegacyCommandResponse(LCR.UNSILENCE_HIERARCHY)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.UNSILENCE_HIERARCHY)); return; } if(!unsilUser.IsSilenced) { - ctx.User.Send(new LegacyCommandResponse(LCR.NOT_SILENCED)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.NOT_SILENCED)); return; } unsilUser.SilencedUntil = DateTimeOffset.MinValue; - unsilUser.Send(new LegacyCommandResponse(LCR.UNSILENCED, false)); - ctx.User.Send(new LegacyCommandResponse(LCR.TARGET_UNSILENCED, false, unsilUser.DisplayName)); + ctx.Chat.SendTo(unsilUser, new LegacyCommandResponse(LCR.UNSILENCED, false)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.TARGET_UNSILENCED, false, unsilUser.DisplayName)); } } } diff --git a/SharpChat/Commands/WhisperCommand.cs b/SharpChat/Commands/WhisperCommand.cs index e55d347..7c63ddc 100644 --- a/SharpChat/Commands/WhisperCommand.cs +++ b/SharpChat/Commands/WhisperCommand.cs @@ -12,7 +12,7 @@ namespace SharpChat.Commands { public void Dispatch(ChatCommandContext ctx) { if(ctx.Args.Length < 2) { - ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR)); return; } @@ -22,7 +22,7 @@ namespace SharpChat.Commands { whisperUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(whisperUserStr)); if(whisperUser == null) { - ctx.User.Send(new LegacyCommandResponse(LCR.USER_NOT_FOUND, true, whisperUserStr)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USER_NOT_FOUND, true, whisperUserStr)); return; } if(whisperUser == ctx.User) @@ -30,7 +30,7 @@ namespace SharpChat.Commands { string whisperStr = string.Join(' ', ctx.Args.Skip(1)); - whisperUser.Send(new ChatMessageAddPacket(new ChatMessage { + ctx.Chat.SendTo(whisperUser, new ChatMessageAddPacket(new ChatMessage { DateTime = DateTimeOffset.Now, Target = whisperUser, TargetName = whisperUser.TargetName, @@ -38,7 +38,7 @@ namespace SharpChat.Commands { Text = whisperStr, Flags = ChatMessageFlags.Private, })); - ctx.User.Send(new ChatMessageAddPacket(new ChatMessage { + ctx.Chat.SendTo(ctx.User, new ChatMessageAddPacket(new ChatMessage { DateTime = DateTimeOffset.Now, Target = whisperUser, TargetName = whisperUser.TargetName, diff --git a/SharpChat/Commands/WhoCommand.cs b/SharpChat/Commands/WhoCommand.cs index 4f5e20c..950a31a 100644 --- a/SharpChat/Commands/WhoCommand.cs +++ b/SharpChat/Commands/WhoCommand.cs @@ -28,19 +28,19 @@ namespace SharpChat.Commands { if(whoChanSB.Length > 2) whoChanSB.Length -= 2; - ctx.User.Send(new LegacyCommandResponse(LCR.USERS_LISTING_SERVER, false, whoChanSB)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USERS_LISTING_SERVER, false, whoChanSB)); } else { ChatChannel whoChan; lock(ctx.Chat.ChannelsAccess) whoChan = ctx.Chat.Channels.FirstOrDefault(c => c.NameEquals(whoChanStr)); if(whoChan == null) { - ctx.User.Send(new LegacyCommandResponse(LCR.CHANNEL_NOT_FOUND, true, whoChanStr)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.CHANNEL_NOT_FOUND, true, whoChanStr)); return; } if(whoChan.Rank > ctx.User.Rank || (whoChan.HasPassword && !ctx.User.Can(ChatUserPermissions.JoinAnyChannel))) { - ctx.User.Send(new LegacyCommandResponse(LCR.USERS_LISTING_ERROR, true, whoChanStr)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USERS_LISTING_ERROR, true, whoChanStr)); return; } @@ -58,7 +58,7 @@ namespace SharpChat.Commands { if(whoChanSB.Length > 2) whoChanSB.Length -= 2; - ctx.User.Send(new LegacyCommandResponse(LCR.USERS_LISTING_CHANNEL, false, whoChan.Name, whoChanSB)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USERS_LISTING_CHANNEL, false, whoChan.Name, whoChanSB)); } } } diff --git a/SharpChat/IPacketTarget.cs b/SharpChat/IPacketTarget.cs index 50e442e..af0d666 100644 --- a/SharpChat/IPacketTarget.cs +++ b/SharpChat/IPacketTarget.cs @@ -1,6 +1,5 @@ namespace SharpChat { public interface IPacketTarget { string TargetName { get; } - void Send(IServerPacket packet); } } diff --git a/SharpChat/PacketHandlers/AuthHandler.cs b/SharpChat/PacketHandlers/AuthHandler.cs index 58e5e59..ca9011f 100644 --- a/SharpChat/PacketHandlers/AuthHandler.cs +++ b/SharpChat/PacketHandlers/AuthHandler.cs @@ -99,28 +99,27 @@ namespace SharpChat.PacketHandlers { } lock(ctx.Chat.UsersAccess) { - ChatUser aUser = ctx.Chat.Users.FirstOrDefault(u => u.UserId == fai.UserId); + ChatUser user = ctx.Chat.Users.FirstOrDefault(u => u.UserId == fai.UserId); - if(aUser == null) - aUser = new ChatUser(fai); + if(user == null) + user = new ChatUser(fai); else { - aUser.ApplyAuth(fai); - aUser.Channel?.Send(new UserUpdatePacket(aUser)); + user.ApplyAuth(fai); + if(user.Channel != null) + ctx.Chat.SendTo(user.Channel, new UserUpdatePacket(user)); } // Enforce a maximum amount of connections per user - if(aUser.ConnectionCount >= MaxConnections) { - ctx.Connection.Send(new AuthFailPacket(AuthFailReason.MaxSessions)); - ctx.Connection.Dispose(); - return; - } + lock(ctx.Chat.ConnectionsAccess) + if(ctx.Chat.Connections.Count(conn => conn.User == user) >= MaxConnections) { + ctx.Connection.Send(new AuthFailPacket(AuthFailReason.MaxSessions)); + ctx.Connection.Dispose(); + return; + } - // Bumping the ping to prevent upgrading ctx.Connection.BumpPing(); - - aUser.AddConnection(ctx.Connection); - - ctx.Connection.Send(new LegacyCommandResponse(LCR.WELCOME, false, $"Welcome to Flashii Chat, {aUser.Username}!")); + ctx.Connection.User = user; + ctx.Connection.Send(new LegacyCommandResponse(LCR.WELCOME, false, $"Welcome to Flashii Chat, {user.Username}!")); if(File.Exists("welcome.txt")) { IEnumerable lines = File.ReadAllLines("welcome.txt").Where(x => !string.IsNullOrWhiteSpace(x)); @@ -130,7 +129,7 @@ namespace SharpChat.PacketHandlers { ctx.Connection.Send(new LegacyCommandResponse(LCR.WELCOME, false, line)); } - ctx.Chat.HandleJoin(aUser, DefaultChannel, ctx.Connection, MaxMessageLength); + ctx.Chat.HandleJoin(user, DefaultChannel, ctx.Connection, MaxMessageLength); } }).Wait(); } diff --git a/SharpChat/PacketHandlers/PingHandler.cs b/SharpChat/PacketHandlers/PingHandler.cs index 49d0f9a..2e16007 100644 --- a/SharpChat/PacketHandlers/PingHandler.cs +++ b/SharpChat/PacketHandlers/PingHandler.cs @@ -1,6 +1,7 @@ using SharpChat.Misuzu; using SharpChat.Packet; using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -32,11 +33,16 @@ namespace SharpChat.PacketHandlers { lock(BumpAccess) { if(LastBump < DateTimeOffset.UtcNow - BumpInterval) { (string, string)[] bumpList; - lock(ctx.Chat.UsersAccess) - bumpList = ctx.Chat.Users - .Where(u => u.HasConnections && u.Status == ChatUserStatus.Online) - .Select(u => (u.UserId.ToString(), u.RemoteAddresses.FirstOrDefault()?.ToString() ?? string.Empty)) + lock(ctx.Chat.UsersAccess) { + IEnumerable filtered = ctx.Chat.Users.Where(u => u.Status == ChatUserStatus.Online); + + lock(ctx.Chat.ConnectionsAccess) + filtered = filtered.Where(u => ctx.Chat.Connections.Any(c => c.User == u)); + + bumpList = filtered + .Select(u => (u.UserId.ToString(), ctx.Chat.GetRemoteAddresses(u).FirstOrDefault()?.ToString() ?? string.Empty)) .ToArray(); + } if(bumpList.Any()) Task.Run(async () => { diff --git a/SharpChat/PacketHandlers/SendMessageHandler.cs b/SharpChat/PacketHandlers/SendMessageHandler.cs index 6842b08..d0f55b8 100644 --- a/SharpChat/PacketHandlers/SendMessageHandler.cs +++ b/SharpChat/PacketHandlers/SendMessageHandler.cs @@ -51,7 +51,7 @@ namespace SharpChat.PacketHandlers { if(user.Status != ChatUserStatus.Online) { user.Status = ChatUserStatus.Online; - channel.Send(new UserUpdatePacket(user)); + ctx.Chat.SendTo(channel, new UserUpdatePacket(user)); } int maxMsgLength = MaxMessageLength; @@ -97,7 +97,7 @@ namespace SharpChat.PacketHandlers { lock(ctx.Chat.EventsAccess) { ctx.Chat.Events.AddEvent(message); - channel.Send(new ChatMessageAddPacket(message)); + ctx.Chat.SendTo(channel, new ChatMessageAddPacket(message)); } } } diff --git a/SharpChat/SockChatServer.cs b/SharpChat/SockChatServer.cs index e4c3b64..b87394f 100644 --- a/SharpChat/SockChatServer.cs +++ b/SharpChat/SockChatServer.cs @@ -143,29 +143,18 @@ namespace SharpChat { private void OnClose(IWebSocketConnection sock) { Logger.Write($"Connection closed from {sock.ConnectionInfo.ClientIpAddress}:{sock.ConnectionInfo.ClientPort}"); - ChatConnection conn; - lock(Context.ConnectionsAccess) - conn = Context.GetConnection(sock); + lock(Context.ConnectionsAccess) { + ChatConnection conn = Context.GetConnection(sock); + if(conn == null) + return; - // Remove connection from user - if(conn?.User != null) { - // RemoveConnection sets conn.User to null so we must grab a local copy. - ChatUser user = conn.User; - - user.RemoveConnection(conn); - - if(!user.HasConnections) - Context.UserLeave(null, user); - } - - // Update context - Context.Update(); - - // Remove connection from server - lock(Context.ConnectionsAccess) Context.Connections.Remove(conn); - conn?.Dispose(); + if(conn.User != null && !Context.Connections.Any(c => c.User == conn.User)) + Context.UserLeave(null, conn.User); + } + + Context.Update(); } private void OnError(IWebSocketConnection sock, Exception ex) { @@ -210,7 +199,7 @@ namespace SharpChat { }).Wait(); return; } else if(conn.User.RateLimiter.State == ChatRateLimitState.Warning) - conn.User.Send(new FloodWarningPacket()); + Context.SendTo(conn.User, new FloodWarningPacket()); } ChatPacketHandlerContext context = new(msg, Context, conn);