From c537df792e95815d4220f7ae0407358bf27c4199 Mon Sep 17 00:00:00 2001 From: flashwave <me@flash.moe> Date: Mon, 28 Apr 2025 13:03:38 +0000 Subject: [PATCH] Create Context before creating a server. --- SharpChat.Flashii/FlashiiClient.cs | 3 +- SharpChat/Context.cs | 55 +++++++++++++- SharpChat/Program.cs | 22 +++--- SharpChat/SockChatServer.cs | 73 ++++--------------- SharpChatCommon/Configuration/Config.cs | 2 +- SharpChatCommon/Configuration/ScopedConfig.cs | 4 - SharpChatCommon/Configuration/StreamConfig.cs | 2 +- 7 files changed, 85 insertions(+), 76 deletions(-) diff --git a/SharpChat.Flashii/FlashiiClient.cs b/SharpChat.Flashii/FlashiiClient.cs index ffac01b..7799431 100644 --- a/SharpChat.Flashii/FlashiiClient.cs +++ b/SharpChat.Flashii/FlashiiClient.cs @@ -61,10 +61,11 @@ public class FlashiiClient(ILogger logger, HttpClient httpClient, Config config) private const string AUTH_BUMP_USERS_ONLINE_URL = "{0}/bump"; public async Task AuthBumpUsersOnline(IEnumerable<(IPAddress remoteAddr, string userId)> entries) { - logger.ZLogInformation($"Bumping online users list..."); if(!entries.Any()) return; + logger.ZLogInformation($"Bumping online users list..."); + string now = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString(); StringBuilder sb = new(); sb.AppendFormat("bump#{0}", now); diff --git a/SharpChat/Context.cs b/SharpChat/Context.cs index 4e066f1..9328994 100644 --- a/SharpChat/Context.cs +++ b/SharpChat/Context.cs @@ -1,5 +1,8 @@ using Microsoft.Extensions.Logging; +using SharpChat.Auth; +using SharpChat.Bans; using SharpChat.Channels; +using SharpChat.Configuration; using SharpChat.Events; using SharpChat.Messages; using SharpChat.Snowflake; @@ -12,11 +15,26 @@ using ZLogger; namespace SharpChat; public class Context { + public const int DEFAULT_MSG_LENGTH_MAX = 5000; + public const int DEFAULT_MAX_CONNECTIONS = 5; + public const int DEFAULT_FLOOD_KICK_LENGTH = 30; + public const int DEFAULT_FLOOD_KICK_EXEMPT_RANK = 9; + public record ChannelUserAssoc(string UserId, string ChannelName); public readonly SemaphoreSlim ContextAccess = new(1, 1); public ILoggerFactory LoggerFactory { get; } + public Config Config { get; } + public MessageStorage Messages { get; } + public AuthClient Auth { get; } + public BansClient Bans { get; } + + public CachedValue<int> MaxMessageLength { get; } + public CachedValue<int> MaxConnections { get; } + public CachedValue<int> FloodKickLength { get; } + public CachedValue<int> FloodKickExemptRank { get; } + private readonly ILogger Logger; public SnowflakeGenerator SnowflakeGenerator { get; } = new(); @@ -25,16 +43,47 @@ public class Context { public ChannelsContext Channels { get; } = new(); public HashSet<Connection> Connections { get; } = []; public HashSet<User> Users { get; } = []; - public MessageStorage Messages { get; } public HashSet<ChannelUserAssoc> ChannelUsers { get; } = []; public Dictionary<string, RateLimiter> UserRateLimiters { get; } = []; public Dictionary<string, Channel> UserLastChannel { get; } = []; - public Context(ILoggerFactory logFactory, MessageStorage msgs) { + public Context( + ILoggerFactory logFactory, + Config config, + Storage storage, + AuthClient authClient, + BansClient bansClient + ) { LoggerFactory = logFactory; Logger = logFactory.CreateLogger("ctx"); - Messages = msgs; + Config = config; + Messages = storage.CreateMessageStorage(); + Auth = authClient; + Bans = bansClient; RandomSnowflake = new(SnowflakeGenerator); + + Logger.ZLogDebug($"Reading cached config values..."); + MaxMessageLength = config.ReadCached("msgMaxLength", DEFAULT_MSG_LENGTH_MAX); + MaxConnections = config.ReadCached("connMaxCount", DEFAULT_MAX_CONNECTIONS); + FloodKickLength = config.ReadCached("floodKickLength", DEFAULT_FLOOD_KICK_LENGTH); + FloodKickExemptRank = config.ReadCached("floodKickExemptRank", DEFAULT_FLOOD_KICK_EXEMPT_RANK); + + Logger.ZLogDebug($"Creating channel list..."); + string[] channelNames = config.ReadValue<string[]>("channels") ?? ["lounge"]; + if(channelNames is not null) + foreach(string channelName in channelNames) { + Config channelCfg = config.ScopeTo($"channels:{channelName}"); + + string name = channelCfg.SafeReadValue("name", string.Empty)!; + if(string.IsNullOrWhiteSpace(name)) + name = channelName; + + Channels.CreateChannel( + name, + channelCfg.SafeReadValue("password", string.Empty)!, + rank: channelCfg.SafeReadValue("minRank", 0) + ); + } } public async Task DispatchEvent(ChatEvent eventInfo) { diff --git a/SharpChat/Program.cs b/SharpChat/Program.cs index 39d0e07..523c660 100644 --- a/SharpChat/Program.cs +++ b/SharpChat/Program.cs @@ -82,11 +82,12 @@ if(!File.Exists(configFile) && configFile == CONFIG) { sw.WriteLine(); sw.WriteLine("# General Configuration"); - sw.WriteLine($"#chat:port {SockChatServer.DEFAULT_PORT}"); - sw.WriteLine($"#chat:msgMaxLength {SockChatServer.DEFAULT_MSG_LENGTH_MAX}"); - sw.WriteLine($"#chat:connMaxCount {SockChatServer.DEFAULT_MAX_CONNECTIONS}"); - sw.WriteLine($"#chat:floodKickLength {SockChatServer.DEFAULT_FLOOD_KICK_LENGTH}"); + sw.WriteLine($"#chat:msgMaxLength {Context.DEFAULT_MSG_LENGTH_MAX}"); + sw.WriteLine($"#chat:connMaxCount {Context.DEFAULT_MAX_CONNECTIONS}"); + sw.WriteLine($"#chat:floodKickLength {Context.DEFAULT_FLOOD_KICK_LENGTH}"); + sw.WriteLine("# Sock Chat Configuration"); + sw.WriteLine($"#sockchat:port {SockChatServer.DEFAULT_PORT}"); sw.WriteLine(); sw.WriteLine("# Channels"); @@ -173,14 +174,17 @@ try { if(cts.IsCancellationRequested) return; + logger.ZLogDebug($"Creating context..."); + Config chatConfig = config.ScopeTo("chat"); + Context ctx = new(logFactory, chatConfig, storage, flashii, flashii); + + if(cts.IsCancellationRequested) return; + logger.ZLogInformation($"Preparing server..."); await new SockChatServer( - logFactory, cts, - flashii, - flashii, - storage.CreateMessageStorage(), - config.ScopeTo("chat") + ctx, + config.ScopeTo("sockchat") ).Listen(cts.Token); } finally { if(storage is IDisposable disp) { diff --git a/SharpChat/SockChatServer.cs b/SharpChat/SockChatServer.cs index c1a3ae0..5af25d5 100644 --- a/SharpChat/SockChatServer.cs +++ b/SharpChat/SockChatServer.cs @@ -1,10 +1,8 @@ using Microsoft.Extensions.Logging; -using SharpChat.Auth; using SharpChat.Bans; using SharpChat.C2SPacketHandlers; using SharpChat.ClientCommands; using SharpChat.Configuration; -using SharpChat.Messages; using SharpChat.SockChat.S2CPackets; using System.Net; using ZLogger; @@ -13,83 +11,44 @@ namespace SharpChat; public class SockChatServer { public const ushort DEFAULT_PORT = 6770; - public const int DEFAULT_MSG_LENGTH_MAX = 5000; - public const int DEFAULT_MAX_CONNECTIONS = 5; - public const int DEFAULT_FLOOD_KICK_LENGTH = 30; - public const int DEFAULT_FLOOD_KICK_EXEMPT_RANK = 9; public Context Context { get; } private readonly ILogger Logger; - private readonly BansClient BansClient; - private readonly CachedValue<ushort> Port; - private readonly CachedValue<int> MaxMessageLength; - private readonly CachedValue<int> MaxConnections; - private readonly CachedValue<int> FloodKickLength; - private readonly CachedValue<int> FloodKickExemptRank; private readonly List<C2SPacketHandler> GuestHandlers = []; private readonly List<C2SPacketHandler> AuthedHandlers = []; private readonly SendMessageC2SPacketHandler SendMessageHandler; - private static readonly string[] DEFAULT_CHANNELS = ["lounge"]; - public SockChatServer( - ILoggerFactory logFactory, CancellationTokenSource cancellationTokenSource, - AuthClient authClient, - BansClient bansClient, - MessageStorage msgStorage, + Context ctx, Config config ) { - Logger = logFactory.CreateLogger("sockchat"); + Logger = ctx.LoggerFactory.CreateLogger("sockchat"); Logger.ZLogInformation($"Initialising Sock Chat server..."); - BansClient = bansClient; + Context = ctx; Logger.ZLogDebug($"Fetching configuration values..."); Port = config.ReadCached("port", DEFAULT_PORT); - MaxMessageLength = config.ReadCached("msgMaxLength", DEFAULT_MSG_LENGTH_MAX); - MaxConnections = config.ReadCached("connMaxCount", DEFAULT_MAX_CONNECTIONS); - FloodKickLength = config.ReadCached("floodKickLength", DEFAULT_FLOOD_KICK_LENGTH); - FloodKickExemptRank = config.ReadCached("floodKickExemptRank", DEFAULT_FLOOD_KICK_EXEMPT_RANK); - - Logger.ZLogDebug($"Creating context..."); - Context = new Context(logFactory, msgStorage ?? throw new ArgumentNullException(nameof(msgStorage))); - - Logger.ZLogDebug($"Creating channel list..."); - string[]? channelNames = config.ReadValue("channels", DEFAULT_CHANNELS); - if(channelNames is not null) - foreach(string channelName in channelNames) { - Config channelCfg = config.ScopeTo($"channels:{channelName}"); - - string name = channelCfg.SafeReadValue("name", string.Empty)!; - if(string.IsNullOrWhiteSpace(name)) - name = channelName; - - Context.Channels.CreateChannel( - name, - channelCfg.SafeReadValue("password", string.Empty)!, - rank: channelCfg.SafeReadValue("minRank", 0) - ); - } Logger.ZLogDebug($"Registering unauthenticated handlers..."); GuestHandlers.Add(new AuthC2SPacketHandler( - authClient, - bansClient, + Context.Auth, + Context.Bans, Context.Channels, Context.RandomSnowflake, - MaxMessageLength, - MaxConnections + Context.MaxMessageLength, + Context.MaxConnections )); Logger.ZLogDebug($"Registering authenticated handlers..."); AuthedHandlers.AddRange([ - new PingC2SPacketHandler(authClient), - SendMessageHandler = new SendMessageC2SPacketHandler(Context.RandomSnowflake, MaxMessageLength), + new PingC2SPacketHandler(Context.Auth), + SendMessageHandler = new SendMessageC2SPacketHandler(Context.RandomSnowflake, Context.MaxMessageLength), ]); Logger.ZLogDebug($"Registering client commands..."); @@ -106,10 +65,10 @@ public class SockChatServer { new RankChannelClientCommand(), new BroadcastClientCommand(), new DeleteMessageClientCommand(), - new KickBanClientCommand(bansClient), - new PardonUserClientCommand(bansClient), - new PardonAddressClientCommand(bansClient), - new BanListClientCommand(bansClient), + new KickBanClientCommand(Context.Bans), + new PardonUserClientCommand(Context.Bans), + new PardonAddressClientCommand(Context.Bans), + new BanListClientCommand(Context.Bans), new RemoteAddressClientCommand(), new ShutdownRestartClientCommand(cancellationTokenSource) ]); @@ -190,7 +149,7 @@ public class SockChatServer { await Context.SafeUpdate(); // this doesn't affect non-authed connections????? - if(conn.User is not null && conn.User.Rank < FloodKickExemptRank) { + if(conn.User is not null && conn.User.Rank < Context.FloodKickExemptRank) { User? banUser = null; string banAddr = string.Empty; TimeSpan banDuration = TimeSpan.MinValue; @@ -207,7 +166,7 @@ public class SockChatServer { rateLimiter.Update(); if(rateLimiter.IsExceeded) { - banDuration = TimeSpan.FromSeconds(FloodKickLength); + banDuration = TimeSpan.FromSeconds(Context.FloodKickLength); banUser = conn.User; banAddr = conn.RemoteEndPoint.Address.ToString(); conn.Logger.ZLogWarning($"Exceeded flood limit! Issuing ban with duration {banDuration} on {banAddr}/{banUser.UserId}..."); @@ -224,7 +183,7 @@ public class SockChatServer { await Context.BanUser(conn.User, banDuration, UserDisconnectS2CPacket.Reason.Flood); if(banDuration > TimeSpan.Zero) - await BansClient.BanCreate( + await Context.Bans.BanCreate( BanKind.User, banDuration, conn.RemoteEndPoint.Address, diff --git a/SharpChatCommon/Configuration/Config.cs b/SharpChatCommon/Configuration/Config.cs index 068061a..26f49db 100644 --- a/SharpChatCommon/Configuration/Config.cs +++ b/SharpChatCommon/Configuration/Config.cs @@ -1,6 +1,6 @@ namespace SharpChat.Configuration; -public interface Config : IDisposable { +public interface Config { /// <summary> /// Creates a proxy object that forces all names to start with the given prefix. /// </summary> diff --git a/SharpChatCommon/Configuration/ScopedConfig.cs b/SharpChatCommon/Configuration/ScopedConfig.cs index 86b4770..41f2446 100644 --- a/SharpChatCommon/Configuration/ScopedConfig.cs +++ b/SharpChatCommon/Configuration/ScopedConfig.cs @@ -27,8 +27,4 @@ public class ScopedConfig(Config config, string prefix) : Config { public CachedValue<T> ReadCached<T>(string name, T? fallback = default, TimeSpan? lifetime = null) { return Config.ReadCached(GetName(name), fallback, lifetime); } - - public void Dispose() { - GC.SuppressFinalize(this); - } } diff --git a/SharpChatCommon/Configuration/StreamConfig.cs b/SharpChatCommon/Configuration/StreamConfig.cs index 78b389f..ffe8f60 100644 --- a/SharpChatCommon/Configuration/StreamConfig.cs +++ b/SharpChatCommon/Configuration/StreamConfig.cs @@ -2,7 +2,7 @@ using System.Text; namespace SharpChat.Configuration; -public class StreamConfig : Config { +public class StreamConfig : Config, IDisposable { private Stream Stream { get; } private StreamReader StreamReader { get; } private Mutex Lock { get; }