Reduce usage of working objects in packet objects as much as possible.

This commit is contained in:
flash 2024-05-10 18:29:48 +00:00
parent 356409eb16
commit b95cd06cb1
27 changed files with 314 additions and 206 deletions

View file

@ -1,11 +1,11 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
namespace SharpChat { namespace SharpChat {
public struct ChatColour { public readonly struct ChatColour {
public byte Red { get; } public readonly byte Red;
public byte Green { get; } public readonly byte Green;
public byte Blue { get; } public readonly byte Blue;
public bool Inherits { get; } public readonly bool Inherits;
public static ChatColour None { get; } = new(); public static ChatColour None { get; } = new();

View file

@ -192,7 +192,13 @@ namespace SharpChat {
if(previousName != null) if(previousName != null)
SendToUserChannels(user, new UserUpdateNotificationPacket(previousName, user.LegacyNameWithStatus)); SendToUserChannels(user, new UserUpdateNotificationPacket(previousName, user.LegacyNameWithStatus));
SendToUserChannels(user, new UserUpdatePacket(user)); SendToUserChannels(user, new UserUpdatePacket(
user.UserId,
user.LegacyNameWithStatus,
user.Colour,
user.Rank,
user.Permissions
));
} }
} }
@ -213,17 +219,36 @@ namespace SharpChat {
public void HandleJoin(ChatUser user, ChatChannel chan, ChatConnection conn, int maxMsgLength) { public void HandleJoin(ChatUser user, ChatChannel chan, ChatConnection conn, int maxMsgLength) {
if(!IsInChannel(user, chan)) { if(!IsInChannel(user, chan)) {
SendTo(chan, new UserConnectPacket(DateTimeOffset.Now, user)); SendTo(chan, new UserConnectPacket(
DateTimeOffset.Now,
user.UserId,
user.LegacyNameWithStatus,
user.Colour,
user.Rank,
user.Permissions
));
Events.AddEvent("user:connect", user, chan, flags: StoredEventFlags.Log); Events.AddEvent("user:connect", user, chan, flags: StoredEventFlags.Log);
} }
conn.Send(new AuthSuccessPacket(user, chan, conn, maxMsgLength)); conn.Send(new AuthSuccessPacket(
conn.Send(new ContextUsersPacket(GetChannelUsers(chan).Except(new[] { user }).OrderByDescending(u => u.Rank))); user.UserId,
user.LegacyNameWithStatus,
user.Colour,
user.Rank,
user.Permissions,
chan.Name,
maxMsgLength
));
conn.Send(new ContextUsersPacket(GetChannelUsers(chan).Except(new[] { user }).Select(
user => new ContextUsersPacket.ListEntry(user.UserId, user.LegacyNameWithStatus, user.Colour, user.Rank, user.Permissions, true)
).OrderByDescending(user => user.Rank).ToArray()));
foreach(StoredEventInfo msg in Events.GetChannelEventLog(chan.Name)) foreach(StoredEventInfo msg in Events.GetChannelEventLog(chan.Name))
conn.Send(new ContextMessagePacket(msg)); conn.Send(new ContextMessagePacket(msg));
conn.Send(new ContextChannelsPacket(Channels.Where(c => c.Rank <= user.Rank))); conn.Send(new ContextChannelsPacket(Channels.Where(c => c.Rank <= user.Rank).Select(
channel => new ContextChannelsPacket.ListEntry(channel.Name, channel.HasPassword, channel.IsTemporary)
).ToArray()));
Users.Add(user); Users.Add(user);
@ -241,7 +266,7 @@ namespace SharpChat {
foreach(ChatChannel chan in channels) { foreach(ChatChannel chan in channels) {
ChannelUsers.Remove(new ChannelUserAssoc(user.UserId, chan.Name)); ChannelUsers.Remove(new ChannelUserAssoc(user.UserId, chan.Name));
SendTo(chan, new UserDisconnectPacket(DateTimeOffset.Now, user, reason)); SendTo(chan, new UserDisconnectPacket(DateTimeOffset.Now, user.UserId, user.LegacyNameWithStatus, reason));
Events.AddEvent("user:disconnect", user, chan, new { reason = (int)reason }, StoredEventFlags.Log); Events.AddEvent("user:disconnect", user, chan, new { reason = (int)reason }, StoredEventFlags.Log);
if(chan.IsTemporary && chan.IsOwner(user)) if(chan.IsTemporary && chan.IsOwner(user))
@ -278,13 +303,15 @@ namespace SharpChat {
ChatChannel oldChan = UserLastChannel[user.UserId]; ChatChannel oldChan = UserLastChannel[user.UserId];
SendTo(oldChan, new UserChannelLeavePacket(user)); SendTo(oldChan, new UserChannelLeavePacket(user.UserId));
Events.AddEvent("chan:leave", user, oldChan, flags: StoredEventFlags.Log); Events.AddEvent("chan:leave", user, oldChan, flags: StoredEventFlags.Log);
SendTo(chan, new UserChannelJoinPacket(user)); SendTo(chan, new UserChannelJoinPacket(user.UserId, user.LegacyNameWithStatus, user.Colour, user.Rank, user.Permissions));
Events.AddEvent("chan:join", user, oldChan, flags: StoredEventFlags.Log); Events.AddEvent("chan:join", user, oldChan, flags: StoredEventFlags.Log);
SendTo(user, new ContextClearPacket(chan, ContextClearMode.MessagesUsers)); SendTo(user, new ContextClearPacket(ContextClearMode.MessagesUsers));
SendTo(user, new ContextUsersPacket(GetChannelUsers(chan).Except(new[] { user }).OrderByDescending(u => u.Rank))); SendTo(user, new ContextUsersPacket(GetChannelUsers(chan).Except(new[] { user }).Select(
user => new ContextUsersPacket.ListEntry(user.UserId, user.LegacyNameWithStatus, user.Colour, user.Rank, user.Permissions, true)
).OrderByDescending(u => u.Rank).ToArray()));
foreach(StoredEventInfo msg in Events.GetChannelEventLog(chan.Name)) foreach(StoredEventInfo msg in Events.GetChannelEventLog(chan.Name))
SendTo(user, new ContextMessagePacket(msg)); SendTo(user, new ContextMessagePacket(msg));
@ -354,27 +381,29 @@ namespace SharpChat {
if(chan == null && !UserLastChannel.TryGetValue(user.UserId, out chan)) if(chan == null && !UserLastChannel.TryGetValue(user.UserId, out chan))
throw new ArgumentException("no channel???"); throw new ArgumentException("no channel???");
SendTo(user, new UserChannelForceJoinPacket(chan)); SendTo(user, new UserChannelForceJoinPacket(chan.Name));
} }
public void UpdateChannel(ChatChannel channel, bool? temporary = null, int? hierarchy = null, string password = null) { public void UpdateChannel(ChatChannel channel, bool? temporary = null, int? minRank = null, string password = null) {
if(channel == null) if(channel == null)
throw new ArgumentNullException(nameof(channel)); throw new ArgumentNullException(nameof(channel));
if(!Channels.Contains(channel)) if(!Channels.Contains(channel))
throw new ArgumentException("Provided channel is not registered with this manager.", nameof(channel)); throw new ArgumentException("Provided channel is not registered with this manager.", nameof(channel));
string prevName = channel.Name;
if(temporary.HasValue) if(temporary.HasValue)
channel.IsTemporary = temporary.Value; channel.IsTemporary = temporary.Value;
if(hierarchy.HasValue) if(minRank.HasValue)
channel.Rank = hierarchy.Value; channel.Rank = minRank.Value;
if(password != null) if(password != null)
channel.Password = password; channel.Password = password;
// 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 // 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.Where(u => u.Rank >= channel.Rank)) {
SendTo(user, new ChannelUpdatePacket(channel.Name, channel)); SendTo(user, new ChannelUpdatePacket(prevName, channel.Name, channel.HasPassword, channel.IsTemporary));
} }
} }
@ -396,7 +425,7 @@ namespace SharpChat {
// Broadcast deletion of channel // Broadcast deletion of channel
foreach(ChatUser user in Users.Where(u => u.Rank >= channel.Rank)) foreach(ChatUser user in Users.Where(u => u.Rank >= channel.Rank))
SendTo(user, new ChannelDeletePacket(channel)); SendTo(user, new ChannelDeletePacket(channel.Name));
} }
} }
} }

View file

@ -1,6 +1,7 @@
using SharpChat.Misuzu; using SharpChat.Misuzu;
using SharpChat.Packet; using SharpChat.Packet;
using System; using System;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace SharpChat.Commands { namespace SharpChat.Commands {
@ -24,7 +25,9 @@ namespace SharpChat.Commands {
Task.Run(async () => { Task.Run(async () => {
ctx.Chat.SendTo(ctx.User, new BanListPacket( ctx.Chat.SendTo(ctx.User, new BanListPacket(
await Misuzu.GetBanListAsync() (await Misuzu.GetBanListAsync()).Select(
ban => string.IsNullOrEmpty(ban.UserName) ? ban.RemoteAddress : ban.UserName
).ToArray()
)); ));
}).Wait(); }).Wait();
} }

View file

@ -51,7 +51,11 @@ namespace SharpChat.Commands {
ctx.Chat.Channels.Add(createChan); ctx.Chat.Channels.Add(createChan);
foreach(ChatUser ccu in ctx.Chat.Users.Where(u => u.Rank >= ctx.Channel.Rank)) 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 ChannelCreatePacket(
ctx.Channel.Name,
ctx.Channel.HasPassword,
ctx.Channel.IsTemporary
));
ctx.Chat.SwitchChannel(ctx.User, createChan, createChan.Password); ctx.Chat.SwitchChannel(ctx.User, createChan, createChan.Password);
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.CHANNEL_CREATED, false, createChan.Name)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.CHANNEL_CREATED, false, createChan.Name));

View file

@ -20,7 +20,7 @@ namespace SharpChat.Commands {
return; return;
} }
ctx.Chat.UpdateChannel(ctx.Channel, hierarchy: chanHierarchy); ctx.Chat.UpdateChannel(ctx.Channel, minRank: chanHierarchy);
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.CHANNEL_HIERARCHY_CHANGED, false)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.CHANNEL_HIERARCHY_CHANGED, false));
} }
} }

View file

@ -1,5 +1,4 @@
using SharpChat.Misuzu; using System;
using System;
namespace SharpChat.Packet { namespace SharpChat.Packet {
public enum AuthFailReason { public enum AuthFailReason {
@ -9,14 +8,16 @@ namespace SharpChat.Packet {
} }
public class AuthFailPacket : ServerPacket { public class AuthFailPacket : ServerPacket {
public AuthFailReason Reason { get; private set; } private readonly AuthFailReason Reason;
public MisuzuBanInfo BanInfo { get; private set; } private readonly DateTimeOffset Expires;
public AuthFailPacket(AuthFailReason reason, MisuzuBanInfo fbi = null) { public AuthFailPacket(AuthFailReason reason) {
Reason = reason; Reason = reason;
}
if(reason == AuthFailReason.Banned) public AuthFailPacket(DateTimeOffset expires) {
BanInfo = fbi ?? throw new ArgumentNullException(nameof(fbi)); Reason = AuthFailReason.Banned;
Expires = expires;
} }
public override string Pack() { public override string Pack() {
@ -28,7 +29,7 @@ namespace SharpChat.Packet {
}); });
if(Reason == AuthFailReason.Banned) if(Reason == AuthFailReason.Banned)
packet += string.Format("\t{0}", BanInfo.IsPermanent ? -1 : BanInfo.ExpiresAt.ToUnixTimeSeconds()); packet += string.Format("\t{0}", Expires.Year >= 2100 ? -1 : Expires.ToUnixTimeSeconds());
return packet; return packet;
} }

View file

@ -2,37 +2,46 @@
namespace SharpChat.Packet { namespace SharpChat.Packet {
public class AuthSuccessPacket : ServerPacket { public class AuthSuccessPacket : ServerPacket {
public ChatUser User { get; private set; } private readonly long UserId;
public ChatChannel Channel { get; private set; } private readonly string UserName;
public ChatConnection Connection { get; private set; } private readonly ChatColour UserColour;
public int MaxMessageLength { get; private set; } private readonly int UserRank;
private readonly ChatUserPermissions UserPerms;
private readonly string ChannelName;
private readonly int MaxMessageLength;
public AuthSuccessPacket( public AuthSuccessPacket(
ChatUser user, long userId,
ChatChannel channel, string userName,
ChatConnection connection, ChatColour userColour,
int userRank,
ChatUserPermissions userPerms,
string channelName,
int maxMsgLength int maxMsgLength
) { ) {
User = user ?? throw new ArgumentNullException(nameof(user)); UserId = userId;
Channel = channel ?? throw new ArgumentNullException(nameof(channel)); UserName = userName ?? throw new ArgumentNullException(nameof(userName));
Connection = connection ?? throw new ArgumentNullException(nameof(connection)); UserColour = userColour;
UserRank = userRank;
UserPerms = userPerms;
ChannelName = channelName ?? throw new ArgumentNullException(nameof(channelName));
MaxMessageLength = maxMsgLength; MaxMessageLength = maxMsgLength;
} }
public override string Pack() { public override string Pack() {
return string.Format( return string.Format(
"1\ty\t{0}\t{1}\t{2}\t{3} {4} {5} {6} {7}\t{8}\t{9}", "1\ty\t{0}\t{1}\t{2}\t{3} {4} {5} {6} {7}\t{8}\t{9}",
User.UserId, UserId,
User.LegacyNameWithStatus, UserName,
User.Colour, UserColour,
User.Rank, UserRank,
User.Can(ChatUserPermissions.KickUser) ? 1 : 0, UserPerms.HasFlag(ChatUserPermissions.KickUser) ? 1 : 0,
User.Can(ChatUserPermissions.ViewLogs) ? 1 : 0, UserPerms.HasFlag(ChatUserPermissions.ViewLogs) ? 1 : 0,
User.Can(ChatUserPermissions.SetOwnNickname) ? 1 : 0, UserPerms.HasFlag(ChatUserPermissions.SetOwnNickname) ? 1 : 0,
User.Can(ChatUserPermissions.CreateChannel | ChatUserPermissions.SetChannelPermanent, true) ? 2 : ( UserPerms.HasFlag(ChatUserPermissions.CreateChannel) ? (
User.Can(ChatUserPermissions.CreateChannel) ? 1 : 0 UserPerms.HasFlag(ChatUserPermissions.SetChannelPermanent) ? 2 : 1
), ) : 0,
Channel.Name, ChannelName,
MaxMessageLength MaxMessageLength
); );
} }

View file

@ -1,14 +1,12 @@
using SharpChat.Misuzu; using System;
using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
namespace SharpChat.Packet { namespace SharpChat.Packet {
public class BanListPacket : ServerPacket { public class BanListPacket : ServerPacket {
public IEnumerable<MisuzuBanInfo> Bans { get; private set; } private readonly string[] Bans;
public BanListPacket(IEnumerable<MisuzuBanInfo> bans) { public BanListPacket(string[] bans) {
Bans = bans ?? throw new ArgumentNullException(nameof(bans)); Bans = bans ?? throw new ArgumentNullException(nameof(bans));
} }
@ -17,10 +15,8 @@ namespace SharpChat.Packet {
sb.AppendFormat("2\t{0}\t-1\t0\fbanlist\f", DateTimeOffset.Now.ToUnixTimeSeconds()); sb.AppendFormat("2\t{0}\t-1\t0\fbanlist\f", DateTimeOffset.Now.ToUnixTimeSeconds());
foreach(MisuzuBanInfo ban in Bans) { foreach(string ban in Bans)
string banStr = string.IsNullOrEmpty(ban.UserName) ? ban.RemoteAddress : ban.UserName; sb.AppendFormat(@"<a href=""javascript:void(0);"" onclick=""Chat.SendMessageWrapper('/unban '+ this.innerHTML);"">{0}</a>, ", ban);
sb.AppendFormat(@"<a href=""javascript:void(0);"" onclick=""Chat.SendMessageWrapper('/unban '+ this.innerHTML);"">{0}</a>, ", banStr);
}
if(Bans.Any()) if(Bans.Any())
sb.Length -= 2; sb.Length -= 2;

View file

@ -1,17 +1,27 @@
namespace SharpChat.Packet { using System;
public class ChannelCreatePacket : ServerPacket {
public ChatChannel Channel { get; private set; }
public ChannelCreatePacket(ChatChannel channel) { namespace SharpChat.Packet {
Channel = channel; public class ChannelCreatePacket : ServerPacket {
private readonly string ChannelName;
private readonly bool ChannelHasPassword;
private readonly bool ChannelIsTemporary;
public ChannelCreatePacket(
string channelName,
bool channelHasPassword,
bool channelIsTemporary
) {
ChannelName = channelName ?? throw new ArgumentNullException(nameof(channelName));
ChannelHasPassword = channelHasPassword;
ChannelIsTemporary = channelIsTemporary;
} }
public override string Pack() { public override string Pack() {
return string.Format( return string.Format(
"4\t0\t{0}\t{1}\t{2}", "4\t0\t{0}\t{1}\t{2}",
Channel.Name, ChannelName,
Channel.HasPassword ? 1 : 0, ChannelHasPassword ? 1 : 0,
Channel.IsTemporary ? 1 : 0 ChannelIsTemporary ? 1 : 0
); );
} }
} }

View file

@ -2,14 +2,14 @@
namespace SharpChat.Packet { namespace SharpChat.Packet {
public class ChannelDeletePacket : ServerPacket { public class ChannelDeletePacket : ServerPacket {
public ChatChannel Channel { get; private set; } private readonly string ChannelName;
public ChannelDeletePacket(ChatChannel channel) { public ChannelDeletePacket(string channelName) {
Channel = channel ?? throw new ArgumentNullException(nameof(channel)); ChannelName = channelName ?? throw new ArgumentNullException(nameof(channelName));
} }
public override string Pack() { public override string Pack() {
return string.Format("4\t2\t{0}", Channel.Name); return string.Format("4\t2\t{0}", ChannelName);
} }
} }
} }

View file

@ -1,20 +1,31 @@
namespace SharpChat.Packet { using System;
public class ChannelUpdatePacket : ServerPacket {
public string PreviousName { get; private set; }
public ChatChannel Channel { get; private set; }
public ChannelUpdatePacket(string previousName, ChatChannel channel) { namespace SharpChat.Packet {
PreviousName = previousName; public class ChannelUpdatePacket : ServerPacket {
Channel = channel; private readonly string ChannelNamePrevious;
private readonly string ChannelNameNew;
private readonly bool ChannelHasPassword;
private readonly bool ChannelIsTemporary;
public ChannelUpdatePacket(
string channelNamePrevious,
string channelNameNew,
bool channelHasPassword,
bool channelIsTemporary
) {
ChannelNamePrevious = channelNamePrevious ?? throw new ArgumentNullException(nameof(channelNamePrevious));
ChannelNameNew = channelNameNew ?? throw new ArgumentNullException(nameof(channelNameNew));
ChannelHasPassword = channelHasPassword;
ChannelIsTemporary = channelIsTemporary;
} }
public override string Pack() { public override string Pack() {
return string.Format( return string.Format(
"4\t1\t{0}\t{1}\t{2}\t{3}", "4\t1\t{0}\t{1}\t{2}\t{3}",
PreviousName, ChannelNamePrevious,
Channel.Name, ChannelNameNew,
Channel.HasPassword ? 1 : 0, ChannelHasPassword ? 1 : 0,
Channel.IsTemporary ? 1 : 0 ChannelIsTemporary ? 1 : 0
); );
} }
} }

View file

@ -2,29 +2,29 @@
namespace SharpChat.Packet { namespace SharpChat.Packet {
public class ChatMessageAddPacket : ServerPacket { public class ChatMessageAddPacket : ServerPacket {
public DateTimeOffset Created { get; } private readonly DateTimeOffset Created;
public long UserId { get; } private readonly long UserId;
public string Text { get; } private readonly string Body;
public bool IsAction { get; } private readonly bool IsAction;
public bool IsPrivate { get; } private readonly bool IsPrivate;
public ChatMessageAddPacket( public ChatMessageAddPacket(
long msgId, long msgId,
DateTimeOffset created, DateTimeOffset created,
long userId, long userId,
string text, string body,
bool isAction, bool isAction,
bool isPrivate bool isPrivate
) : base(msgId) { ) : base(msgId) {
Created = created; Created = created;
UserId = userId < 0 ? -1 : userId; UserId = userId < 0 ? -1 : userId;
Text = text; Body = body ?? throw new ArgumentNullException(nameof(body));
IsAction = isAction; IsAction = isAction;
IsPrivate = isPrivate; IsPrivate = isPrivate;
} }
public override string Pack() { public override string Pack() {
string body = Text.Replace("<", "&lt;").Replace(">", "&gt;").Replace("\n", " <br/> ").Replace("\t", " "); string body = Body.Replace("<", "&lt;").Replace(">", "&gt;").Replace("\n", " <br/> ").Replace("\t", " ");
if(IsAction) if(IsAction)
body = string.Format("<i>{0}</i>", body); body = string.Format("<i>{0}</i>", body);

View file

@ -1,6 +1,6 @@
namespace SharpChat.Packet { namespace SharpChat.Packet {
public class ChatMessageDeletePacket : ServerPacket { public class ChatMessageDeletePacket : ServerPacket {
public long EventId { get; private set; } private readonly long EventId;
public ChatMessageDeletePacket(long eventId) { public ChatMessageDeletePacket(long eventId) {
EventId = eventId; EventId = eventId;

View file

@ -1,27 +1,27 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; using System.Text;
namespace SharpChat.Packet { namespace SharpChat.Packet {
public class ContextChannelsPacket : ServerPacket { public class ContextChannelsPacket : ServerPacket {
public IEnumerable<ChatChannel> Channels { get; private set; } public record ListEntry(string Name, bool HasPassword, bool IsTemporary);
public ContextChannelsPacket(IEnumerable<ChatChannel> channels) { private readonly ListEntry[] Entries;
Channels = channels?.Where(c => c != null) ?? throw new ArgumentNullException(nameof(channels));
public ContextChannelsPacket(ListEntry[] entries) {
Entries = entries ?? throw new ArgumentNullException(nameof(entries));
} }
public override string Pack() { public override string Pack() {
StringBuilder sb = new(); StringBuilder sb = new();
sb.AppendFormat("7\t2\t{0}", Channels.Count()); sb.AppendFormat("7\t2\t{0}", Entries.Length);
foreach(ChatChannel channel in Channels) foreach(ListEntry entry in Entries)
sb.AppendFormat( sb.AppendFormat(
"\t{0}\t{1}\t{2}", "\t{0}\t{1}\t{2}",
channel.Name, entry.Name,
channel.HasPassword ? 1 : 0, entry.HasPassword ? 1 : 0,
channel.IsTemporary ? 1 : 0 entry.IsTemporary ? 1 : 0
); );
return sb.ToString(); return sb.ToString();

View file

@ -8,14 +8,9 @@
} }
public class ContextClearPacket : ServerPacket { public class ContextClearPacket : ServerPacket {
public ChatChannel Channel { get; private set; } private readonly ContextClearMode Mode;
public ContextClearMode Mode { get; private set; }
public bool IsGlobal public ContextClearPacket(ContextClearMode mode) {
=> Channel == null;
public ContextClearPacket(ChatChannel channel, ContextClearMode mode) {
Channel = channel;
Mode = mode; Mode = mode;
} }

View file

@ -3,9 +3,10 @@ using System;
using System.Text; using System.Text;
namespace SharpChat.Packet { namespace SharpChat.Packet {
// this entire class is disgusting
public class ContextMessagePacket : ServerPacket { public class ContextMessagePacket : ServerPacket {
public StoredEventInfo Event { get; private set; } private readonly StoredEventInfo Event;
public bool Notify { get; private set; } private readonly bool Notify;
public ContextMessagePacket(StoredEventInfo evt, bool notify = false) { public ContextMessagePacket(StoredEventInfo evt, bool notify = false) {
Event = evt ?? throw new ArgumentNullException(nameof(evt)); Event = evt ?? throw new ArgumentNullException(nameof(evt));

View file

@ -1,35 +1,35 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; using System.Text;
namespace SharpChat.Packet { namespace SharpChat.Packet {
public class ContextUsersPacket : ServerPacket { public class ContextUsersPacket : ServerPacket {
public IEnumerable<ChatUser> Users { get; private set; } public record ListEntry(long Id, string Name, ChatColour Colour, int Rank, ChatUserPermissions Perms, bool Visible);
public ContextUsersPacket(IEnumerable<ChatUser> users) { private readonly ListEntry[] Entries;
Users = users?.Where(u => u != null) ?? throw new ArgumentNullException(nameof(users));
public ContextUsersPacket(ListEntry[] entries) {
Entries = entries ?? throw new ArgumentNullException(nameof(entries));
} }
public override string Pack() { public override string Pack() {
StringBuilder sb = new(); StringBuilder sb = new();
sb.AppendFormat("7\t0\t{0}", Users.Count()); sb.AppendFormat("7\t0\t{0}", Entries.Length);
foreach(ChatUser user in Users) foreach(ListEntry entry in Entries)
sb.AppendFormat( sb.AppendFormat(
"\t{0}\t{1}\t{2}\t{3} {4} {5} {6} {7}\t{8}", "\t{0}\t{1}\t{2}\t{3} {4} {5} {6} {7}\t{8}",
user.UserId, entry.Id,
user.LegacyNameWithStatus, entry.Name,
user.Colour, entry.Colour,
user.Rank, entry.Rank,
user.Can(ChatUserPermissions.KickUser) ? 1 : 0, entry.Perms.HasFlag(ChatUserPermissions.KickUser) ? 1 : 0,
user.Can(ChatUserPermissions.ViewLogs) ? 1 : 0, entry.Perms.HasFlag(ChatUserPermissions.ViewLogs) ? 1 : 0,
user.Can(ChatUserPermissions.SetOwnNickname) ? 1 : 0, entry.Perms.HasFlag(ChatUserPermissions.SetOwnNickname) ? 1 : 0,
user.Can(ChatUserPermissions.CreateChannel | ChatUserPermissions.SetChannelPermanent, true) ? 2 : ( entry.Perms.HasFlag(ChatUserPermissions.CreateChannel) ? (
user.Can(ChatUserPermissions.CreateChannel) ? 1 : 0 entry.Perms.HasFlag(ChatUserPermissions.SetChannelPermanent) ? 2 : 1
), ) : 0,
1 // visibility flag entry.Visible ? 1 : 0
); );
return sb.ToString(); return sb.ToString();

View file

@ -2,17 +2,21 @@
namespace SharpChat.Packet { namespace SharpChat.Packet {
public class ForceDisconnectPacket : ServerPacket { public class ForceDisconnectPacket : ServerPacket {
public DateTimeOffset? Expires { get; private set; } private readonly DateTimeOffset Expires;
public ForceDisconnectPacket(DateTimeOffset? expires = null) { public ForceDisconnectPacket() {
Expires = DateTimeOffset.MinValue;
}
public ForceDisconnectPacket(DateTimeOffset expires) {
Expires = expires; Expires = expires;
} }
public override string Pack() { public override string Pack() {
if(Expires.HasValue) if(Expires == DateTimeOffset.MinValue)
return string.Format( return string.Format(
"9\t1\t{0}", "9\t1\t{0}",
Expires.Value.Year >= 2100 ? -1 : Expires.Value.ToUnixTimeSeconds() Expires.Year >= 2100 ? -1 : Expires.ToUnixTimeSeconds()
); );
return "9\t0"; return "9\t0";

View file

@ -1,13 +1,11 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; using System.Text;
namespace SharpChat.Packet { namespace SharpChat.Packet {
public class LegacyCommandResponse : ServerPacket { public class LegacyCommandResponse : ServerPacket {
public bool IsError { get; private set; } private readonly bool IsError;
public string StringId { get; private set; } private readonly string StringId;
public IEnumerable<object> Arguments { get; private set; } private readonly object[] Arguments;
public LegacyCommandResponse( public LegacyCommandResponse(
string stringId, string stringId,
@ -39,9 +37,8 @@ namespace SharpChat.Packet {
StringId == LCR.WELCOME ? LCR.BROADCAST : StringId StringId == LCR.WELCOME ? LCR.BROADCAST : StringId
); );
if(Arguments?.Any() == true) foreach(object arg in Arguments)
foreach(object arg in Arguments) sb.AppendFormat("\f{0}", arg);
sb.AppendFormat("\f{0}", arg);
sb.Append('\t'); sb.Append('\t');

View file

@ -2,14 +2,14 @@
namespace SharpChat.Packet { namespace SharpChat.Packet {
public class UserChannelForceJoinPacket : ServerPacket { public class UserChannelForceJoinPacket : ServerPacket {
public ChatChannel Channel { get; private set; } private readonly string ChannelName;
public UserChannelForceJoinPacket(ChatChannel channel) { public UserChannelForceJoinPacket(string channelName) {
Channel = channel ?? throw new ArgumentNullException(nameof(channel)); ChannelName = channelName ?? throw new ArgumentNullException(nameof(channelName));
} }
public override string Pack() { public override string Pack() {
return string.Format("5\t2\t{0}", Channel.Name); return string.Format("5\t2\t{0}", ChannelName);
} }
} }
} }

View file

@ -2,25 +2,39 @@
namespace SharpChat.Packet { namespace SharpChat.Packet {
public class UserChannelJoinPacket : ServerPacket { public class UserChannelJoinPacket : ServerPacket {
public ChatUser User { get; private set; } private readonly long UserId;
private readonly string UserName;
private readonly ChatColour UserColour;
private readonly int UserRank;
private readonly ChatUserPermissions UserPerms;
public UserChannelJoinPacket(ChatUser user) { public UserChannelJoinPacket(
User = user ?? throw new ArgumentNullException(nameof(user)); long userId,
string userName,
ChatColour userColour,
int userRank,
ChatUserPermissions userPerms
) {
UserId = userId;
UserName = userName ?? throw new ArgumentNullException(nameof(userName));
UserColour = userColour;
UserRank = userRank;
UserPerms = userPerms;
} }
public override string Pack() { public override string Pack() {
return string.Format( return string.Format(
"5\t0\t{0}\t{1}\t{2}\t{3} {4} {5} {6} {7}\t{8}", "5\t0\t{0}\t{1}\t{2}\t{3} {4} {5} {6} {7}\t{8}",
User.UserId, UserId,
User.LegacyNameWithStatus, UserName,
User.Colour, UserColour,
User.Rank, UserRank,
User.Can(ChatUserPermissions.KickUser) ? 1 : 0, UserPerms.HasFlag(ChatUserPermissions.KickUser) ? 1 : 0,
User.Can(ChatUserPermissions.ViewLogs) ? 1 : 0, UserPerms.HasFlag(ChatUserPermissions.ViewLogs) ? 1 : 0,
User.Can(ChatUserPermissions.SetOwnNickname) ? 1 : 0, UserPerms.HasFlag(ChatUserPermissions.SetOwnNickname) ? 1 : 0,
User.Can(ChatUserPermissions.CreateChannel | ChatUserPermissions.SetChannelPermanent, true) ? 2 : ( UserPerms.HasFlag(ChatUserPermissions.CreateChannel) ? (
User.Can(ChatUserPermissions.CreateChannel) ? 1 : 0 UserPerms.HasFlag(ChatUserPermissions.SetChannelPermanent) ? 2 : 1
), ) : 0,
SequenceId SequenceId
); );
} }

View file

@ -1,15 +1,13 @@
using System; namespace SharpChat.Packet {
namespace SharpChat.Packet {
public class UserChannelLeavePacket : ServerPacket { public class UserChannelLeavePacket : ServerPacket {
public ChatUser User { get; private set; } private readonly long UserId;
public UserChannelLeavePacket(ChatUser user) { public UserChannelLeavePacket(long userId) {
User = user ?? throw new ArgumentNullException(nameof(user)); UserId = userId;
} }
public override string Pack() { public override string Pack() {
return string.Format("5\t1\t{0}\t{1}", User.UserId, SequenceId); return string.Format("5\t1\t{0}\t{1}", UserId, SequenceId);
} }
} }
} }

View file

@ -2,28 +2,43 @@
namespace SharpChat.Packet { namespace SharpChat.Packet {
public class UserConnectPacket : ServerPacket { public class UserConnectPacket : ServerPacket {
public DateTimeOffset Joined { get; private set; } private readonly DateTimeOffset Joined;
public ChatUser User { get; private set; } private readonly long UserId;
private readonly string UserName;
private readonly ChatColour UserColour;
private readonly int UserRank;
private readonly ChatUserPermissions UserPerms;
public UserConnectPacket(DateTimeOffset joined, ChatUser user) { public UserConnectPacket(
DateTimeOffset joined,
long userId,
string userName,
ChatColour userColour,
int userRank,
ChatUserPermissions userPerms
) {
Joined = joined; Joined = joined;
User = user ?? throw new ArgumentNullException(nameof(user)); UserId = userId;
UserName = userName ?? throw new ArgumentNullException(nameof(userName));
UserColour = userColour;
UserRank = userRank;
UserPerms = userPerms;
} }
public override string Pack() { public override string Pack() {
return string.Format( return string.Format(
"1\t{0}\t{1}\t{2}\t{3}\t{4} {5} {6} {7} {8}\t{9}", "1\t{0}\t{1}\t{2}\t{3}\t{4} {5} {6} {7} {8}\t{9}",
Joined.ToUnixTimeSeconds(), Joined.ToUnixTimeSeconds(),
User.UserId, UserId,
User.LegacyNameWithStatus, UserName,
User.Colour, UserColour,
User.Rank, UserRank,
User.Can(ChatUserPermissions.KickUser) ? 1 : 0, UserPerms.HasFlag(ChatUserPermissions.KickUser) ? 1 : 0,
User.Can(ChatUserPermissions.ViewLogs) ? 1 : 0, UserPerms.HasFlag(ChatUserPermissions.ViewLogs) ? 1 : 0,
User.Can(ChatUserPermissions.SetOwnNickname) ? 1 : 0, UserPerms.HasFlag(ChatUserPermissions.SetOwnNickname) ? 1 : 0,
User.Can(ChatUserPermissions.CreateChannel | ChatUserPermissions.SetChannelPermanent, true) ? 2 : ( UserPerms.HasFlag(ChatUserPermissions.CreateChannel) ? (
User.Can(ChatUserPermissions.CreateChannel) ? 1 : 0 UserPerms.HasFlag(ChatUserPermissions.SetChannelPermanent) ? 2 : 1
), ) : 0,
SequenceId SequenceId
); );
} }

View file

@ -9,21 +9,28 @@ namespace SharpChat.Packet {
} }
public class UserDisconnectPacket : ServerPacket { public class UserDisconnectPacket : ServerPacket {
public DateTimeOffset Disconnected { get; private set; } private readonly DateTimeOffset Disconnected;
public ChatUser User { get; private set; } private readonly long UserId;
public UserDisconnectReason Reason { get; private set; } private readonly string UserName;
private readonly UserDisconnectReason Reason;
public UserDisconnectPacket(DateTimeOffset disconnected, ChatUser user, UserDisconnectReason reason) { public UserDisconnectPacket(
DateTimeOffset disconnected,
long userId,
string userName,
UserDisconnectReason reason
) {
Disconnected = disconnected; Disconnected = disconnected;
User = user ?? throw new ArgumentNullException(nameof(user)); UserId = userId;
UserName = userName ?? throw new ArgumentNullException(nameof(userName));
Reason = reason; Reason = reason;
} }
public override string Pack() { public override string Pack() {
return string.Format( return string.Format(
"3\t{0}\t{1}\t{2}\t{3}\t{4}", "3\t{0}\t{1}\t{2}\t{3}\t{4}",
User.UserId, UserId,
User.LegacyNameWithStatus, UserName,
Reason switch { Reason switch {
UserDisconnectReason.Leave => "leave", UserDisconnectReason.Leave => "leave",
UserDisconnectReason.TimeOut => "timeout", UserDisconnectReason.TimeOut => "timeout",

View file

@ -2,9 +2,9 @@
namespace SharpChat.Packet { namespace SharpChat.Packet {
public class UserUpdateNotificationPacket : ServerPacket { public class UserUpdateNotificationPacket : ServerPacket {
public string PreviousName { get; private set; } private readonly string PreviousName;
public string NewName { get; private set; } private readonly string NewName;
public DateTimeOffset Timestamp { get; } private readonly DateTimeOffset Timestamp;
public UserUpdateNotificationPacket(string previousName, string newName) { public UserUpdateNotificationPacket(string previousName, string newName) {
PreviousName = previousName ?? throw new ArgumentNullException(nameof(previousName)); PreviousName = previousName ?? throw new ArgumentNullException(nameof(previousName));

View file

@ -2,25 +2,39 @@
namespace SharpChat.Packet { namespace SharpChat.Packet {
public class UserUpdatePacket : ServerPacket { public class UserUpdatePacket : ServerPacket {
public ChatUser User { get; private set; } private readonly long UserId;
private readonly string UserName;
private readonly ChatColour UserColour;
private readonly int UserRank;
private readonly ChatUserPermissions UserPerms;
public UserUpdatePacket(ChatUser user) { public UserUpdatePacket(
User = user ?? throw new ArgumentNullException(nameof(user)); long userId,
string userName,
ChatColour userColour,
int userRank,
ChatUserPermissions userPerms
) {
UserId = userId;
UserName = userName ?? throw new ArgumentNullException(nameof(userName));
UserColour = userColour;
UserRank = userRank;
UserPerms = userPerms;
} }
public override string Pack() { public override string Pack() {
return string.Format( return string.Format(
"10\t{0}\t{1}\t{2}\t{3} {4} {5} {6} {7}", "10\t{0}\t{1}\t{2}\t{3} {4} {5} {6} {7}",
User.UserId, UserId,
User.LegacyNameWithStatus, UserName,
User.Colour, UserColour,
User.Rank, UserRank,
User.Can(ChatUserPermissions.KickUser) ? 1 : 0, UserPerms.HasFlag(ChatUserPermissions.KickUser) ? 1 : 0,
User.Can(ChatUserPermissions.ViewLogs) ? 1 : 0, UserPerms.HasFlag(ChatUserPermissions.ViewLogs) ? 1 : 0,
User.Can(ChatUserPermissions.SetOwnNickname) ? 1 : 0, UserPerms.HasFlag(ChatUserPermissions.SetOwnNickname) ? 1 : 0,
User.Can(ChatUserPermissions.CreateChannel | ChatUserPermissions.SetChannelPermanent, true) ? 2 : ( UserPerms.HasFlag(ChatUserPermissions.CreateChannel) ? (
User.Can(ChatUserPermissions.CreateChannel) ? 1 : 0 UserPerms.HasFlag(ChatUserPermissions.SetChannelPermanent) ? 2 : 1
) ) : 0
); );
} }
} }

View file

@ -93,7 +93,7 @@ namespace SharpChat.PacketHandlers {
if(fbi.IsBanned && !fbi.HasExpired) { if(fbi.IsBanned && !fbi.HasExpired) {
Logger.Write($"<{ctx.Connection.Id}> User is banned."); Logger.Write($"<{ctx.Connection.Id}> User is banned.");
ctx.Connection.Send(new AuthFailPacket(AuthFailReason.Banned, fbi)); ctx.Connection.Send(new AuthFailPacket(fbi.ExpiresAt));
ctx.Connection.Dispose(); ctx.Connection.Dispose();
return; return;
} }