diff --git a/SharpChat/ChatChannel.cs b/SharpChat/ChatChannel.cs
index c27a046..762936e 100644
--- a/SharpChat/ChatChannel.cs
+++ b/SharpChat/ChatChannel.cs
@@ -12,14 +12,6 @@ namespace SharpChat {
         public bool HasPassword
             => !string.IsNullOrWhiteSpace(Password);
 
-        public ChatChannel(
-            ChatUser owner,
-            string name,
-            string? password = null,
-            bool isTemporary = false,
-            int rank = 0
-        ) : this(name, password, isTemporary, rank, owner?.UserId ?? 0) {}
-
         public ChatChannel(
             string name,
             string? password = null,
@@ -44,10 +36,6 @@ namespace SharpChat {
                 && OwnerId == user.UserId;
         }
 
-        public override int GetHashCode() {
-            return Name.GetHashCode();
-        }
-
         public static bool CheckName(string name) {
             return !string.IsNullOrWhiteSpace(name) && name.All(CheckNameChar);
         }
diff --git a/SharpChat/ChatColour.cs b/SharpChat/ChatColour.cs
index 75b429a..aad0fb4 100644
--- a/SharpChat/ChatColour.cs
+++ b/SharpChat/ChatColour.cs
@@ -21,19 +21,11 @@
             Inherits = false;
         }
 
-        public override bool Equals(object? obj) {
-            return obj is ChatColour colour && Equals(colour);
-        }
-       
         public bool Equals(ChatColour other) {
-            return Red == other.Red
+            return Inherits == other.Inherits
+                && Red == other.Red
                 && Green == other.Green
-                && Blue == other.Blue
-                && Inherits == other.Inherits;
-        }
-
-        public override int GetHashCode() {
-            return ToMisuzu();
+                && Blue == other.Blue;
         }
 
         public override string ToString() {
@@ -65,13 +57,5 @@
                 ? None
                 : FromRawRGB(raw);
         }
-
-        public static bool operator ==(ChatColour left, ChatColour right) {
-            return left.Equals(right);
-        }
-
-        public static bool operator !=(ChatColour left, ChatColour right) {
-            return !(left == right);
-        }
     }
 }
diff --git a/SharpChat/ChatCommandContext.cs b/SharpChat/ChatCommandContext.cs
index 2ac1c37..1a8cefe 100644
--- a/SharpChat/ChatCommandContext.cs
+++ b/SharpChat/ChatCommandContext.cs
@@ -17,35 +17,16 @@ namespace SharpChat {
             ChatConnection connection,
             ChatChannel channel
         ) {
-            if(text == null)
-                throw new ArgumentNullException(nameof(text));
-
-            Chat = chat ?? throw new ArgumentNullException(nameof(chat));
-            User = user ?? throw new ArgumentNullException(nameof(user));
-            Connection = connection ?? throw new ArgumentNullException(nameof(connection));
-            Channel = channel ?? throw new ArgumentNullException(nameof(channel));
+            Chat = chat;
+            User = user;
+            Connection = connection;
+            Channel = channel;
 
             string[] parts = text[1..].Split(' ');
             Name = parts.First().Replace(".", string.Empty);
             Args = parts.Skip(1).ToArray();
         }
 
-        public ChatCommandContext(
-            string name,
-            string[] args,
-            ChatContext chat,
-            ChatUser user,
-            ChatConnection connection,
-            ChatChannel channel
-        ) {
-            Name = name ?? throw new ArgumentNullException(nameof(name));
-            Args = args ?? throw new ArgumentNullException(nameof(args));
-            Chat = chat ?? throw new ArgumentNullException(nameof(chat));
-            User = user ?? throw new ArgumentNullException(nameof(user));
-            Connection = connection ?? throw new ArgumentNullException(nameof(connection));
-            Channel = channel ?? throw new ArgumentNullException(nameof(channel));
-        }
-
         public bool NameEquals(string name) {
             return Name.Equals(name, StringComparison.InvariantCultureIgnoreCase);
         }
diff --git a/SharpChat/ChatConnection.cs b/SharpChat/ChatConnection.cs
index aadf074..f1a9b1b 100644
--- a/SharpChat/ChatConnection.cs
+++ b/SharpChat/ChatConnection.cs
@@ -84,9 +84,5 @@ namespace SharpChat {
         public override string ToString() {
             return Id;
         }
-
-        public override int GetHashCode() {
-            return Id.GetHashCode();
-        }
     }
 }
diff --git a/SharpChat/ChatContext.cs b/SharpChat/ChatContext.cs
index 09b4ec6..d5d93d7 100644
--- a/SharpChat/ChatContext.cs
+++ b/SharpChat/ChatContext.cs
@@ -13,16 +13,16 @@ namespace SharpChat {
 
         public readonly SemaphoreSlim ContextAccess = new(1, 1);
 
-        public HashSet<ChatChannel> Channels { get; } = new();
-        public HashSet<ChatConnection> Connections { get; } = new();
-        public HashSet<ChatUser> Users { get; } = new();
+        public Dictionary<string, ChatChannel> Channels { get; } = new();
+        public List<ChatConnection> Connections { get; } = new();
+        public Dictionary<long, ChatUser> Users { get; } = new();
         public IEventStorage Events { get; }
         public HashSet<ChannelUserAssoc> ChannelUsers { get; } = new();
         public Dictionary<long, RateLimiter> UserRateLimiters { get; } = new();
         public Dictionary<long, ChatChannel> UserLastChannel { get; } = new();
 
         public ChatContext(IEventStorage evtStore) {
-            Events = evtStore ?? throw new ArgumentNullException(nameof(evtStore));
+            Events = evtStore;
         }
 
         public void DispatchEvent(IChatEvent eventInfo) {
@@ -37,11 +37,11 @@ namespace SharpChat {
                     if(mce.ChannelName?.StartsWith("@") != true)
                         return;
 
-                    IEnumerable<long> uids = mce.ChannelName[1..].Split('-', 3).Select(u => long.TryParse(u, out long up) ? up : -1);
-                    if(uids.Count() != 2)
+                    long[] targetIds = mce.ChannelName[1..].Split('-', 3).Select(u => long.TryParse(u, out long up) ? up : -1).ToArray();
+                    if(targetIds.Length != 2)
                         return;
 
-                    IEnumerable<ChatUser> users = Users.Where(u => uids.Any(uid => uid == u.UserId));
+                    ChatUser[] users = Users.Where(kvp => targetIds.Contains(kvp.Key)).Select(kvp => kvp.Value).ToArray();
                     ChatUser? target = users.FirstOrDefault(u => u.UserId != mce.SenderId);
                     if(target == null)
                         return;
@@ -56,7 +56,7 @@ namespace SharpChat {
                             true
                         ));
                 } else {
-                    ChatChannel? channel = Channels.FirstOrDefault(c => c.NameEquals(mce.ChannelName));
+                    ChatChannel? channel = Channels.Values.FirstOrDefault(c => c.NameEquals(mce.ChannelName));
                     if(channel != null)
                         SendTo(channel, new MessageAddPacket(
                             mce.MessageId,
@@ -88,9 +88,11 @@ namespace SharpChat {
                     Logger.Write($"Nuked connection {conn.Id} associated with {conn.User}.");
                 }
 
-            Connections.RemoveWhere(conn => conn.IsDisposed);
+            int removed = Connections.RemoveAll(conn => conn.IsDisposed);
+            if(removed > 0)
+                Logger.Write($"Removed {removed} nuked connections from the list.");
 
-            foreach(ChatUser user in Users)
+            foreach(ChatUser user in Users.Values)
                 if(!Connections.Any(conn => conn.User == user)) {
                     HandleDisconnect(user, ChatUserDisconnectReason.TimeOut);
                     Logger.Write($"Timed out {user} (no more connections).");
@@ -118,7 +120,7 @@ namespace SharpChat {
 
         public ChatChannel[] GetUserChannels(ChatUser user) {
             string[] names = GetUserChannelNames(user);
-            return Channels.Where(c => names.Any(n => c.NameEquals(n))).ToArray();
+            return Channels.Values.Where(c => names.Any(n => c.NameEquals(n))).ToArray();
         }
 
         public long[] GetChannelUserIds(ChatChannel channel) {
@@ -126,8 +128,8 @@ namespace SharpChat {
         }
 
         public ChatUser[] GetChannelUsers(ChatChannel channel) {
-            long[] ids = GetChannelUserIds(channel);
-            return Users.Where(u => ids.Contains(u.UserId)).ToArray();
+            long[] targetIds = GetChannelUserIds(channel);
+            return Users.Values.Where(u => targetIds.Contains(u.UserId)).ToArray();
         }
 
         public void UpdateUser(
@@ -142,9 +144,6 @@ namespace SharpChat {
             bool? isSuper = null,
             bool silent = false
         ) {
-            if(user == null)
-                throw new ArgumentNullException(nameof(user));
-
             bool hasChanged = false;
             string? previousName = null;
 
@@ -161,7 +160,7 @@ namespace SharpChat {
                 hasChanged = true;
             }
 
-            if(colour.HasValue && user.Colour != colour.Value) {
+            if(colour.HasValue && user.Colour.Equals(colour.Value)) {
                 user.Colour = colour.Value;
                 hasChanged = true;
             }
@@ -215,7 +214,7 @@ namespace SharpChat {
             foreach(ChatConnection conn in Connections)
                 if(conn.User == user)
                     conn.Dispose();
-            Connections.RemoveWhere(conn => conn.IsDisposed);
+            Connections.RemoveAll(conn => conn.IsDisposed);
 
             HandleDisconnect(user, reason);
         }
@@ -261,11 +260,11 @@ namespace SharpChat {
 
             HandleChannelEventLog(chan.Name, p => conn.Send(p));
 
-            conn.Send(new ChannelsPopulatePacket(Channels.Where(c => c.Rank <= user.Rank).Select(
+            conn.Send(new ChannelsPopulatePacket(Channels.Values.Where(c => c.Rank <= user.Rank).Select(
                 channel => new ChannelsPopulatePacket.ListEntry(channel.Name, channel.HasPassword, channel.IsTemporary)
             ).ToArray()));
 
-            Users.Add(user);
+            Users.Add(user.UserId, user);
 
             ChannelUsers.Add(new ChannelUserAssoc(user.UserId, chan.Name));
             UserLastChannel[user.UserId] = chan;
@@ -273,7 +272,7 @@ namespace SharpChat {
 
         public void HandleDisconnect(ChatUser user, ChatUserDisconnectReason reason = ChatUserDisconnectReason.Leave) {
             UpdateUser(user, status: ChatUserStatus.Offline);
-            Users.Remove(user);
+            Users.Remove(user.UserId);
             UserLastChannel.Remove(user.UserId);
 
             ChatChannel[] channels = GetUserChannels(user);
@@ -313,7 +312,7 @@ namespace SharpChat {
         }
 
         public void ForceChannelSwitch(ChatUser user, ChatChannel chan) {
-            if(!Channels.Contains(chan))
+            if(!Channels.ContainsValue(chan))
                 return;
 
             ChatChannel oldChan = UserLastChannel[user.UserId];
@@ -340,31 +339,18 @@ namespace SharpChat {
         }
 
         public void Send(IServerPacket packet) {
-            if(packet == null)
-                throw new ArgumentNullException(nameof(packet));
-
             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));
-
             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));
-
             // might be faster to grab the users first and then cascade into that SendTo
             IEnumerable<ChatConnection> conns = Connections.Where(c => c.IsAuthed && IsInChannel(c.User, channel));
             foreach(ChatConnection conn in conns)
@@ -372,12 +358,7 @@ namespace SharpChat {
         }
 
         public void SendToUserChannels(ChatUser user, IServerPacket packet) {
-            if(user == null)
-                throw new ArgumentNullException(nameof(user));
-            if(packet == null)
-                throw new ArgumentNullException(nameof(packet));
-
-            IEnumerable<ChatChannel> chans = Channels.Where(c => IsInChannel(user, c));
+            IEnumerable<ChatChannel> chans = Channels.Values.Where(c => IsInChannel(user, c));
             IEnumerable<ChatConnection> conns = Connections.Where(conn => conn.IsAuthed && ChannelUsers.Any(cu => cu.UserId == conn.User?.UserId && chans.Any(chan => chan.NameEquals(cu.ChannelName))));
             foreach(ChatConnection conn in conns)
                 conn.Send(packet);
@@ -388,9 +369,6 @@ namespace SharpChat {
         }
 
         public void ForceChannel(ChatUser user, ChatChannel? chan = null) {
-            if(user == null)
-                throw new ArgumentNullException(nameof(user));
-
             if(chan == null && !UserLastChannel.TryGetValue(user.UserId, out chan))
                 throw new ArgumentException("no channel???");
 
@@ -403,9 +381,7 @@ namespace SharpChat {
             int? minRank = null,
             string? password = null
         ) {
-            if(channel == null)
-                throw new ArgumentNullException(nameof(channel));
-            if(!Channels.Contains(channel))
+            if(!Channels.ContainsValue(channel))
                 throw new ArgumentException("Provided channel is not registered with this manager.", nameof(channel));
 
             string prevName = channel.Name;
@@ -420,7 +396,7 @@ namespace SharpChat {
                 channel.Password = password;
 
             // TODO: Users that no longer have access to the channel/gained access to the channel by the rank change should receive delete and create packets respectively
-            foreach(ChatUser user in Users.Where(u => u.Rank >= channel.Rank)) {
+            foreach(ChatUser user in Users.Values.Where(u => u.Rank >= channel.Rank)) {
                 SendTo(user, new ChannelUpdatePacket(prevName, channel.Name, channel.HasPassword, channel.IsTemporary));
             }
         }
@@ -429,12 +405,12 @@ namespace SharpChat {
             if(channel == null || !Channels.Any())
                 return;
 
-            ChatChannel? defaultChannel = Channels.FirstOrDefault();
+            ChatChannel? defaultChannel = Channels.Values.FirstOrDefault();
             if(defaultChannel == null)
                 return;
 
             // Remove channel from the listing
-            Channels.Remove(channel);
+            Channels.Remove(channel.Name);
 
             // Move all users back to the main channel
             // TODO: Replace this with a kick. SCv2 supports being in 0 channels, SCv1 should force the user back to DefaultChannel.
@@ -442,7 +418,7 @@ namespace SharpChat {
                 SwitchChannel(user, defaultChannel, string.Empty);
 
             // Broadcast deletion of channel
-            foreach(ChatUser user in Users.Where(u => u.Rank >= channel.Rank))
+            foreach(ChatUser user in Users.Values.Where(u => u.Rank >= channel.Rank))
                 SendTo(user, new ChannelDeletePacket(channel.Name));
         }
     }
diff --git a/SharpChat/ChatPacketHandlerContext.cs b/SharpChat/ChatPacketHandlerContext.cs
index eb8eda3..321fb2f 100644
--- a/SharpChat/ChatPacketHandlerContext.cs
+++ b/SharpChat/ChatPacketHandlerContext.cs
@@ -11,9 +11,9 @@ namespace SharpChat {
             ChatContext chat,
             ChatConnection connection
         ) {
-            Text = text ?? throw new ArgumentNullException(nameof(text));
-            Chat = chat ?? throw new ArgumentNullException(nameof(chat));
-            Connection = connection ?? throw new ArgumentNullException(nameof(connection));
+            Text = text;
+            Chat = chat;
+            Connection = connection;
         }
 
         public bool CheckPacketId(string packetId) {
diff --git a/SharpChat/ChatUser.cs b/SharpChat/ChatUser.cs
index d8c0983..7151825 100644
--- a/SharpChat/ChatUser.cs
+++ b/SharpChat/ChatUser.cs
@@ -2,7 +2,7 @@
 using System.Text;
 
 namespace SharpChat {
-    public class ChatUser : IEquatable<ChatUser> {
+    public class ChatUser {
         public const int DEFAULT_SIZE = 30;
         public const int DEFAULT_MINIMUM_DELAY = 10000;
         public const int DEFAULT_RISKY_OFFSET = 5;
@@ -34,7 +34,7 @@ namespace SharpChat {
 
         public ChatUser(
             long userId,
-            string? userName,
+            string userName,
             ChatColour colour,
             int rank,
             ChatUserPermissions perms,
@@ -44,7 +44,7 @@ namespace SharpChat {
             bool isSuper = false
         ) {
             UserId = userId;
-            UserName = userName ?? throw new ArgumentNullException(nameof(userName));
+            UserName = userName;
             Colour = colour;
             Rank = rank;
             Permissions = perms;
@@ -61,18 +61,6 @@ namespace SharpChat {
                 || string.Equals(name, LegacyNameWithStatus, StringComparison.InvariantCultureIgnoreCase);
         }
 
-        public override int GetHashCode() {
-            return UserId.GetHashCode();
-        }
-
-        public override bool Equals(object? obj) {
-            return Equals(obj as ChatUser);
-        }
-
-        public bool Equals(ChatUser? other) {
-            return UserId == other?.UserId;
-        }
-
         public static string GetDMChannelName(ChatUser user1, ChatUser user2) {
             return user1.UserId < user2.UserId
                 ? $"@{user1.UserId}-{user2.UserId}"
diff --git a/SharpChat/Commands/BanListCommand.cs b/SharpChat/Commands/BanListCommand.cs
index 38ea8cd..66327f3 100644
--- a/SharpChat/Commands/BanListCommand.cs
+++ b/SharpChat/Commands/BanListCommand.cs
@@ -9,7 +9,7 @@ namespace SharpChat.Commands {
         private readonly MisuzuClient Misuzu;
 
         public BanListCommand(MisuzuClient msz) {
-            Misuzu = msz ?? throw new ArgumentNullException(nameof(msz));
+            Misuzu = msz;
         }
 
         public bool IsMatch(ChatCommandContext ctx) {
diff --git a/SharpChat/Commands/ChannelCreateCommand.cs b/SharpChat/Commands/ChannelCreateCommand.cs
index 1cded6d..369a448 100644
--- a/SharpChat/Commands/ChannelCreateCommand.cs
+++ b/SharpChat/Commands/ChannelCreateCommand.cs
@@ -38,19 +38,20 @@ namespace SharpChat.Commands {
                 return;
             }
 
-            if(ctx.Chat.Channels.Any(c => c.NameEquals(createChanName))) {
+            if(ctx.Chat.Channels.Values.Any(c => c.NameEquals(createChanName))) {
                 ctx.Chat.SendTo(ctx.User, new ChannelNameInUseErrorPacket(createChanName));
                 return;
             }
 
             ChatChannel createChan = new(
-                ctx.User, createChanName,
+                createChanName,
                 isTemporary: !ctx.User.Permissions.HasFlag(ChatUserPermissions.SetChannelPermanent),
-                rank: createChanHierarchy
+                rank: createChanHierarchy,
+                ownerId: ctx.User.UserId
             );
 
-            ctx.Chat.Channels.Add(createChan);
-            foreach(ChatUser ccu in ctx.Chat.Users.Where(u => u.Rank >= ctx.Channel.Rank))
+            ctx.Chat.Channels.Add(createChan.Name, createChan);
+            foreach(ChatUser ccu in ctx.Chat.Users.Values.Where(u => u.Rank >= ctx.Channel.Rank))
                 ctx.Chat.SendTo(ccu, new ChannelCreatePacket(
                     ctx.Channel.Name,
                     ctx.Channel.HasPassword,
diff --git a/SharpChat/Commands/ChannelDeleteCommand.cs b/SharpChat/Commands/ChannelDeleteCommand.cs
index f1094bc..09da53d 100644
--- a/SharpChat/Commands/ChannelDeleteCommand.cs
+++ b/SharpChat/Commands/ChannelDeleteCommand.cs
@@ -17,7 +17,7 @@ namespace SharpChat.Commands {
             }
 
             string delChanName = string.Join('_', ctx.Args);
-            ChatChannel? delChan = ctx.Chat.Channels.FirstOrDefault(c => c.NameEquals(delChanName));
+            ChatChannel? delChan = ctx.Chat.Channels.Values.FirstOrDefault(c => c.NameEquals(delChanName));
 
             if(delChan == null) {
                 ctx.Chat.SendTo(ctx.User, new ChannelNotFoundErrorPacket(delChanName));
diff --git a/SharpChat/Commands/ChannelJoinCommand.cs b/SharpChat/Commands/ChannelJoinCommand.cs
index b7e1277..8c2a948 100644
--- a/SharpChat/Commands/ChannelJoinCommand.cs
+++ b/SharpChat/Commands/ChannelJoinCommand.cs
@@ -9,7 +9,7 @@ namespace SharpChat.Commands {
 
         public void Dispatch(ChatCommandContext ctx) {
             string joinChanStr = ctx.Args.FirstOrDefault() ?? string.Empty;
-            ChatChannel? joinChan = ctx.Chat.Channels.FirstOrDefault(c => c.NameEquals(joinChanStr));
+            ChatChannel? joinChan = ctx.Chat.Channels.Values.FirstOrDefault(c => c.NameEquals(joinChanStr));
 
             if(joinChan == null) {
                 ctx.Chat.SendTo(ctx.User, new ChannelNotFoundErrorPacket(joinChanStr));
diff --git a/SharpChat/Commands/KickBanCommand.cs b/SharpChat/Commands/KickBanCommand.cs
index 60e887b..b670299 100644
--- a/SharpChat/Commands/KickBanCommand.cs
+++ b/SharpChat/Commands/KickBanCommand.cs
@@ -9,7 +9,7 @@ namespace SharpChat.Commands {
         private readonly MisuzuClient Misuzu;
 
         public KickBanCommand(MisuzuClient msz) {
-            Misuzu = msz ?? throw new ArgumentNullException(nameof(msz));
+            Misuzu = msz;
         }
 
         public bool IsMatch(ChatCommandContext ctx) {
@@ -30,7 +30,7 @@ namespace SharpChat.Commands {
             int banReasonIndex = 1;
             ChatUser? banUser = null;
 
-            if(string.IsNullOrEmpty(banUserTarget) || (banUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(banUserTarget))) == null) {
+            if(string.IsNullOrEmpty(banUserTarget) || (banUser = ctx.Chat.Users.Values.FirstOrDefault(u => u.NameEquals(banUserTarget))) == null) {
                 ctx.Chat.SendTo(ctx.User, new UserNotFoundErrorPacket(banUserTarget));
                 return;
             }
diff --git a/SharpChat/Commands/MessageWhisperCommand.cs b/SharpChat/Commands/MessageWhisperCommand.cs
index d1e657a..86c3b2e 100644
--- a/SharpChat/Commands/MessageWhisperCommand.cs
+++ b/SharpChat/Commands/MessageWhisperCommand.cs
@@ -17,7 +17,7 @@ namespace SharpChat.Commands {
             }
 
             string whisperUserStr = ctx.Args.FirstOrDefault() ?? string.Empty;
-            ChatUser? whisperUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(whisperUserStr));
+            ChatUser? whisperUser = ctx.Chat.Users.Values.FirstOrDefault(u => u.NameEquals(whisperUserStr));
 
             if(whisperUser == null) {
                 ctx.Chat.SendTo(ctx.User, new UserNotFoundErrorPacket(whisperUserStr));
diff --git a/SharpChat/Commands/PardonAddressCommand.cs b/SharpChat/Commands/PardonAddressCommand.cs
index fb4e87d..8e069f3 100644
--- a/SharpChat/Commands/PardonAddressCommand.cs
+++ b/SharpChat/Commands/PardonAddressCommand.cs
@@ -10,7 +10,7 @@ namespace SharpChat.Commands {
         private readonly MisuzuClient Misuzu;
 
         public PardonAddressCommand(MisuzuClient msz) {
-            Misuzu = msz ?? throw new ArgumentNullException(nameof(msz));
+            Misuzu = msz;
         }
 
         public bool IsMatch(ChatCommandContext ctx) {
diff --git a/SharpChat/Commands/PardonUserCommand.cs b/SharpChat/Commands/PardonUserCommand.cs
index ed861ff..b98b971 100644
--- a/SharpChat/Commands/PardonUserCommand.cs
+++ b/SharpChat/Commands/PardonUserCommand.cs
@@ -9,7 +9,7 @@ namespace SharpChat.Commands {
         private readonly MisuzuClient Misuzu;
 
         public PardonUserCommand(MisuzuClient msz) {
-            Misuzu = msz ?? throw new ArgumentNullException(nameof(msz));
+            Misuzu = msz;
         }
 
         public bool IsMatch(ChatCommandContext ctx) {
@@ -31,10 +31,10 @@ namespace SharpChat.Commands {
                 return;
             }
 
-            ChatUser? unbanUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(unbanUserTarget));
+            ChatUser? unbanUser = ctx.Chat.Users.Values.FirstOrDefault(u => u.NameEquals(unbanUserTarget));
             if(unbanUser == null && long.TryParse(unbanUserTarget, out long unbanUserId)) {
                 unbanUserTargetIsName = false;
-                unbanUser = ctx.Chat.Users.FirstOrDefault(u => u.UserId == unbanUserId);
+                unbanUser = ctx.Chat.Users.Values.FirstOrDefault(u => u.UserId == unbanUserId);
             }
 
             if(unbanUser != null)
diff --git a/SharpChat/Commands/ShutdownRestartCommand.cs b/SharpChat/Commands/ShutdownRestartCommand.cs
index f2746aa..b7bf413 100644
--- a/SharpChat/Commands/ShutdownRestartCommand.cs
+++ b/SharpChat/Commands/ShutdownRestartCommand.cs
@@ -8,8 +8,8 @@ namespace SharpChat.Commands {
         private readonly Func<bool> ShutdownCheck;
 
         public ShutdownRestartCommand(ManualResetEvent waitHandle, Func<bool> shutdownCheck) {
-            WaitHandle = waitHandle ?? throw new ArgumentNullException(nameof(waitHandle));
-            ShutdownCheck = shutdownCheck ?? throw new ArgumentNullException(nameof(shutdownCheck));
+            WaitHandle = waitHandle;
+            ShutdownCheck = shutdownCheck;
         }
 
         public bool IsMatch(ChatCommandContext ctx) {
diff --git a/SharpChat/Commands/UserNickCommand.cs b/SharpChat/Commands/UserNickCommand.cs
index adab7c1..f8de6b5 100644
--- a/SharpChat/Commands/UserNickCommand.cs
+++ b/SharpChat/Commands/UserNickCommand.cs
@@ -19,7 +19,7 @@ namespace SharpChat.Commands {
             int offset = 0;
 
             if(setOthersNick && long.TryParse(ctx.Args.FirstOrDefault(), out long targetUserId) && targetUserId > 0) {
-                targetUser = ctx.Chat.Users.FirstOrDefault(u => u.UserId == targetUserId);
+                targetUser = ctx.Chat.Users.Values.FirstOrDefault(u => u.UserId == targetUserId);
                 ++offset;
             }
 
@@ -42,7 +42,7 @@ namespace SharpChat.Commands {
             else if(string.IsNullOrEmpty(nickStr))
                 nickStr = string.Empty;
 
-            if(!string.IsNullOrWhiteSpace(nickStr) && ctx.Chat.Users.Any(u => u.NameEquals(nickStr))) {
+            if(!string.IsNullOrWhiteSpace(nickStr) && ctx.Chat.Users.Values.Any(u => u.NameEquals(nickStr))) {
                 ctx.Chat.SendTo(ctx.User, new UserNameInUseErrorPacket(nickStr));
                 return;
             }
diff --git a/SharpChat/Commands/WhoCommand.cs b/SharpChat/Commands/WhoCommand.cs
index b184e1a..6ee4ab0 100644
--- a/SharpChat/Commands/WhoCommand.cs
+++ b/SharpChat/Commands/WhoCommand.cs
@@ -12,13 +12,13 @@ namespace SharpChat.Commands {
 
             if(string.IsNullOrEmpty(channelName)) {
                 ctx.Chat.SendTo(ctx.User, new WhoServerResponsePacket(
-                    ctx.Chat.Users.Select(u => u.LegacyName).ToArray(),
+                    ctx.Chat.Users.Values.Select(u => u.LegacyName).ToArray(),
                     ctx.User.LegacyName
                 ));
                 return;
             }
 
-            ChatChannel? channel = ctx.Chat.Channels.FirstOrDefault(c => c.NameEquals(channelName));
+            ChatChannel? channel = ctx.Chat.Channels.Values.FirstOrDefault(c => c.NameEquals(channelName));
 
             if(channel == null) {
                 ctx.Chat.SendTo(ctx.User, new ChannelNotFoundErrorPacket(channelName));
diff --git a/SharpChat/Commands/WhoisCommand.cs b/SharpChat/Commands/WhoisCommand.cs
index 2c6a144..f97db47 100644
--- a/SharpChat/Commands/WhoisCommand.cs
+++ b/SharpChat/Commands/WhoisCommand.cs
@@ -18,7 +18,7 @@ namespace SharpChat.Commands {
             string ipUserStr = ctx.Args.FirstOrDefault() ?? string.Empty;
             ChatUser? ipUser;
 
-            if(string.IsNullOrWhiteSpace(ipUserStr) || (ipUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(ipUserStr))) == null) {
+            if(string.IsNullOrWhiteSpace(ipUserStr) || (ipUser = ctx.Chat.Users.Values.FirstOrDefault(u => u.NameEquals(ipUserStr))) == null) {
                 ctx.Chat.SendTo(ctx.User, new UserNotFoundErrorPacket(ipUserStr));
                 return;
             }
diff --git a/SharpChat/Config/CachedValue.cs b/SharpChat/Config/CachedValue.cs
index 64adc96..b33a0d5 100644
--- a/SharpChat/Config/CachedValue.cs
+++ b/SharpChat/Config/CachedValue.cs
@@ -28,12 +28,13 @@ namespace SharpChat.Config {
         public static implicit operator T?(CachedValue<T> val) => val.Value;
 
         public CachedValue(IConfig config, string name, TimeSpan lifetime, T? fallback) {
-            Config = config ?? throw new ArgumentNullException(nameof(config));
-            Name = name ?? throw new ArgumentNullException(nameof(name));
-            Lifetime = lifetime;
-            Fallback = fallback;
             if(string.IsNullOrWhiteSpace(name))
                 throw new ArgumentException("Name cannot be empty.", nameof(name));
+
+            Config = config;
+            Name = name;
+            Lifetime = lifetime;
+            Fallback = fallback;
         }
 
         public void Refresh() {
diff --git a/SharpChat/Config/ScopedConfig.cs b/SharpChat/Config/ScopedConfig.cs
index 7cc5a53..3350e17 100644
--- a/SharpChat/Config/ScopedConfig.cs
+++ b/SharpChat/Config/ScopedConfig.cs
@@ -6,10 +6,12 @@ namespace SharpChat.Config {
         private string Prefix { get; }
 
         public ScopedConfig(IConfig config, string prefix) {
-            Config = config ?? throw new ArgumentNullException(nameof(config));
-            Prefix = prefix ?? throw new ArgumentNullException(nameof(prefix));
             if(string.IsNullOrWhiteSpace(prefix))
                 throw new ArgumentException("Prefix must exist.", nameof(prefix));
+
+            Config = config;
+            Prefix = prefix;
+
             if(Prefix[^1] != ':')
                 Prefix += ':';
         }
diff --git a/SharpChat/Config/StreamConfig.cs b/SharpChat/Config/StreamConfig.cs
index 653f302..702e661 100644
--- a/SharpChat/Config/StreamConfig.cs
+++ b/SharpChat/Config/StreamConfig.cs
@@ -17,7 +17,7 @@ namespace SharpChat.Config {
             : this(new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Read, FileShare.ReadWrite)) { }
 
         public StreamConfig(Stream stream) {
-            Stream = stream ?? throw new ArgumentNullException(nameof(stream));
+            Stream = stream;
             if(!Stream.CanRead)
                 throw new ArgumentException("Provided stream must be readable.", nameof(stream));
             if(!Stream.CanSeek)
diff --git a/SharpChat/EventStorage/MariaDBEventStorage.cs b/SharpChat/EventStorage/MariaDBEventStorage.cs
index c0e5bd1..7ce4b97 100644
--- a/SharpChat/EventStorage/MariaDBEventStorage.cs
+++ b/SharpChat/EventStorage/MariaDBEventStorage.cs
@@ -1,18 +1,15 @@
 using MySqlConnector;
 using System;
 using System.Collections.Generic;
-using System.Dynamic;
 using System.Text;
 using System.Text.Json;
-using System.Threading.Channels;
 
-namespace SharpChat.EventStorage
-{
+namespace SharpChat.EventStorage {
     public partial class MariaDBEventStorage : IEventStorage {
         private string ConnectionString { get; }
 
         public MariaDBEventStorage(string connString) {
-            ConnectionString = connString ?? throw new ArgumentNullException(nameof(connString));
+            ConnectionString = connString;
         }
 
         public void AddEvent(
@@ -48,9 +45,6 @@ namespace SharpChat.EventStorage
             object? data = null,
             StoredEventFlags flags = StoredEventFlags.None
         ) {
-            if(type == null)
-                throw new ArgumentNullException(nameof(type));
-
             RunCommand(
                 "INSERT INTO `sqc_events` (`event_id`, `event_created`, `event_type`, `event_target`, `event_flags`, `event_data`"
                 + ", `event_sender`, `event_sender_name`, `event_sender_colour`, `event_sender_rank`, `event_sender_nick`, `event_sender_perms`)"
@@ -71,9 +65,6 @@ namespace SharpChat.EventStorage
         }
 
         public long AddEvent(string type, ChatUser user, ChatChannel channel, object? data = null, StoredEventFlags flags = StoredEventFlags.None) {
-            if(type == null)
-                throw new ArgumentNullException(nameof(type));
-
             long id = SharpId.Next();
 
             AddEvent(
@@ -172,8 +163,6 @@ namespace SharpChat.EventStorage
         }
 
         public void RemoveEvent(StoredEventInfo evt) {
-            if(evt == null)
-                throw new ArgumentNullException(nameof(evt));
             RunCommand(
                 "UPDATE IGNORE `sqc_events` SET `event_deleted` = NOW() WHERE `event_id` = @id AND `event_deleted` IS NULL",
                 new MySqlParameter("id", evt.Id)
diff --git a/SharpChat/EventStorage/VirtualEventStorage.cs b/SharpChat/EventStorage/VirtualEventStorage.cs
index c3b61cd..13c9295 100644
--- a/SharpChat/EventStorage/VirtualEventStorage.cs
+++ b/SharpChat/EventStorage/VirtualEventStorage.cs
@@ -40,14 +40,11 @@ namespace SharpChat.EventStorage {
             object? data = null,
             StoredEventFlags flags = StoredEventFlags.None
         ) {
-            if(type == null)
-                throw new ArgumentNullException(nameof(type));
-
             // VES is meant as an emergency fallback but this is something else
             JsonDocument hack = JsonDocument.Parse(data == null ? "{}" : JsonSerializer.Serialize(data));
             Events.Add(id, new(id, type, senderId < 1 ? null : new ChatUser(
                 senderId,
-                senderName,
+                senderName ?? string.Empty,
                 senderColour,
                 senderRank,
                 senderPerms,
@@ -56,9 +53,6 @@ namespace SharpChat.EventStorage {
         }
 
         public long AddEvent(string type, ChatUser user, ChatChannel channel, object? data = null, StoredEventFlags flags = StoredEventFlags.None) {
-            if(type == null)
-                throw new ArgumentNullException(nameof(type));
-
             long id = SharpId.Next();
 
             AddEvent(
diff --git a/SharpChat/Misuzu/MisuzuClient.cs b/SharpChat/Misuzu/MisuzuClient.cs
index 5d1584a..4c50a84 100644
--- a/SharpChat/Misuzu/MisuzuClient.cs
+++ b/SharpChat/Misuzu/MisuzuClient.cs
@@ -34,9 +34,7 @@ namespace SharpChat.Misuzu {
         private CachedValue<string> SecretKey { get; }
 
         public MisuzuClient(HttpClient httpClient, IConfig config) {
-            if(config == null)
-                throw new ArgumentNullException(nameof(config));
-            HttpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
+            HttpClient = httpClient;
 
             BaseURL = config.ReadCached("url", DEFAULT_BASE_URL);
             SecretKey = config.ReadCached("secret", DEFAULT_SECRET_KEY);
@@ -77,8 +75,6 @@ namespace SharpChat.Misuzu {
         }
 
         public async Task BumpUsersOnlineAsync(IEnumerable<(string userId, string ipAddr)> list) {
-            if(list == null)
-                throw new ArgumentNullException(nameof(list));
             if(!list.Any())
                 return;
 
@@ -164,9 +160,6 @@ namespace SharpChat.Misuzu {
         }
 
         public async Task<bool> RevokeBanAsync(MisuzuBanInfo banInfo, BanRevokeKind kind) {
-            if(banInfo == null)
-                throw new ArgumentNullException(nameof(banInfo));
-
             string type = kind switch {
                 BanRevokeKind.UserId => "user",
                 BanRevokeKind.RemoteAddress => "addr",
@@ -208,9 +201,9 @@ namespace SharpChat.Misuzu {
             string reason
         ) {
             if(string.IsNullOrWhiteSpace(targetAddr))
-                throw new ArgumentNullException(nameof(targetAddr));
+                throw new ArgumentException("targetAddr may not be empty", nameof(targetAddr));
             if(string.IsNullOrWhiteSpace(modAddr))
-                throw new ArgumentNullException(nameof(modAddr));
+                throw new ArgumentException("modAddr may not be empty", nameof(modAddr));
             if(duration <= TimeSpan.Zero)
                 return;
 
diff --git a/SharpChat/Packet/MessagePopulatePacket.cs b/SharpChat/Packet/MessagePopulatePacket.cs
index 28e6897..e455d72 100644
--- a/SharpChat/Packet/MessagePopulatePacket.cs
+++ b/SharpChat/Packet/MessagePopulatePacket.cs
@@ -9,7 +9,7 @@ namespace SharpChat.Packet {
         private readonly bool Notify;
 
         public MessagePopulatePacket(StoredEventInfo evt, bool notify = false) {
-            Event = evt ?? throw new ArgumentNullException(nameof(evt));
+            Event = evt;
             Notify = notify;
         }
 
diff --git a/SharpChat/Packet/UserChannelForceJoinPacket.cs b/SharpChat/Packet/UserChannelForceJoinPacket.cs
index d280d14..766186d 100644
--- a/SharpChat/Packet/UserChannelForceJoinPacket.cs
+++ b/SharpChat/Packet/UserChannelForceJoinPacket.cs
@@ -5,7 +5,7 @@ namespace SharpChat.Packet {
         private readonly string ChannelName;
 
         public UserChannelForceJoinPacket(string channelName) {
-            ChannelName = channelName ?? throw new ArgumentNullException(nameof(channelName));
+            ChannelName = channelName;
         }
 
         public override string Pack() {
diff --git a/SharpChat/Packet/UserChannelJoinPacket.cs b/SharpChat/Packet/UserChannelJoinPacket.cs
index df126ab..c7e6c09 100644
--- a/SharpChat/Packet/UserChannelJoinPacket.cs
+++ b/SharpChat/Packet/UserChannelJoinPacket.cs
@@ -16,7 +16,7 @@ namespace SharpChat.Packet {
             ChatUserPermissions userPerms
         ) {
             UserId = userId;
-            UserName = userName ?? throw new ArgumentNullException(nameof(userName));
+            UserName = userName;
             UserColour = userColour;
             UserRank = userRank;
             UserPerms = userPerms;
diff --git a/SharpChat/Packet/UserUpdateNotificationPacket.cs b/SharpChat/Packet/UserUpdateNotificationPacket.cs
index 7c01f7b..2275ddc 100644
--- a/SharpChat/Packet/UserUpdateNotificationPacket.cs
+++ b/SharpChat/Packet/UserUpdateNotificationPacket.cs
@@ -7,8 +7,8 @@ namespace SharpChat.Packet {
         private readonly long Timestamp;
 
         public UserUpdateNotificationPacket(string previousName, string newName) {
-            PreviousName = previousName ?? throw new ArgumentNullException(nameof(previousName));
-            NewName = newName ?? throw new ArgumentNullException(nameof(newName));
+            PreviousName = previousName;
+            NewName = newName;
             Timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
         }
 
diff --git a/SharpChat/Packet/UserUpdatePacket.cs b/SharpChat/Packet/UserUpdatePacket.cs
index f6a8226..ddd2936 100644
--- a/SharpChat/Packet/UserUpdatePacket.cs
+++ b/SharpChat/Packet/UserUpdatePacket.cs
@@ -16,7 +16,7 @@ namespace SharpChat.Packet {
             ChatUserPermissions userPerms
         ) {
             UserId = userId;
-            UserName = userName ?? throw new ArgumentNullException(nameof(userName));
+            UserName = userName;
             UserColour = userColour;
             UserRank = userRank;
             UserPerms = userPerms;
diff --git a/SharpChat/Packet/UsersPopulatePacket.cs b/SharpChat/Packet/UsersPopulatePacket.cs
index 5c0b1a9..57d1af9 100644
--- a/SharpChat/Packet/UsersPopulatePacket.cs
+++ b/SharpChat/Packet/UsersPopulatePacket.cs
@@ -8,7 +8,7 @@ namespace SharpChat.Packet {
         private readonly ListEntry[] Entries;
 
         public UsersPopulatePacket(ListEntry[] entries) {
-            Entries = entries ?? throw new ArgumentNullException(nameof(entries));
+            Entries = entries;
         }
 
         public override string Pack() {
diff --git a/SharpChat/PacketHandlers/AuthHandler.cs b/SharpChat/PacketHandlers/AuthHandler.cs
index e6e5016..999c307 100644
--- a/SharpChat/PacketHandlers/AuthHandler.cs
+++ b/SharpChat/PacketHandlers/AuthHandler.cs
@@ -20,10 +20,10 @@ namespace SharpChat.PacketHandlers {
             CachedValue<int> maxMsgLength,
             CachedValue<int> maxConns
         ) {
-            Misuzu = msz ?? throw new ArgumentNullException(nameof(msz));
-            DefaultChannel = defaultChannel ?? throw new ArgumentNullException(nameof(defaultChannel));
-            MaxMessageLength = maxMsgLength ?? throw new ArgumentNullException(nameof(maxMsgLength));
-            MaxConnections = maxConns ?? throw new ArgumentNullException(nameof(maxConns));
+            Misuzu = msz;
+            DefaultChannel = defaultChannel;
+            MaxMessageLength = maxMsgLength;
+            MaxConnections = maxConns;
         }
 
         public bool IsMatch(ChatPacketHandlerContext ctx) {
@@ -114,12 +114,12 @@ namespace SharpChat.PacketHandlers {
 
                 await ctx.Chat.ContextAccess.WaitAsync();
                 try {
-                    ChatUser? user = ctx.Chat.Users.FirstOrDefault(u => u.UserId == fai.UserId);
+                    ChatUser? user = ctx.Chat.Users.Values.FirstOrDefault(u => u.UserId == fai.UserId);
 
                     if(user == null)
                         user = new ChatUser(
                             fai.UserId,
-                            fai.UserName,
+                            fai.UserName ?? string.Empty,
                             fai.Colour,
                             fai.Rank,
                             fai.Permissions,
diff --git a/SharpChat/PacketHandlers/PingHandler.cs b/SharpChat/PacketHandlers/PingHandler.cs
index 87d04c9..a33bf8b 100644
--- a/SharpChat/PacketHandlers/PingHandler.cs
+++ b/SharpChat/PacketHandlers/PingHandler.cs
@@ -12,7 +12,7 @@ namespace SharpChat.PacketHandlers {
         private DateTimeOffset LastBump = DateTimeOffset.MinValue;
 
         public PingHandler(MisuzuClient msz) {
-            Misuzu = msz ?? throw new ArgumentNullException(nameof(msz));
+            Misuzu = msz;
         }
 
         public bool IsMatch(ChatPacketHandlerContext ctx) {
@@ -31,7 +31,7 @@ namespace SharpChat.PacketHandlers {
             ctx.Chat.ContextAccess.Wait();
             try {
                 if(LastBump < DateTimeOffset.UtcNow - BumpInterval) {
-                    (string, string)[] bumpList = ctx.Chat.Users
+                    (string, string)[] bumpList = ctx.Chat.Users.Values
                         .Where(u => u.Status == ChatUserStatus.Online && ctx.Chat.Connections.Any(c => c.User == u))
                         .Select(u => (u.UserId.ToString(), ctx.Chat.GetRemoteAddresses(u).FirstOrDefault()?.ToString() ?? string.Empty))
                         .ToArray();
diff --git a/SharpChat/PacketHandlers/SendMessageHandler.cs b/SharpChat/PacketHandlers/SendMessageHandler.cs
index 785e2fc..90668e0 100644
--- a/SharpChat/PacketHandlers/SendMessageHandler.cs
+++ b/SharpChat/PacketHandlers/SendMessageHandler.cs
@@ -15,15 +15,15 @@ namespace SharpChat.PacketHandlers
         private List<IChatCommand> Commands { get; } = new();
 
         public SendMessageHandler(CachedValue<int> maxMsgLength) {
-            MaxMessageLength = maxMsgLength ?? throw new ArgumentNullException(nameof(maxMsgLength));
+            MaxMessageLength = maxMsgLength;
         }
 
         public void AddCommand(IChatCommand command) {
-            Commands.Add(command ?? throw new ArgumentNullException(nameof(command)));
+            Commands.Add(command);
         }
 
         public void AddCommands(IEnumerable<IChatCommand> commands) {
-            Commands.AddRange(commands ?? throw new ArgumentNullException(nameof(commands)));
+            Commands.AddRange(commands);
         }
 
         public bool IsMatch(ChatPacketHandlerContext ctx) {
diff --git a/SharpChat/SockChatServer.cs b/SharpChat/SockChatServer.cs
index bbf640e..24e5709 100644
--- a/SharpChat/SockChatServer.cs
+++ b/SharpChat/SockChatServer.cs
@@ -41,8 +41,8 @@ namespace SharpChat {
         public SockChatServer(HttpClient httpClient, MisuzuClient msz, IEventStorage evtStore, IConfig config) {
             Logger.Write("Initialising Sock Chat server...");
 
-            HttpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
-            Misuzu = msz ?? throw new ArgumentNullException(nameof(msz));
+            HttpClient = httpClient;
+            Misuzu = msz;
 
             MaxMessageLength = config.ReadCached("msgMaxLength", DEFAULT_MSG_LENGTH_MAX);
             MaxConnections = config.ReadCached("connMaxCount", DEFAULT_MAX_CONNECTIONS);
@@ -67,13 +67,13 @@ namespace SharpChat {
                         rank: channelCfg.SafeReadValue("minRank", 0)
                     );
 
-                    Context.Channels.Add(channelInfo);
+                    Context.Channels.Add(channelInfo.Name, channelInfo);
                     DefaultChannel ??= channelInfo;
                 }
 
             DefaultChannel ??= new ChatChannel("Default");
-            if(!Context.Channels.Any())
-                Context.Channels.Add(DefaultChannel);
+            if(Context.Channels.Count < 1)
+                Context.Channels.Add(DefaultChannel.Name, DefaultChannel);
 
             GuestHandlers.Add(new AuthHandler(Misuzu, DefaultChannel, MaxMessageLength, MaxConnections));