diff --git a/SharpChat/IChatPacketHandler.cs b/SharpChat/C2SPacketHandler.cs
similarity index 76%
rename from SharpChat/IChatPacketHandler.cs
rename to SharpChat/C2SPacketHandler.cs
index 043964b..13f478d 100644
--- a/SharpChat/IChatPacketHandler.cs
+++ b/SharpChat/C2SPacketHandler.cs
@@ -1,5 +1,5 @@
 namespace SharpChat {
-    public interface IChatPacketHandler {
+    public interface C2SPacketHandler {
         bool IsMatch(ChatPacketHandlerContext ctx);
         void Handle(ChatPacketHandlerContext ctx);
     }
diff --git a/SharpChat/PacketHandlers/AuthHandler.cs b/SharpChat/C2SPacketHandlers/AuthC2SPacketHandler.cs
similarity index 82%
rename from SharpChat/PacketHandlers/AuthHandler.cs
rename to SharpChat/C2SPacketHandlers/AuthC2SPacketHandler.cs
index 5eea7eb..d899e09 100644
--- a/SharpChat/PacketHandlers/AuthHandler.cs
+++ b/SharpChat/C2SPacketHandlers/AuthC2SPacketHandler.cs
@@ -1,14 +1,14 @@
 using SharpChat.Config;
 using SharpChat.Misuzu;
-using SharpChat.Packet;
+using SharpChat.S2CPackets;
 
-namespace SharpChat.PacketHandlers {
-    public class AuthHandler(
+namespace SharpChat.C2SPacketHandlers {
+    public class AuthC2SPacketHandler(
         MisuzuClient msz,
         ChatChannel defaultChannel,
         CachedValue<int> maxMsgLength,
         CachedValue<int> maxConns
-    ) : IChatPacketHandler {
+    ) : C2SPacketHandler {
         private readonly MisuzuClient Misuzu = msz ?? throw new ArgumentNullException(nameof(msz));
         private readonly ChatChannel DefaultChannel = defaultChannel ?? throw new ArgumentNullException(nameof(defaultChannel));
         private readonly CachedValue<int> MaxMessageLength = maxMsgLength ?? throw new ArgumentNullException(nameof(maxMsgLength));
@@ -23,14 +23,14 @@ namespace SharpChat.PacketHandlers {
 
             string? authMethod = args.ElementAtOrDefault(1);
             if(string.IsNullOrWhiteSpace(authMethod)) {
-                ctx.Connection.Send(new AuthFailPacket(AuthFailReason.AuthInvalid));
+                ctx.Connection.Send(new AuthFailS2CPacket(AuthFailReason.AuthInvalid));
                 ctx.Connection.Dispose();
                 return;
             }
 
             string? authToken = args.ElementAtOrDefault(2);
             if(string.IsNullOrWhiteSpace(authToken)) {
-                ctx.Connection.Send(new AuthFailPacket(AuthFailReason.AuthInvalid));
+                ctx.Connection.Send(new AuthFailS2CPacket(AuthFailReason.AuthInvalid));
                 ctx.Connection.Dispose();
                 return;
             }
@@ -49,7 +49,7 @@ namespace SharpChat.PacketHandlers {
                     fai = await Misuzu.AuthVerifyAsync(authMethod, authToken, ipAddr);
                 } catch(Exception ex) {
                     Logger.Write($"<{ctx.Connection.Id}> Failed to authenticate: {ex}");
-                    ctx.Connection.Send(new AuthFailPacket(AuthFailReason.AuthInvalid));
+                    ctx.Connection.Send(new AuthFailS2CPacket(AuthFailReason.AuthInvalid));
                     ctx.Connection.Dispose();
 #if DEBUG
                     throw;
@@ -60,7 +60,7 @@ namespace SharpChat.PacketHandlers {
 
                 if(fai?.Success != true) {
                     Logger.Debug($"<{ctx.Connection.Id}> Auth fail: {fai?.Reason ?? "unknown"}");
-                    ctx.Connection.Send(new AuthFailPacket(AuthFailReason.AuthInvalid));
+                    ctx.Connection.Send(new AuthFailS2CPacket(AuthFailReason.AuthInvalid));
                     ctx.Connection.Dispose();
                     return;
                 }
@@ -70,7 +70,7 @@ namespace SharpChat.PacketHandlers {
                     fbi = await Misuzu.CheckBanAsync(fai.UserId.ToString(), ipAddr);
                 } catch(Exception ex) {
                     Logger.Write($"<{ctx.Connection.Id}> Failed auth ban check: {ex}");
-                    ctx.Connection.Send(new AuthFailPacket(AuthFailReason.AuthInvalid));
+                    ctx.Connection.Send(new AuthFailS2CPacket(AuthFailReason.AuthInvalid));
                     ctx.Connection.Dispose();
 #if DEBUG
                     throw;
@@ -81,7 +81,7 @@ namespace SharpChat.PacketHandlers {
 
                 if(fbi?.IsBanned == true && !fbi.HasExpired) {
                     Logger.Write($"<{ctx.Connection.Id}> User is banned.");
-                    ctx.Connection.Send(new AuthFailPacket(AuthFailReason.Banned, fbi));
+                    ctx.Connection.Send(new AuthFailS2CPacket(AuthFailReason.Banned, fbi));
                     ctx.Connection.Dispose();
                     return;
                 }
@@ -109,21 +109,21 @@ namespace SharpChat.PacketHandlers {
 
                     // Enforce a maximum amount of connections per user
                     if(ctx.Chat.Connections.Count(conn => conn.User == user) >= MaxConnections) {
-                        ctx.Connection.Send(new AuthFailPacket(AuthFailReason.MaxSessions));
+                        ctx.Connection.Send(new AuthFailS2CPacket(AuthFailReason.MaxSessions));
                         ctx.Connection.Dispose();
                         return;
                     }
 
                     ctx.Connection.BumpPing();
                     ctx.Connection.User = user;
-                    ctx.Connection.Send(new LegacyCommandResponse(0, LCR.WELCOME, false, $"Welcome to Flashii Chat, {user.UserName}!"));
+                    ctx.Connection.Send(new CommandResponseS2CPacket(0, LCR.WELCOME, false, $"Welcome to Flashii Chat, {user.UserName}!"));
 
                     if(File.Exists("welcome.txt")) {
                         IEnumerable<string> lines = File.ReadAllLines("welcome.txt").Where(x => !string.IsNullOrWhiteSpace(x));
                         string? line = lines.ElementAtOrDefault(RNG.Next(lines.Count()));
 
                         if(!string.IsNullOrWhiteSpace(line))
-                            ctx.Connection.Send(new LegacyCommandResponse(0, LCR.WELCOME, false, line));
+                            ctx.Connection.Send(new CommandResponseS2CPacket(0, LCR.WELCOME, false, line));
                     }
 
                     ctx.Chat.HandleJoin(user, DefaultChannel, ctx.Connection, MaxMessageLength);
diff --git a/SharpChat/PacketHandlers/PingHandler.cs b/SharpChat/C2SPacketHandlers/PingC2SPacketHandler.cs
similarity index 88%
rename from SharpChat/PacketHandlers/PingHandler.cs
rename to SharpChat/C2SPacketHandlers/PingC2SPacketHandler.cs
index 0c0ef30..9f58fdf 100644
--- a/SharpChat/PacketHandlers/PingHandler.cs
+++ b/SharpChat/C2SPacketHandlers/PingC2SPacketHandler.cs
@@ -1,8 +1,8 @@
 using SharpChat.Misuzu;
-using SharpChat.Packet;
+using SharpChat.S2CPackets;
 
-namespace SharpChat.PacketHandlers {
-    public class PingHandler(MisuzuClient msz) : IChatPacketHandler {
+namespace SharpChat.C2SPacketHandlers {
+    public class PingC2SPacketHandler(MisuzuClient msz) : C2SPacketHandler {
         private readonly MisuzuClient Misuzu = msz ?? throw new ArgumentNullException(nameof(msz));
 
         private readonly TimeSpan BumpInterval = TimeSpan.FromMinutes(1);
@@ -19,7 +19,7 @@ namespace SharpChat.PacketHandlers {
                 return;
 
             ctx.Connection.BumpPing();
-            ctx.Connection.Send(new PongPacket());
+            ctx.Connection.Send(new PongS2CPacket());
 
             ctx.Chat.ContextAccess.Wait();
             try {
diff --git a/SharpChat/PacketHandlers/SendMessageHandler.cs b/SharpChat/C2SPacketHandlers/SendMessageC2SPacketHandler.cs
similarity index 96%
rename from SharpChat/PacketHandlers/SendMessageHandler.cs
rename to SharpChat/C2SPacketHandlers/SendMessageC2SPacketHandler.cs
index 48e720b..98e1194 100644
--- a/SharpChat/PacketHandlers/SendMessageHandler.cs
+++ b/SharpChat/C2SPacketHandlers/SendMessageC2SPacketHandler.cs
@@ -4,11 +4,11 @@ using SharpChat.Snowflake;
 using System.Globalization;
 using System.Text;
 
-namespace SharpChat.PacketHandlers {
-    public class SendMessageHandler(
+namespace SharpChat.C2SPacketHandlers {
+    public class SendMessageC2SPacketHandler(
         RandomSnowflake randomSnowflake,
         CachedValue<int> maxMsgLength
-    ) : IChatPacketHandler {
+    ) : C2SPacketHandler {
         private readonly CachedValue<int> MaxMessageLength = maxMsgLength ?? throw new ArgumentNullException(nameof(maxMsgLength));
 
         private List<IChatCommand> Commands { get; } = [];
diff --git a/SharpChat/ChatConnection.cs b/SharpChat/ChatConnection.cs
index 5d3d848..8cb5638 100644
--- a/SharpChat/ChatConnection.cs
+++ b/SharpChat/ChatConnection.cs
@@ -41,7 +41,7 @@ namespace SharpChat {
             RemotePort = (ushort)sock.ConnectionInfo.ClientPort;
         }
 
-        public void Send(IServerPacket packet) {
+        public void Send(S2CPacket packet) {
             if(!Socket.IsAvailable)
                 return;
 
diff --git a/SharpChat/ChatContext.cs b/SharpChat/ChatContext.cs
index d296be9..7672d92 100644
--- a/SharpChat/ChatContext.cs
+++ b/SharpChat/ChatContext.cs
@@ -1,6 +1,6 @@
 using SharpChat.Events;
 using SharpChat.EventStorage;
-using SharpChat.Packet;
+using SharpChat.S2CPackets;
 using SharpChat.Snowflake;
 using System.Net;
 
@@ -29,7 +29,7 @@ namespace SharpChat {
         public void DispatchEvent(IChatEvent eventInfo) {
             if(eventInfo is MessageCreateEvent mce) {
                 if(mce.IsBroadcast) {
-                    Send(new LegacyCommandResponse(RandomSnowflake.Next(), LCR.BROADCAST, false, mce.MessageText));
+                    Send(new CommandResponseS2CPacket(RandomSnowflake.Next(), LCR.BROADCAST, false, mce.MessageText));
                 } else if(mce.IsPrivate) {
                     // The channel name returned by GetDMChannelName should not be exposed to the user, instead @<Target User> should be displayed
                     // e.g. nook sees @Arysil and Arysil sees @nook
@@ -48,7 +48,7 @@ namespace SharpChat {
                         return;
 
                     foreach(ChatUser user in users)
-                        SendTo(user, new ChatMessageAddPacket(
+                        SendTo(user, new ChatMessageAddS2CPacket(
                             mce.MessageId,
                             DateTimeOffset.Now,
                             mce.SenderId,
@@ -59,7 +59,7 @@ namespace SharpChat {
                 } else {
                     ChatChannel? channel = Channels.FirstOrDefault(c => c.NameEquals(mce.ChannelName));
                     if(channel is not null)
-                        SendTo(channel, new ChatMessageAddPacket(
+                        SendTo(channel, new ChatMessageAddS2CPacket(
                             mce.MessageId,
                             DateTimeOffset.Now,
                             mce.SenderId,
@@ -190,15 +190,15 @@ namespace SharpChat {
             }
 
             if(hasChanged)
-                SendToUserChannels(user, new UserUpdatePacket(RandomSnowflake.Next(), user, previousName));
+                SendToUserChannels(user, new UserUpdateS2CPacket(RandomSnowflake.Next(), user, previousName));
         }
 
         public void BanUser(ChatUser user, TimeSpan duration, UserDisconnectReason reason = UserDisconnectReason.Kicked) {
             if(duration > TimeSpan.Zero) {
                 DateTimeOffset expires = duration >= TimeSpan.MaxValue ? DateTimeOffset.MaxValue : DateTimeOffset.Now + duration;
-                SendTo(user, new ForceDisconnectPacket(ForceDisconnectReason.Banned, expires));
+                SendTo(user, new ForceDisconnectS2CPacket(ForceDisconnectReason.Banned, expires));
             } else
-                SendTo(user, new ForceDisconnectPacket(ForceDisconnectReason.Kicked));
+                SendTo(user, new ForceDisconnectS2CPacket(ForceDisconnectReason.Kicked));
 
             foreach(ChatConnection conn in Connections)
                 if(conn.User == user)
@@ -211,17 +211,17 @@ namespace SharpChat {
         public void HandleJoin(ChatUser user, ChatChannel chan, ChatConnection conn, int maxMsgLength) {
             if(!IsInChannel(user, chan)) {
                 long msgId = RandomSnowflake.Next();
-                SendTo(chan, new UserConnectPacket(msgId, DateTimeOffset.Now, user));
+                SendTo(chan, new UserConnectS2CPacket(msgId, DateTimeOffset.Now, user));
                 Events.AddEvent(msgId, "user:connect", chan.Name, user.UserId, user.UserName, user.Colour, user.Rank, user.NickName, user.Permissions, null, StoredEventFlags.Log);
             }
 
-            conn.Send(new AuthSuccessPacket(user, chan, maxMsgLength));
-            conn.Send(new ContextUsersPacket(GetChannelUsers(chan).Except([user]).OrderByDescending(u => u.Rank)));
+            conn.Send(new AuthSuccessS2CPacket(user, chan, maxMsgLength));
+            conn.Send(new ContextUsersS2CPacket(GetChannelUsers(chan).Except([user]).OrderByDescending(u => u.Rank)));
 
             foreach(StoredEventInfo msg in Events.GetChannelEventLog(chan.Name))
-                conn.Send(new ContextMessagePacket(msg));
+                conn.Send(new ContextMessageS2CPacket(msg));
 
-            conn.Send(new ContextChannelsPacket(Channels.Where(c => c.Rank <= user.Rank)));
+            conn.Send(new ContextChannelsS2CPacket(Channels.Where(c => c.Rank <= user.Rank)));
 
             Users.Add(user);
 
@@ -240,7 +240,7 @@ namespace SharpChat {
                 ChannelUsers.Remove(new ChannelUserAssoc(user.UserId, chan.Name));
 
                 long msgId = RandomSnowflake.Next();
-                SendTo(chan, new UserDisconnectPacket(msgId, DateTimeOffset.Now, user, reason));
+                SendTo(chan, new UserDisconnectS2CPacket(msgId, DateTimeOffset.Now, user, reason));
                 Events.AddEvent(msgId, "user:disconnect", chan.Name, user.UserId, user.UserName, user.Colour, user.Rank, user.NickName, user.Permissions, new { reason = (int)reason }, StoredEventFlags.Log);
 
                 if(chan.IsTemporary && chan.IsOwner(user))
@@ -256,13 +256,13 @@ namespace SharpChat {
 
             if(!user.Can(ChatUserPermissions.JoinAnyChannel) && chan.IsOwner(user)) {
                 if(chan.Rank > user.Rank) {
-                    SendTo(user, new LegacyCommandResponse(RandomSnowflake.Next(), LCR.CHANNEL_INSUFFICIENT_HIERARCHY, true, chan.Name));
+                    SendTo(user, new CommandResponseS2CPacket(RandomSnowflake.Next(), LCR.CHANNEL_INSUFFICIENT_HIERARCHY, true, chan.Name));
                     ForceChannel(user);
                     return;
                 }
 
                 if(!string.IsNullOrEmpty(chan.Password) && chan.Password != password) {
-                    SendTo(user, new LegacyCommandResponse(RandomSnowflake.Next(), LCR.CHANNEL_INVALID_PASSWORD, true, chan.Name));
+                    SendTo(user, new CommandResponseS2CPacket(RandomSnowflake.Next(), LCR.CHANNEL_INVALID_PASSWORD, true, chan.Name));
                     ForceChannel(user);
                     return;
                 }
@@ -278,18 +278,18 @@ namespace SharpChat {
             ChatChannel oldChan = UserLastChannel[user.UserId];
 
             long leaveId = RandomSnowflake.Next();
-            SendTo(oldChan, new UserChannelLeavePacket(leaveId, user));
+            SendTo(oldChan, new UserChannelLeaveS2CPacket(leaveId, user));
             Events.AddEvent(leaveId, "chan:leave", oldChan.Name, user.UserId, user.UserName, user.Colour, user.Rank, user.NickName, user.Permissions, null, StoredEventFlags.Log);
 
             long joinId = RandomSnowflake.Next();
-            SendTo(chan, new UserChannelJoinPacket(joinId, user));
+            SendTo(chan, new UserChannelJoinS2CPacket(joinId, user));
             Events.AddEvent(joinId, "chan:join", chan.Name, user.UserId, user.UserName, user.Colour, user.Rank, user.NickName, user.Permissions, null, StoredEventFlags.Log);
 
-            SendTo(user, new ContextClearPacket(ContextClearMode.MessagesUsers));
-            SendTo(user, new ContextUsersPacket(GetChannelUsers(chan).Except([user]).OrderByDescending(u => u.Rank)));
+            SendTo(user, new ContextClearS2CPacket(ContextClearMode.MessagesUsers));
+            SendTo(user, new ContextUsersS2CPacket(GetChannelUsers(chan).Except([user]).OrderByDescending(u => u.Rank)));
 
             foreach(StoredEventInfo msg in Events.GetChannelEventLog(chan.Name))
-                SendTo(user, new ContextMessagePacket(msg));
+                SendTo(user, new ContextMessageS2CPacket(msg));
 
             ForceChannel(user, chan);
 
@@ -301,7 +301,7 @@ namespace SharpChat {
                 RemoveChannel(oldChan);
         }
 
-        public void Send(IServerPacket packet) {
+        public void Send(S2CPacket packet) {
             ArgumentNullException.ThrowIfNull(packet);
 
             foreach(ChatConnection conn in Connections)
@@ -309,7 +309,7 @@ namespace SharpChat {
                     conn.Send(packet);
         }
 
-        public void SendTo(ChatUser user, IServerPacket packet) {
+        public void SendTo(ChatUser user, S2CPacket packet) {
             ArgumentNullException.ThrowIfNull(user);
             ArgumentNullException.ThrowIfNull(packet);
 
@@ -318,7 +318,7 @@ namespace SharpChat {
                     conn.Send(packet);
         }
 
-        public void SendTo(ChatChannel channel, IServerPacket packet) {
+        public void SendTo(ChatChannel channel, S2CPacket packet) {
             ArgumentNullException.ThrowIfNull(channel);
             ArgumentNullException.ThrowIfNull(packet);
 
@@ -328,7 +328,7 @@ namespace SharpChat {
                 conn.Send(packet);
         }
 
-        public void SendToUserChannels(ChatUser user, IServerPacket packet) {
+        public void SendToUserChannels(ChatUser user, S2CPacket packet) {
             ArgumentNullException.ThrowIfNull(user);
             ArgumentNullException.ThrowIfNull(packet);
 
@@ -348,7 +348,7 @@ namespace SharpChat {
             if(chan == null && !UserLastChannel.TryGetValue(user.UserId, out chan))
                 throw new ArgumentException("no channel???");
 
-            SendTo(user, new UserChannelForceJoinPacket(chan));
+            SendTo(user, new UserChannelForceJoinS2CPacket(chan));
         }
 
         public void UpdateChannel(ChatChannel channel, bool? temporary = null, int? hierarchy = null, string? password = null) {
@@ -367,7 +367,7 @@ namespace SharpChat {
 
             // TODO: 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
             foreach(ChatUser user in Users.Where(u => u.Rank >= channel.Rank)) {
-                SendTo(user, new ChannelUpdatePacket(channel.Name, channel));
+                SendTo(user, new ChannelUpdateS2CPacket(channel.Name, channel));
             }
         }
 
@@ -389,7 +389,7 @@ namespace SharpChat {
 
             // Broadcast deletion of channel
             foreach(ChatUser user in Users.Where(u => u.Rank >= channel.Rank))
-                SendTo(user, new ChannelDeletePacket(channel));
+                SendTo(user, new ChannelDeleteS2CPacket(channel));
         }
     }
 }
diff --git a/SharpChat/Commands/BanListCommand.cs b/SharpChat/Commands/BanListCommand.cs
index fc0485b..8e674db 100644
--- a/SharpChat/Commands/BanListCommand.cs
+++ b/SharpChat/Commands/BanListCommand.cs
@@ -1,5 +1,5 @@
 using SharpChat.Misuzu;
-using SharpChat.Packet;
+using SharpChat.S2CPackets;
 
 namespace SharpChat.Commands {
     public class BanListCommand(MisuzuClient msz) : IChatCommand {
@@ -14,7 +14,7 @@ namespace SharpChat.Commands {
             long msgId = ctx.Chat.RandomSnowflake.Next();
 
             if(!ctx.User.Can(ChatUserPermissions.BanUser | ChatUserPermissions.KickUser)) {
-                ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
+                ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
                 return;
             }
 
@@ -22,9 +22,9 @@ namespace SharpChat.Commands {
                 MisuzuBanInfo[]? mbi = await Misuzu.GetBanListAsync();
 
                 if(mbi is null)
-                    ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.GENERIC_ERROR, true));
+                    ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.GENERIC_ERROR, true));
                 else
-                    ctx.Chat.SendTo(ctx.User, new BanListPacket(msgId, mbi));
+                    ctx.Chat.SendTo(ctx.User, new BanListS2CPacket(msgId, mbi));
             }).Wait();
         }
     }
diff --git a/SharpChat/Commands/BroadcastCommand.cs b/SharpChat/Commands/BroadcastCommand.cs
index 2161096..13c04a0 100644
--- a/SharpChat/Commands/BroadcastCommand.cs
+++ b/SharpChat/Commands/BroadcastCommand.cs
@@ -1,5 +1,5 @@
 using SharpChat.Events;
-using SharpChat.Packet;
+using SharpChat.S2CPackets;
 
 namespace SharpChat.Commands {
     public class BroadcastCommand : IChatCommand {
@@ -12,7 +12,7 @@ namespace SharpChat.Commands {
             long msgId = ctx.Chat.RandomSnowflake.Next();
 
             if(!ctx.User.Can(ChatUserPermissions.Broadcast)) {
-                ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
+                ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
                 return;
             }
 
diff --git a/SharpChat/Commands/CreateChannelCommand.cs b/SharpChat/Commands/CreateChannelCommand.cs
index 26c7a8a..c487bab 100644
--- a/SharpChat/Commands/CreateChannelCommand.cs
+++ b/SharpChat/Commands/CreateChannelCommand.cs
@@ -1,4 +1,4 @@
-using SharpChat.Packet;
+using SharpChat.S2CPackets;
 
 namespace SharpChat.Commands {
     public class CreateChannelCommand : IChatCommand {
@@ -10,7 +10,7 @@ namespace SharpChat.Commands {
             long msgId = ctx.Chat.RandomSnowflake.Next();
 
             if(!ctx.User.Can(ChatUserPermissions.CreateChannel)) {
-                ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
+                ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
                 return;
             }
 
@@ -18,7 +18,7 @@ namespace SharpChat.Commands {
 
             bool createChanHasHierarchy;
             if(ctx.Args.Length < 1 || (createChanHasHierarchy = firstArg.All(char.IsDigit) && ctx.Args.Length < 2)) {
-                ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.COMMAND_FORMAT_ERROR));
+                ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_FORMAT_ERROR));
                 return;
             }
 
@@ -28,19 +28,19 @@ namespace SharpChat.Commands {
                     createChanHierarchy = 0;
 
             if(createChanHierarchy > ctx.User.Rank) {
-                ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.INSUFFICIENT_HIERARCHY));
+                ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.INSUFFICIENT_HIERARCHY));
                 return;
             }
 
             string createChanName = string.Join('_', ctx.Args.Skip(createChanHasHierarchy ? 1 : 0));
 
             if(!ChatChannel.CheckName(createChanName)) {
-                ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.CHANNEL_NAME_INVALID));
+                ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_NAME_INVALID));
                 return;
             }
 
             if(ctx.Chat.Channels.Any(c => c.NameEquals(createChanName))) {
-                ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.CHANNEL_ALREADY_EXISTS, true, createChanName));
+                ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_ALREADY_EXISTS, true, createChanName));
                 return;
             }
 
@@ -53,10 +53,10 @@ namespace SharpChat.Commands {
 
             ctx.Chat.Channels.Add(createChan);
             foreach(ChatUser ccu in ctx.Chat.Users.Where(u => u.Rank >= ctx.Channel.Rank))
-                ctx.Chat.SendTo(ccu, new ChannelCreatePacket(ctx.Channel));
+                ctx.Chat.SendTo(ccu, new ChannelCreateS2CPacket(ctx.Channel));
 
             ctx.Chat.SwitchChannel(ctx.User, createChan, createChan.Password);
-            ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.CHANNEL_CREATED, false, createChan.Name));
+            ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_CREATED, false, createChan.Name));
         }
     }
 }
diff --git a/SharpChat/Commands/DeleteChannelCommand.cs b/SharpChat/Commands/DeleteChannelCommand.cs
index 012cfe5..ecc0009 100644
--- a/SharpChat/Commands/DeleteChannelCommand.cs
+++ b/SharpChat/Commands/DeleteChannelCommand.cs
@@ -1,4 +1,4 @@
-using SharpChat.Packet;
+using SharpChat.S2CPackets;
 
 namespace SharpChat.Commands {
     public class DeleteChannelCommand : IChatCommand {
@@ -13,7 +13,7 @@ namespace SharpChat.Commands {
             long msgId = ctx.Chat.RandomSnowflake.Next();
 
             if(ctx.Args.Length < 1 || string.IsNullOrWhiteSpace(ctx.Args.FirstOrDefault())) {
-                ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.COMMAND_FORMAT_ERROR));
+                ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_FORMAT_ERROR));
                 return;
             }
 
@@ -21,17 +21,17 @@ namespace SharpChat.Commands {
             ChatChannel? delChan = ctx.Chat.Channels.FirstOrDefault(c => c.NameEquals(delChanName));
 
             if(delChan == null) {
-                ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.CHANNEL_NOT_FOUND, true, delChanName));
+                ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_NOT_FOUND, true, delChanName));
                 return;
             }
 
             if(!ctx.User.Can(ChatUserPermissions.DeleteChannel) && delChan.IsOwner(ctx.User)) {
-                ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.CHANNEL_DELETE_FAILED, true, delChan.Name));
+                ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_DELETE_FAILED, true, delChan.Name));
                 return;
             }
 
             ctx.Chat.RemoveChannel(delChan);
-            ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.CHANNEL_DELETED, false, delChan.Name));
+            ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_DELETED, false, delChan.Name));
         }
     }
 }
diff --git a/SharpChat/Commands/DeleteMessageCommand.cs b/SharpChat/Commands/DeleteMessageCommand.cs
index 3f07460..ceb3927 100644
--- a/SharpChat/Commands/DeleteMessageCommand.cs
+++ b/SharpChat/Commands/DeleteMessageCommand.cs
@@ -1,5 +1,5 @@
 using SharpChat.EventStorage;
-using SharpChat.Packet;
+using SharpChat.S2CPackets;
 
 namespace SharpChat.Commands
 {
@@ -16,26 +16,26 @@ namespace SharpChat.Commands
             bool deleteAnyMessage = ctx.User.Can(ChatUserPermissions.DeleteAnyMessage);
 
             if(!deleteAnyMessage && !ctx.User.Can(ChatUserPermissions.DeleteOwnMessage)) {
-                ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
+                ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, 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.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.COMMAND_FORMAT_ERROR));
+                ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_FORMAT_ERROR));
                 return;
             }
 
             StoredEventInfo? delMsg = ctx.Chat.Events.GetEvent(delSeqId);
 
             if(delMsg?.Sender is null || delMsg.Sender.Rank > ctx.User.Rank || (!deleteAnyMessage && delMsg.Sender.UserId != ctx.User.UserId)) {
-                ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.MESSAGE_DELETE_ERROR));
+                ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.MESSAGE_DELETE_ERROR));
                 return;
             }
 
             ctx.Chat.Events.RemoveEvent(delMsg);
-            ctx.Chat.Send(new ChatMessageDeletePacket(delMsg.Id));
+            ctx.Chat.Send(new ChatMessageDeleteS2CPacket(delMsg.Id));
         }
     }
 }
diff --git a/SharpChat/Commands/JoinChannelCommand.cs b/SharpChat/Commands/JoinChannelCommand.cs
index 1c2a5cc..a00298c 100644
--- a/SharpChat/Commands/JoinChannelCommand.cs
+++ b/SharpChat/Commands/JoinChannelCommand.cs
@@ -1,4 +1,4 @@
-using SharpChat.Packet;
+using SharpChat.S2CPackets;
 
 namespace SharpChat.Commands {
     public class JoinChannelCommand : IChatCommand {
@@ -12,7 +12,7 @@ namespace SharpChat.Commands {
             ChatChannel? joinChan = ctx.Chat.Channels.FirstOrDefault(c => c.NameEquals(joinChanStr));
 
             if(joinChan is null) {
-                ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.CHANNEL_NOT_FOUND, true, joinChanStr));
+                ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, 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 076e637..2482321 100644
--- a/SharpChat/Commands/KickBanCommand.cs
+++ b/SharpChat/Commands/KickBanCommand.cs
@@ -1,5 +1,5 @@
 using SharpChat.Misuzu;
-using SharpChat.Packet;
+using SharpChat.S2CPackets;
 
 namespace SharpChat.Commands {
     public class KickBanCommand(MisuzuClient msz) : IChatCommand {
@@ -15,7 +15,7 @@ namespace SharpChat.Commands {
             long msgId = ctx.Chat.RandomSnowflake.Next();
 
             if(!ctx.User.Can(isBanning ? ChatUserPermissions.BanUser : ChatUserPermissions.KickUser)) {
-                ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
+                ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
                 return;
             }
 
@@ -25,19 +25,19 @@ namespace SharpChat.Commands {
             ChatUser? banUser = null;
 
             if(banUserTarget == null || (banUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(banUserTarget))) == null) {
-                ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.USER_NOT_FOUND, true, banUserTarget ?? "User"));
+                ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_NOT_FOUND, true, banUserTarget ?? "User"));
                 return;
             }
 
             if(!ctx.User.IsSuper && banUser.Rank >= ctx.User.Rank && banUser != ctx.User) {
-                ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.KICK_NOT_ALLOWED, true, banUser.LegacyName));
+                ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.KICK_NOT_ALLOWED, true, banUser.LegacyName));
                 return;
             }
 
             TimeSpan duration = isBanning ? TimeSpan.MaxValue : TimeSpan.Zero;
             if(!string.IsNullOrWhiteSpace(banDurationStr) && double.TryParse(banDurationStr, out double durationSeconds)) {
                 if(durationSeconds < 0) {
-                    ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.COMMAND_FORMAT_ERROR));
+                    ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_FORMAT_ERROR));
                     return;
                 }
 
@@ -59,12 +59,12 @@ namespace SharpChat.Commands {
                 // obviously it makes no sense to only check for one ip address but that's current misuzu limitations
                 MisuzuBanInfo? fbi = await Misuzu.CheckBanAsync(userId, userIp);
                 if(fbi is null) {
-                    ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.GENERIC_ERROR, true));
+                    ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.GENERIC_ERROR, true));
                     return;
                 }
 
                 if(fbi.IsBanned && !fbi.HasExpired) {
-                    ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.KICK_NOT_ALLOWED, true, banUser.LegacyName));
+                    ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.KICK_NOT_ALLOWED, true, banUser.LegacyName));
                     return;
                 }
 
diff --git a/SharpChat/Commands/NickCommand.cs b/SharpChat/Commands/NickCommand.cs
index 195785e..633589c 100644
--- a/SharpChat/Commands/NickCommand.cs
+++ b/SharpChat/Commands/NickCommand.cs
@@ -1,4 +1,4 @@
-using SharpChat.Packet;
+using SharpChat.S2CPackets;
 using System.Globalization;
 using System.Text;
 
@@ -16,7 +16,7 @@ namespace SharpChat.Commands {
             bool setOthersNick = ctx.User.Can(ChatUserPermissions.SetOthersNickname);
 
             if(!setOthersNick && !ctx.User.Can(ChatUserPermissions.SetOwnNickname)) {
-                ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
+                ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
                 return;
             }
 
@@ -31,7 +31,7 @@ namespace SharpChat.Commands {
             targetUser ??= ctx.User;
 
             if(ctx.Args.Length < offset) {
-                ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.COMMAND_FORMAT_ERROR));
+                ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_FORMAT_ERROR));
                 return;
             }
 
@@ -52,7 +52,7 @@ namespace SharpChat.Commands {
             }
 
             if(!string.IsNullOrWhiteSpace(nickStr) && ctx.Chat.Users.Any(u => u.NameEquals(nickStr))) {
-                ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.NAME_IN_USE, true, nickStr));
+                ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.NAME_IN_USE, true, nickStr));
                 return;
             }
 
diff --git a/SharpChat/Commands/PardonAddressCommand.cs b/SharpChat/Commands/PardonAddressCommand.cs
index c8024d3..1e0729a 100644
--- a/SharpChat/Commands/PardonAddressCommand.cs
+++ b/SharpChat/Commands/PardonAddressCommand.cs
@@ -1,5 +1,5 @@
 using SharpChat.Misuzu;
-using SharpChat.Packet;
+using SharpChat.S2CPackets;
 using System.Net;
 
 namespace SharpChat.Commands {
@@ -15,13 +15,13 @@ namespace SharpChat.Commands {
             long msgId = ctx.Chat.RandomSnowflake.Next();
 
             if(!ctx.User.Can(ChatUserPermissions.BanUser | ChatUserPermissions.KickUser)) {
-                ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
+                ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
                 return;
             }
 
             string? unbanAddrTarget = ctx.Args.FirstOrDefault();
             if(string.IsNullOrWhiteSpace(unbanAddrTarget) || !IPAddress.TryParse(unbanAddrTarget, out IPAddress? unbanAddr)) {
-                ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.COMMAND_FORMAT_ERROR));
+                ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_FORMAT_ERROR));
                 return;
             }
 
@@ -30,20 +30,20 @@ namespace SharpChat.Commands {
             Task.Run(async () => {
                 MisuzuBanInfo? banInfo = await Misuzu.CheckBanAsync(ipAddr: unbanAddrTarget);
                 if(banInfo is null) {
-                    ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.GENERIC_ERROR, true));
+                    ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.GENERIC_ERROR, true));
                     return;
                 }
 
                 if(!banInfo.IsBanned || banInfo.HasExpired) {
-                    ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.USER_NOT_BANNED, true, unbanAddrTarget));
+                    ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_NOT_BANNED, true, unbanAddrTarget));
                     return;
                 }
 
                 bool wasBanned = await Misuzu.RevokeBanAsync(banInfo, MisuzuClient.BanRevokeKind.RemoteAddress);
                 if(wasBanned)
-                    ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.USER_UNBANNED, false, unbanAddrTarget));
+                    ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_UNBANNED, false, unbanAddrTarget));
                 else
-                    ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.USER_NOT_BANNED, true, unbanAddrTarget));
+                    ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_NOT_BANNED, true, unbanAddrTarget));
             }).Wait();
         }
     }
diff --git a/SharpChat/Commands/PardonUserCommand.cs b/SharpChat/Commands/PardonUserCommand.cs
index e972600..0b31f8f 100644
--- a/SharpChat/Commands/PardonUserCommand.cs
+++ b/SharpChat/Commands/PardonUserCommand.cs
@@ -1,5 +1,5 @@
 using SharpChat.Misuzu;
-using SharpChat.Packet;
+using SharpChat.S2CPackets;
 
 namespace SharpChat.Commands {
     public class PardonUserCommand(MisuzuClient msz) : IChatCommand {
@@ -14,14 +14,14 @@ namespace SharpChat.Commands {
             long msgId = ctx.Chat.RandomSnowflake.Next();
 
             if(!ctx.User.Can(ChatUserPermissions.BanUser | ChatUserPermissions.KickUser)) {
-                ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
+                ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
                 return;
             }
 
             bool unbanUserTargetIsName = true;
             string? unbanUserTarget = ctx.Args.FirstOrDefault();
             if(string.IsNullOrWhiteSpace(unbanUserTarget)) {
-                ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.COMMAND_FORMAT_ERROR));
+                ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_FORMAT_ERROR));
                 return;
             }
 
@@ -37,20 +37,20 @@ namespace SharpChat.Commands {
             Task.Run(async () => {
                 MisuzuBanInfo? banInfo = await Misuzu.CheckBanAsync(unbanUserTarget, userIdIsName: unbanUserTargetIsName);
                 if(banInfo is null) {
-                    ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.GENERIC_ERROR, true));
+                    ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.GENERIC_ERROR, true));
                     return;
                 }
 
                 if(!banInfo.IsBanned || banInfo.HasExpired) {
-                    ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.USER_NOT_BANNED, true, unbanUserTarget));
+                    ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_NOT_BANNED, true, unbanUserTarget));
                     return;
                 }
 
                 bool wasBanned = await Misuzu.RevokeBanAsync(banInfo, MisuzuClient.BanRevokeKind.UserId);
                 if(wasBanned)
-                    ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.USER_UNBANNED, false, unbanUserTarget));
+                    ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_UNBANNED, false, unbanUserTarget));
                 else
-                    ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.USER_NOT_BANNED, true, unbanUserTarget));
+                    ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_NOT_BANNED, true, unbanUserTarget));
             }).Wait();
         }
     }
diff --git a/SharpChat/Commands/PasswordChannelCommand.cs b/SharpChat/Commands/PasswordChannelCommand.cs
index 45c054c..cbe8785 100644
--- a/SharpChat/Commands/PasswordChannelCommand.cs
+++ b/SharpChat/Commands/PasswordChannelCommand.cs
@@ -1,4 +1,4 @@
-using SharpChat.Packet;
+using SharpChat.S2CPackets;
 
 namespace SharpChat.Commands {
     public class PasswordChannelCommand : IChatCommand {
@@ -11,7 +11,7 @@ namespace SharpChat.Commands {
             long msgId = ctx.Chat.RandomSnowflake.Next();
 
             if(!ctx.User.Can(ChatUserPermissions.SetChannelPassword) || ctx.Channel.IsOwner(ctx.User)) {
-                ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
+                ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
                 return;
             }
 
@@ -21,7 +21,7 @@ namespace SharpChat.Commands {
                 chanPass = string.Empty;
 
             ctx.Chat.UpdateChannel(ctx.Channel, password: chanPass);
-            ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.CHANNEL_PASSWORD_CHANGED, false));
+            ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_PASSWORD_CHANGED, false));
         }
     }
 }
diff --git a/SharpChat/Commands/RankChannelCommand.cs b/SharpChat/Commands/RankChannelCommand.cs
index c07ffc4..8a665dc 100644
--- a/SharpChat/Commands/RankChannelCommand.cs
+++ b/SharpChat/Commands/RankChannelCommand.cs
@@ -1,4 +1,4 @@
-using SharpChat.Packet;
+using SharpChat.S2CPackets;
 
 namespace SharpChat.Commands {
     public class RankChannelCommand : IChatCommand {
@@ -12,17 +12,17 @@ namespace SharpChat.Commands {
             long msgId = ctx.Chat.RandomSnowflake.Next();
 
             if(!ctx.User.Can(ChatUserPermissions.SetChannelHierarchy) || ctx.Channel.IsOwner(ctx.User)) {
-                ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
+                ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
                 return;
             }
 
             if(ctx.Args.Length < 1 || !int.TryParse(ctx.Args.First(), out int chanHierarchy) || chanHierarchy > ctx.User.Rank) {
-                ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.INSUFFICIENT_HIERARCHY));
+                ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.INSUFFICIENT_HIERARCHY));
                 return;
             }
 
             ctx.Chat.UpdateChannel(ctx.Channel, hierarchy: chanHierarchy);
-            ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.CHANNEL_HIERARCHY_CHANGED, false));
+            ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_HIERARCHY_CHANGED, false));
         }
     }
 }
diff --git a/SharpChat/Commands/RemoteAddressCommand.cs b/SharpChat/Commands/RemoteAddressCommand.cs
index 65d7c6f..b85f0d0 100644
--- a/SharpChat/Commands/RemoteAddressCommand.cs
+++ b/SharpChat/Commands/RemoteAddressCommand.cs
@@ -1,4 +1,4 @@
-using SharpChat.Packet;
+using SharpChat.S2CPackets;
 using System.Net;
 
 namespace SharpChat.Commands {
@@ -12,7 +12,7 @@ namespace SharpChat.Commands {
             long msgId = ctx.Chat.RandomSnowflake.Next();
 
             if(!ctx.User.Can(ChatUserPermissions.SeeIPAddress)) {
-                ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.COMMAND_NOT_ALLOWED, true, "/ip"));
+                ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, "/ip"));
                 return;
             }
 
@@ -20,12 +20,12 @@ namespace SharpChat.Commands {
             ChatUser? ipUser = null;
 
             if(string.IsNullOrWhiteSpace(ipUserStr) || (ipUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(ipUserStr))) == null) {
-                ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.USER_NOT_FOUND, true, ipUserStr ?? "User"));
+                ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_NOT_FOUND, true, ipUserStr ?? "User"));
                 return;
             }
 
             foreach(IPAddress ip in ctx.Chat.GetRemoteAddresses(ipUser))
-                ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.IP_ADDRESS, false, ipUser.UserName, ip));
+                ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.IP_ADDRESS, false, ipUser.UserName, ip));
         }
     }
 }
diff --git a/SharpChat/Commands/ShutdownRestartCommand.cs b/SharpChat/Commands/ShutdownRestartCommand.cs
index d47accf..aa6a780 100644
--- a/SharpChat/Commands/ShutdownRestartCommand.cs
+++ b/SharpChat/Commands/ShutdownRestartCommand.cs
@@ -1,4 +1,4 @@
-using SharpChat.Packet;
+using SharpChat.S2CPackets;
 
 namespace SharpChat.Commands {
     public class ShutdownRestartCommand(ManualResetEvent waitHandle, Func<bool> shutdownCheck) : IChatCommand {
@@ -13,7 +13,7 @@ namespace SharpChat.Commands {
         public void Dispatch(ChatCommandContext ctx) {
             if(ctx.User.UserId != 1) {
                 long msgId = ctx.Chat.RandomSnowflake.Next();
-                ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
+                ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
                 return;
             }
 
diff --git a/SharpChat/Commands/WhisperCommand.cs b/SharpChat/Commands/WhisperCommand.cs
index 6cca1d1..7210dcf 100644
--- a/SharpChat/Commands/WhisperCommand.cs
+++ b/SharpChat/Commands/WhisperCommand.cs
@@ -1,5 +1,5 @@
 using SharpChat.Events;
-using SharpChat.Packet;
+using SharpChat.S2CPackets;
 
 namespace SharpChat.Commands {
     public class WhisperCommand : IChatCommand {
@@ -12,7 +12,7 @@ namespace SharpChat.Commands {
             long msgId = ctx.Chat.RandomSnowflake.Next();
 
             if(ctx.Args.Length < 2) {
-                ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.COMMAND_FORMAT_ERROR));
+                ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_FORMAT_ERROR));
                 return;
             }
 
@@ -20,7 +20,7 @@ namespace SharpChat.Commands {
             ChatUser? whisperUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(whisperUserStr));
 
             if(whisperUser == null) {
-                ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.USER_NOT_FOUND, true, whisperUserStr));
+                ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_NOT_FOUND, true, whisperUserStr));
                 return;
             }
 
diff --git a/SharpChat/Commands/WhoCommand.cs b/SharpChat/Commands/WhoCommand.cs
index 95adff2..d9a15a6 100644
--- a/SharpChat/Commands/WhoCommand.cs
+++ b/SharpChat/Commands/WhoCommand.cs
@@ -1,4 +1,4 @@
-using SharpChat.Packet;
+using SharpChat.S2CPackets;
 using System.Text;
 
 namespace SharpChat.Commands {
@@ -27,17 +27,17 @@ namespace SharpChat.Commands {
                 if(whoChanSB.Length > 2)
                     whoChanSB.Length -= 2;
 
-                ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.USERS_LISTING_SERVER, false, whoChanSB));
+                ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USERS_LISTING_SERVER, false, whoChanSB));
             } else {
                 ChatChannel? whoChan = ctx.Chat.Channels.FirstOrDefault(c => c.NameEquals(whoChanStr));
 
                 if(whoChan is null) {
-                    ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.CHANNEL_NOT_FOUND, true, whoChanStr));
+                    ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_NOT_FOUND, true, whoChanStr));
                     return;
                 }
 
                 if(whoChan.Rank > ctx.User.Rank || (whoChan.HasPassword && !ctx.User.Can(ChatUserPermissions.JoinAnyChannel))) {
-                    ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.USERS_LISTING_ERROR, true, whoChanStr));
+                    ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USERS_LISTING_ERROR, true, whoChanStr));
                     return;
                 }
 
@@ -55,7 +55,7 @@ namespace SharpChat.Commands {
                 if(whoChanSB.Length > 2)
                     whoChanSB.Length -= 2;
 
-                ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(msgId, LCR.USERS_LISTING_CHANNEL, false, whoChan.Name, whoChanSB));
+                ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USERS_LISTING_CHANNEL, false, whoChan.Name, whoChanSB));
             }
         }
     }
diff --git a/SharpChat/IServerPacket.cs b/SharpChat/S2CPacket.cs
similarity index 65%
rename from SharpChat/IServerPacket.cs
rename to SharpChat/S2CPacket.cs
index d659af2..995c3f0 100644
--- a/SharpChat/IServerPacket.cs
+++ b/SharpChat/S2CPacket.cs
@@ -1,5 +1,5 @@
 namespace SharpChat {
-    public interface IServerPacket {
+    public interface S2CPacket {
         IEnumerable<string> Pack();
     }
 }
diff --git a/SharpChat/Packet/AuthFailPacket.cs b/SharpChat/S2CPackets/AuthFailS2CPacket.cs
similarity index 88%
rename from SharpChat/Packet/AuthFailPacket.cs
rename to SharpChat/S2CPackets/AuthFailS2CPacket.cs
index 7b6a736..41baf30 100644
--- a/SharpChat/Packet/AuthFailPacket.cs
+++ b/SharpChat/S2CPackets/AuthFailS2CPacket.cs
@@ -1,18 +1,18 @@
 using SharpChat.Misuzu;
 using System.Text;
 
-namespace SharpChat.Packet {
+namespace SharpChat.S2CPackets {
     public enum AuthFailReason {
         AuthInvalid,
         MaxSessions,
         Banned,
     }
 
-    public class AuthFailPacket : IServerPacket {
+    public class AuthFailS2CPacket : S2CPacket {
         public AuthFailReason Reason { get; private set; }
         public MisuzuBanInfo? BanInfo { get; private set; }
 
-        public AuthFailPacket(AuthFailReason reason, MisuzuBanInfo? fbi = null) {
+        public AuthFailS2CPacket(AuthFailReason reason, MisuzuBanInfo? fbi = null) {
             Reason = reason;
 
             if(reason == AuthFailReason.Banned)
diff --git a/SharpChat/Packet/AuthSuccessPacket.cs b/SharpChat/S2CPackets/AuthSuccessS2CPacket.cs
similarity index 88%
rename from SharpChat/Packet/AuthSuccessPacket.cs
rename to SharpChat/S2CPackets/AuthSuccessS2CPacket.cs
index c3bfb40..6ed5959 100644
--- a/SharpChat/Packet/AuthSuccessPacket.cs
+++ b/SharpChat/S2CPackets/AuthSuccessS2CPacket.cs
@@ -1,11 +1,11 @@
 using System.Text;
 
-namespace SharpChat.Packet {
-    public class AuthSuccessPacket(
+namespace SharpChat.S2CPackets {
+    public class AuthSuccessS2CPacket(
         ChatUser user,
         ChatChannel channel,
         int maxMsgLength
-    ) : IServerPacket {
+    ) : S2CPacket {
         public ChatUser User { get; private set; } = user ?? throw new ArgumentNullException(nameof(user));
         public ChatChannel Channel { get; private set; } = channel ?? throw new ArgumentNullException(nameof(channel));
 
diff --git a/SharpChat/Packet/BanListPacket.cs b/SharpChat/S2CPackets/BanListS2CPacket.cs
similarity index 92%
rename from SharpChat/Packet/BanListPacket.cs
rename to SharpChat/S2CPackets/BanListS2CPacket.cs
index 7c47b45..0a2f2f3 100644
--- a/SharpChat/Packet/BanListPacket.cs
+++ b/SharpChat/S2CPackets/BanListS2CPacket.cs
@@ -1,11 +1,11 @@
 using SharpChat.Misuzu;
 using System.Text;
 
-namespace SharpChat.Packet {
-    public class BanListPacket(
+namespace SharpChat.S2CPackets {
+    public class BanListS2CPacket(
         long msgId,
         IEnumerable<MisuzuBanInfo> bans
-    ) : IServerPacket {
+    ) : S2CPacket {
         public IEnumerable<MisuzuBanInfo> Bans { get; private set; } = bans ?? throw new ArgumentNullException(nameof(bans));
 
         public IEnumerable<string> Pack() {
diff --git a/SharpChat/Packet/ChannelCreatePacket.cs b/SharpChat/S2CPackets/ChannelCreateS2CPacket.cs
similarity index 80%
rename from SharpChat/Packet/ChannelCreatePacket.cs
rename to SharpChat/S2CPackets/ChannelCreateS2CPacket.cs
index 9a91f1f..0c4ec65 100644
--- a/SharpChat/Packet/ChannelCreatePacket.cs
+++ b/SharpChat/S2CPackets/ChannelCreateS2CPacket.cs
@@ -1,7 +1,7 @@
 using System.Text;
 
-namespace SharpChat.Packet {
-    public class ChannelCreatePacket(ChatChannel channel) : IServerPacket {
+namespace SharpChat.S2CPackets {
+    public class ChannelCreateS2CPacket(ChatChannel channel) : S2CPacket {
         public ChatChannel Channel { get; private set; } = channel ?? throw new ArgumentNullException(nameof(channel));
 
         public IEnumerable<string> Pack() {
diff --git a/SharpChat/Packet/ChannelDeletePacket.cs b/SharpChat/S2CPackets/ChannelDeleteS2CPacket.cs
similarity index 80%
rename from SharpChat/Packet/ChannelDeletePacket.cs
rename to SharpChat/S2CPackets/ChannelDeleteS2CPacket.cs
index aff6c02..811db87 100644
--- a/SharpChat/Packet/ChannelDeletePacket.cs
+++ b/SharpChat/S2CPackets/ChannelDeleteS2CPacket.cs
@@ -1,7 +1,7 @@
 using System.Text;
 
-namespace SharpChat.Packet {
-    public class ChannelDeletePacket(ChatChannel channel) : IServerPacket {
+namespace SharpChat.S2CPackets {
+    public class ChannelDeleteS2CPacket(ChatChannel channel) : S2CPacket {
         public ChatChannel Channel { get; private set; } = channel ?? throw new ArgumentNullException(nameof(channel));
 
         public IEnumerable<string> Pack() {
diff --git a/SharpChat/Packet/ChannelUpdatePacket.cs b/SharpChat/S2CPackets/ChannelUpdateS2CPacket.cs
similarity index 83%
rename from SharpChat/Packet/ChannelUpdatePacket.cs
rename to SharpChat/S2CPackets/ChannelUpdateS2CPacket.cs
index 9972776..06c8234 100644
--- a/SharpChat/Packet/ChannelUpdatePacket.cs
+++ b/SharpChat/S2CPackets/ChannelUpdateS2CPacket.cs
@@ -1,7 +1,7 @@
 using System.Text;
 
-namespace SharpChat.Packet {
-    public class ChannelUpdatePacket(string previousName, ChatChannel channel) : IServerPacket {
+namespace SharpChat.S2CPackets {
+    public class ChannelUpdateS2CPacket(string previousName, ChatChannel channel) : S2CPacket {
         public string PreviousName { get; private set; } = previousName ?? throw new ArgumentNullException(nameof(previousName));
         public ChatChannel Channel { get; private set; } = channel ?? throw new ArgumentNullException(nameof(channel));
 
diff --git a/SharpChat/Packet/ChatMessageAddPacket.cs b/SharpChat/S2CPackets/ChatMessageAddS2CPacket.cs
similarity index 92%
rename from SharpChat/Packet/ChatMessageAddPacket.cs
rename to SharpChat/S2CPackets/ChatMessageAddS2CPacket.cs
index 452a8c4..88cdfd1 100644
--- a/SharpChat/Packet/ChatMessageAddPacket.cs
+++ b/SharpChat/S2CPackets/ChatMessageAddS2CPacket.cs
@@ -1,14 +1,14 @@
 using System.Text;
 
-namespace SharpChat.Packet {
-    public class ChatMessageAddPacket(
+namespace SharpChat.S2CPackets {
+    public class ChatMessageAddS2CPacket(
         long msgId,
         DateTimeOffset created,
         long userId,
         string text,
         bool isAction,
         bool isPrivate
-    ) : IServerPacket {
+    ) : S2CPacket {
         public string Text { get; } = text ?? throw new ArgumentNullException(nameof(text));
 
         public IEnumerable<string> Pack() {
diff --git a/SharpChat/Packet/ChatMessageDeletePacket.cs b/SharpChat/S2CPackets/ChatMessageDeleteS2CPacket.cs
similarity index 70%
rename from SharpChat/Packet/ChatMessageDeletePacket.cs
rename to SharpChat/S2CPackets/ChatMessageDeleteS2CPacket.cs
index da028bd..8566e24 100644
--- a/SharpChat/Packet/ChatMessageDeletePacket.cs
+++ b/SharpChat/S2CPackets/ChatMessageDeleteS2CPacket.cs
@@ -1,7 +1,7 @@
 using System.Text;
 
-namespace SharpChat.Packet {
-    public class ChatMessageDeletePacket(long eventId) : IServerPacket {
+namespace SharpChat.S2CPackets {
+    public class ChatMessageDeleteS2CPacket(long eventId) : S2CPacket {
         public IEnumerable<string> Pack() {
             StringBuilder sb = new();
 
diff --git a/SharpChat/Packet/LegacyCommandResponse.cs b/SharpChat/S2CPackets/CommandResponseS2CPacket.cs
similarity index 97%
rename from SharpChat/Packet/LegacyCommandResponse.cs
rename to SharpChat/S2CPackets/CommandResponseS2CPacket.cs
index 4049f87..5ddbcad 100644
--- a/SharpChat/Packet/LegacyCommandResponse.cs
+++ b/SharpChat/S2CPackets/CommandResponseS2CPacket.cs
@@ -1,12 +1,12 @@
 using System.Text;
 
-namespace SharpChat.Packet {
-    public class LegacyCommandResponse(
+namespace SharpChat.S2CPackets {
+    public class CommandResponseS2CPacket(
         long msgId,
         string stringId,
         bool isError = true,
         params object[] args
-    ) : IServerPacket {
+    ) : S2CPacket {
         public string StringId { get; private set; } = stringId ?? throw new ArgumentNullException(nameof(stringId));
 
         public IEnumerable<string> Pack() {
diff --git a/SharpChat/Packet/ContextChannelsPacket.cs b/SharpChat/S2CPackets/ContextChannelsS2CPacket.cs
similarity index 83%
rename from SharpChat/Packet/ContextChannelsPacket.cs
rename to SharpChat/S2CPackets/ContextChannelsS2CPacket.cs
index 0f0da26..c969ca5 100644
--- a/SharpChat/Packet/ContextChannelsPacket.cs
+++ b/SharpChat/S2CPackets/ContextChannelsS2CPacket.cs
@@ -1,7 +1,7 @@
 using System.Text;
 
-namespace SharpChat.Packet {
-    public class ContextChannelsPacket(IEnumerable<ChatChannel> channels) : IServerPacket {
+namespace SharpChat.S2CPackets {
+    public class ContextChannelsS2CPacket(IEnumerable<ChatChannel> channels) : S2CPacket {
         public IEnumerable<ChatChannel> Channels { get; private set; } = channels?.Where(c => c != null) ?? throw new ArgumentNullException(nameof(channels));
 
         public IEnumerable<string> Pack() {
diff --git a/SharpChat/Packet/ContextClearPacket.cs b/SharpChat/S2CPackets/ContextClearS2CPacket.cs
similarity index 79%
rename from SharpChat/Packet/ContextClearPacket.cs
rename to SharpChat/S2CPackets/ContextClearS2CPacket.cs
index ee49bae..6ab91be 100644
--- a/SharpChat/Packet/ContextClearPacket.cs
+++ b/SharpChat/S2CPackets/ContextClearS2CPacket.cs
@@ -1,6 +1,6 @@
 using System.Text;
 
-namespace SharpChat.Packet {
+namespace SharpChat.S2CPackets {
     public enum ContextClearMode {
         Messages = 0,
         Users = 1,
@@ -9,7 +9,7 @@ namespace SharpChat.Packet {
         MessagesUsersChannels = 4,
     }
 
-    public class ContextClearPacket(ContextClearMode mode) : IServerPacket {
+    public class ContextClearS2CPacket(ContextClearMode mode) : S2CPacket {
         public IEnumerable<string> Pack() {
             StringBuilder sb = new();
 
diff --git a/SharpChat/Packet/ContextMessagePacket.cs b/SharpChat/S2CPackets/ContextMessageS2CPacket.cs
similarity index 96%
rename from SharpChat/Packet/ContextMessagePacket.cs
rename to SharpChat/S2CPackets/ContextMessageS2CPacket.cs
index 9aebe3f..fe15a08 100644
--- a/SharpChat/Packet/ContextMessagePacket.cs
+++ b/SharpChat/S2CPackets/ContextMessageS2CPacket.cs
@@ -1,9 +1,9 @@
 using SharpChat.EventStorage;
 using System.Text;
 
-namespace SharpChat.Packet
+namespace SharpChat.S2CPackets
 {
-    public class ContextMessagePacket(StoredEventInfo evt, bool notify = false) : IServerPacket {
+    public class ContextMessageS2CPacket(StoredEventInfo evt, bool notify = false) : S2CPacket {
         public StoredEventInfo Event { get; private set; } = evt ?? throw new ArgumentNullException(nameof(evt));
 
         private const string V1_CHATBOT = "-1\tChatBot\tinherit\t\t";
diff --git a/SharpChat/Packet/ContextUsersPacket.cs b/SharpChat/S2CPackets/ContextUsersS2CPacket.cs
similarity index 85%
rename from SharpChat/Packet/ContextUsersPacket.cs
rename to SharpChat/S2CPackets/ContextUsersS2CPacket.cs
index da83803..239f02a 100644
--- a/SharpChat/Packet/ContextUsersPacket.cs
+++ b/SharpChat/S2CPackets/ContextUsersS2CPacket.cs
@@ -1,7 +1,7 @@
 using System.Text;
 
-namespace SharpChat.Packet {
-    public class ContextUsersPacket(IEnumerable<ChatUser> users) : IServerPacket {
+namespace SharpChat.S2CPackets {
+    public class ContextUsersS2CPacket(IEnumerable<ChatUser> users) : S2CPacket {
         public IEnumerable<ChatUser> Users { get; private set; } = users?.Where(u => u != null) ?? throw new ArgumentNullException(nameof(users));
 
         public IEnumerable<string> Pack() {
diff --git a/SharpChat/Packet/ForceDisconnectPacket.cs b/SharpChat/S2CPackets/ForceDisconnectS2CPacket.cs
similarity index 83%
rename from SharpChat/Packet/ForceDisconnectPacket.cs
rename to SharpChat/S2CPackets/ForceDisconnectS2CPacket.cs
index 1e1266d..e4baef7 100644
--- a/SharpChat/Packet/ForceDisconnectPacket.cs
+++ b/SharpChat/S2CPackets/ForceDisconnectS2CPacket.cs
@@ -1,16 +1,16 @@
 using System.Text;
 
-namespace SharpChat.Packet {
+namespace SharpChat.S2CPackets {
     public enum ForceDisconnectReason {
         Kicked = 0,
         Banned = 1,
     }
 
-    public class ForceDisconnectPacket : IServerPacket {
+    public class ForceDisconnectS2CPacket : S2CPacket {
         public ForceDisconnectReason Reason { get; private set; }
         public DateTimeOffset Expires { get; private set; }
 
-        public ForceDisconnectPacket(ForceDisconnectReason reason, DateTimeOffset? expires = null) {
+        public ForceDisconnectS2CPacket(ForceDisconnectReason reason, DateTimeOffset? expires = null) {
             Reason = reason;
 
             if(reason == ForceDisconnectReason.Banned) {
diff --git a/SharpChat/Packet/PongPacket.cs b/SharpChat/S2CPackets/PongS2CPacket.cs
similarity index 54%
rename from SharpChat/Packet/PongPacket.cs
rename to SharpChat/S2CPackets/PongS2CPacket.cs
index 984df7e..04cad15 100644
--- a/SharpChat/Packet/PongPacket.cs
+++ b/SharpChat/S2CPackets/PongS2CPacket.cs
@@ -1,5 +1,5 @@
-namespace SharpChat.Packet {
-    public class PongPacket : IServerPacket {
+namespace SharpChat.S2CPackets {
+    public class PongS2CPacket : S2CPacket {
         public IEnumerable<string> Pack() {
             yield return "0\tpong";
         }
diff --git a/SharpChat/Packet/UserChannelForceJoinPacket.cs b/SharpChat/S2CPackets/UserChannelForceJoinS2CPacket.cs
similarity index 79%
rename from SharpChat/Packet/UserChannelForceJoinPacket.cs
rename to SharpChat/S2CPackets/UserChannelForceJoinS2CPacket.cs
index 614c62c..81010b8 100644
--- a/SharpChat/Packet/UserChannelForceJoinPacket.cs
+++ b/SharpChat/S2CPackets/UserChannelForceJoinS2CPacket.cs
@@ -1,7 +1,7 @@
 using System.Text;
 
-namespace SharpChat.Packet {
-    public class UserChannelForceJoinPacket(ChatChannel channel) : IServerPacket {
+namespace SharpChat.S2CPackets {
+    public class UserChannelForceJoinS2CPacket(ChatChannel channel) : S2CPacket {
         public ChatChannel Channel { get; private set; } = channel ?? throw new ArgumentNullException(nameof(channel));
 
         public IEnumerable<string> Pack() {
diff --git a/SharpChat/Packet/UserChannelJoinPacket.cs b/SharpChat/S2CPackets/UserChannelJoinS2CPacket.cs
similarity index 80%
rename from SharpChat/Packet/UserChannelJoinPacket.cs
rename to SharpChat/S2CPackets/UserChannelJoinS2CPacket.cs
index b88841d..14705f7 100644
--- a/SharpChat/Packet/UserChannelJoinPacket.cs
+++ b/SharpChat/S2CPackets/UserChannelJoinS2CPacket.cs
@@ -1,7 +1,7 @@
 using System.Text;
 
-namespace SharpChat.Packet {
-    public class UserChannelJoinPacket(long msgId, ChatUser user) : IServerPacket {
+namespace SharpChat.S2CPackets {
+    public class UserChannelJoinS2CPacket(long msgId, ChatUser user) : S2CPacket {
         public ChatUser User { get; private set; } = user ?? throw new ArgumentNullException(nameof(user));
 
         public IEnumerable<string> Pack() {
diff --git a/SharpChat/Packet/UserChannelLeavePacket.cs b/SharpChat/S2CPackets/UserChannelLeaveS2CPacket.cs
similarity index 80%
rename from SharpChat/Packet/UserChannelLeavePacket.cs
rename to SharpChat/S2CPackets/UserChannelLeaveS2CPacket.cs
index 1b8568a..5766625 100644
--- a/SharpChat/Packet/UserChannelLeavePacket.cs
+++ b/SharpChat/S2CPackets/UserChannelLeaveS2CPacket.cs
@@ -1,7 +1,7 @@
 using System.Text;
 
-namespace SharpChat.Packet {
-    public class UserChannelLeavePacket(long msgId, ChatUser user) : IServerPacket {
+namespace SharpChat.S2CPackets {
+    public class UserChannelLeaveS2CPacket(long msgId, ChatUser user) : S2CPacket {
         public ChatUser User { get; private set; } = user ?? throw new ArgumentNullException(nameof(user));
 
         public IEnumerable<string> Pack() {
diff --git a/SharpChat/Packet/UserConnectPacket.cs b/SharpChat/S2CPackets/UserConnectS2CPacket.cs
similarity index 78%
rename from SharpChat/Packet/UserConnectPacket.cs
rename to SharpChat/S2CPackets/UserConnectS2CPacket.cs
index 300a2e9..64d5321 100644
--- a/SharpChat/Packet/UserConnectPacket.cs
+++ b/SharpChat/S2CPackets/UserConnectS2CPacket.cs
@@ -1,7 +1,7 @@
 using System.Text;
 
-namespace SharpChat.Packet {
-    public class UserConnectPacket(long msgId, DateTimeOffset joined, ChatUser user) : IServerPacket {
+namespace SharpChat.S2CPackets {
+    public class UserConnectS2CPacket(long msgId, DateTimeOffset joined, ChatUser user) : S2CPacket {
         public ChatUser User { get; private set; } = user ?? throw new ArgumentNullException(nameof(user));
 
         public IEnumerable<string> Pack() {
diff --git a/SharpChat/Packet/UserDisconnectPacket.cs b/SharpChat/S2CPackets/UserDisconnectS2CPacket.cs
similarity index 93%
rename from SharpChat/Packet/UserDisconnectPacket.cs
rename to SharpChat/S2CPackets/UserDisconnectS2CPacket.cs
index b67af0c..4adbb2a 100644
--- a/SharpChat/Packet/UserDisconnectPacket.cs
+++ b/SharpChat/S2CPackets/UserDisconnectS2CPacket.cs
@@ -1,6 +1,6 @@
 using System.Text;
 
-namespace SharpChat.Packet {
+namespace SharpChat.S2CPackets {
     public enum UserDisconnectReason {
         Leave,
         TimeOut,
@@ -8,12 +8,12 @@ namespace SharpChat.Packet {
         Flood,
     }
 
-    public class UserDisconnectPacket(
+    public class UserDisconnectS2CPacket(
         long msgId,
         DateTimeOffset disconnected,
         ChatUser user,
         UserDisconnectReason reason
-    ) : IServerPacket {
+    ) : S2CPacket {
         public ChatUser User { get; private set; } = user ?? throw new ArgumentNullException(nameof(user));
 
         public IEnumerable<string> Pack() {
diff --git a/SharpChat/Packet/UserUpdatePacket.cs b/SharpChat/S2CPackets/UserUpdateS2CPacket.cs
similarity index 87%
rename from SharpChat/Packet/UserUpdatePacket.cs
rename to SharpChat/S2CPackets/UserUpdateS2CPacket.cs
index d12f4bb..e761147 100644
--- a/SharpChat/Packet/UserUpdatePacket.cs
+++ b/SharpChat/S2CPackets/UserUpdateS2CPacket.cs
@@ -1,7 +1,7 @@
 using System.Text;
 
-namespace SharpChat.Packet {
-    public class UserUpdatePacket(long msgId, ChatUser user, string previousName = "") : IServerPacket {
+namespace SharpChat.S2CPackets {
+    public class UserUpdateS2CPacket(long msgId, ChatUser user, string previousName = "") : S2CPacket {
         public ChatUser User { get; private set; } = user ?? throw new ArgumentNullException(nameof(user));
 
         public IEnumerable<string> Pack() {
diff --git a/SharpChat/SockChatServer.cs b/SharpChat/SockChatServer.cs
index 427a2aa..c504b53 100644
--- a/SharpChat/SockChatServer.cs
+++ b/SharpChat/SockChatServer.cs
@@ -3,8 +3,8 @@ using SharpChat.Commands;
 using SharpChat.Config;
 using SharpChat.EventStorage;
 using SharpChat.Misuzu;
-using SharpChat.Packet;
-using SharpChat.PacketHandlers;
+using SharpChat.S2CPackets;
+using SharpChat.C2SPacketHandlers;
 
 namespace SharpChat {
     public class SockChatServer : IDisposable {
@@ -25,9 +25,9 @@ namespace SharpChat {
         private readonly CachedValue<int> FloodKickLength;
         private readonly CachedValue<int> FloodKickExemptRank;
 
-        private readonly List<IChatPacketHandler> GuestHandlers = [];
-        private readonly List<IChatPacketHandler> AuthedHandlers = [];
-        private readonly SendMessageHandler SendMessageHandler;
+        private readonly List<C2SPacketHandler> GuestHandlers = [];
+        private readonly List<C2SPacketHandler> AuthedHandlers = [];
+        private readonly SendMessageC2SPacketHandler SendMessageHandler;
 
         private bool IsShuttingDown = false;
 
@@ -70,11 +70,11 @@ namespace SharpChat {
             if(DefaultChannel is null)
                 throw new Exception("The default channel could not be determined.");
 
-            GuestHandlers.Add(new AuthHandler(Misuzu, DefaultChannel, MaxMessageLength, MaxConnections));
+            GuestHandlers.Add(new AuthC2SPacketHandler(Misuzu, DefaultChannel, MaxMessageLength, MaxConnections));
 
             AuthedHandlers.AddRange([
-                new PingHandler(Misuzu),
-                SendMessageHandler = new SendMessageHandler(Context.RandomSnowflake, MaxMessageLength),
+                new PingC2SPacketHandler(Misuzu),
+                SendMessageHandler = new SendMessageC2SPacketHandler(Context.RandomSnowflake, MaxMessageLength),
             ]);
 
             SendMessageHandler.AddCommands([
@@ -179,7 +179,7 @@ namespace SharpChat {
 
                     if(banUser is not null) {
                         if(banDuration == TimeSpan.MinValue) {
-                            Context.SendTo(conn.User, new LegacyCommandResponse(Context.RandomSnowflake.Next(), LCR.FLOOD_WARN, false));
+                            Context.SendTo(conn.User, new CommandResponseS2CPacket(Context.RandomSnowflake.Next(), LCR.FLOOD_WARN, false));
                         } else {
                             Context.BanUser(conn.User, banDuration, UserDisconnectReason.Flood);
 
@@ -200,7 +200,7 @@ namespace SharpChat {
             }
 
             ChatPacketHandlerContext context = new(msg, Context, conn);
-            IChatPacketHandler? handler = conn.User is null
+            C2SPacketHandler? handler = conn.User is null
                 ? GuestHandlers.FirstOrDefault(h => h.IsMatch(context))
                 : AuthedHandlers.FirstOrDefault(h => h.IsMatch(context));