- Restructure imports to follow a more core architurcte

This commit is contained in:
Infi
2025-10-01 21:11:14 +02:00
parent d7326896b1
commit ad77299e9e
4 changed files with 80 additions and 83 deletions
+13 -12
View File
@@ -5,23 +5,24 @@ namespace ChatTwo.Http;
public class HostContext public class HostContext
{ {
private readonly Plugin Plugin; public readonly ServerCore Core;
public bool IsActive; public bool IsActive;
public bool IsStopping; public bool IsStopping;
internal WebserverLite Host; // Initialized at webserver start
internal Processing Processing; public WebserverLite Host = null!;
internal RouteController RouteController; public Processing Processing = null!;
public RouteController RouteController = null!;
internal readonly List<SSEConnection> EventConnections = []; public readonly List<SSEConnection> EventConnections = [];
internal readonly CancellationTokenSource TokenSource = new(); public readonly CancellationTokenSource TokenSource = new();
internal readonly string StaticDir = Path.Combine(Plugin.Interface.AssemblyLocation.DirectoryName!, "Http/Frontend/build"); public readonly string StaticDir = Path.Combine(Plugin.Interface.AssemblyLocation.DirectoryName!, "Http/Frontend/build");
public HostContext(Plugin plugin) public HostContext(ServerCore core)
{ {
Plugin = plugin; Core = core;
} }
public bool Start() public bool Start()
@@ -30,8 +31,8 @@ public class HostContext
{ {
Host = new WebserverLite(new WebserverSettings("*", Plugin.Config.WebinterfacePort), DefaultRoute); Host = new WebserverLite(new WebserverSettings("*", Plugin.Config.WebinterfacePort), DefaultRoute);
Processing = new Processing(Plugin); Processing = new Processing(this);
RouteController = new RouteController(Plugin, this); RouteController = new RouteController(this);
Host.Routes.PreAuthentication.Content.BaseDirectory = StaticDir; Host.Routes.PreAuthentication.Content.BaseDirectory = StaticDir;
Host.Routes.AuthenticateRequest = CheckAuthenticationCookie; Host.Routes.AuthenticateRequest = CheckAuthenticationCookie;
@@ -82,7 +83,7 @@ public class HostContext
Host.Stop(); Host.Stop();
// Save our session tokens // Save our session tokens
Plugin.SaveConfig(); Core.Plugin.SaveConfig();
// We get a copy, so that the original can be cleaned up successfully // We get a copy, so that the original can be cleaned up successfully
foreach (var eventServer in EventConnections.ToArray()) foreach (var eventServer in EventConnections.ToArray())
+16 -34
View File
@@ -8,22 +8,22 @@ namespace ChatTwo.Http;
public class Processing public class Processing
{ {
private readonly Plugin Plugin; private readonly HostContext HostContext;
public Processing(Plugin plugin) public Processing(HostContext hostContext)
{ {
Plugin = plugin; HostContext = hostContext;
} }
internal (MessageTemplate[] Name, bool Locked) ReadChannelName(Chunk[] channelName) internal (MessageTemplate[] Name, bool Locked) ReadChannelName(Chunk[] channelName)
{ {
var locked = Plugin.CurrentTab is not { Channel: null }; var locked = HostContext.Core.Plugin.CurrentTab is not { Channel: null };
return (channelName.Select(ProcessChunk).ToArray(), locked); return (channelName.Select(ProcessChunk).ToArray(), locked);
} }
internal async Task<MessageResponse[]> ReadMessageList() internal async Task<MessageResponse[]> ReadMessageList()
{ {
var tabMessages = await Plugin.CurrentTab.Messages.GetCopy(); var tabMessages = await HostContext.Core.Plugin.CurrentTab.Messages.GetCopy();
return tabMessages.TakeLast(Plugin.Config.WebinterfaceMaxLinesToSend).Select(ReadMessageContent).ToArray(); return tabMessages.TakeLast(Plugin.Config.WebinterfaceMaxLinesToSend).Select(ReadMessageContent).ToArray();
} }
@@ -41,24 +41,6 @@ public class Processing
return response; return response;
} }
internal async Task PrepareNewClient(SSEConnection sse)
{
// This takes long, so keep it outside the next frame
var messages = await GetAllMessages();
// Using the bulk message event to clear everything on the client side that may still exist
await Plugin.Framework.RunOnTick(() =>
{
sse.OutboundQueue.Enqueue(new BulkMessagesEvent(messages));
sse.OutboundQueue.Enqueue(new SwitchChannelEvent(GetCurrentChannel()));
sse.OutboundQueue.Enqueue(new ChannelListEvent(GetValidChannels()));
sse.OutboundQueue.Enqueue(new ChatTabSwitchedEvent(GetCurrentTab()));
sse.OutboundQueue.Enqueue(new ChatTabListEvent(GetAllTabs()));
});
}
private MessageTemplate ProcessChunk(Chunk chunk) private MessageTemplate ProcessChunk(Chunk chunk)
{ {
if (chunk is IconChunk { } icon) if (chunk is IconChunk { } icon)
@@ -87,12 +69,12 @@ public class Processing
color ??= 0; color ??= 0;
var userContent = text.Content ?? string.Empty; var userContent = text.Content ?? string.Empty;
if (Plugin.ChatLogWindow.ScreenshotMode) if (HostContext.Core.Plugin.ChatLogWindow.ScreenshotMode)
{ {
if (chunk.Link is PlayerPayload playerPayload) if (chunk.Link is PlayerPayload playerPayload)
userContent = Plugin.ChatLogWindow.HidePlayerInString(userContent, playerPayload.PlayerName, playerPayload.World.RowId); userContent = HostContext.Core.Plugin.ChatLogWindow.HidePlayerInString(userContent, playerPayload.PlayerName, playerPayload.World.RowId);
else if (Plugin.ClientState.LocalPlayer is { } player) else if (Plugin.ClientState.LocalPlayer is { } player)
userContent = Plugin.ChatLogWindow.HidePlayerInString(userContent, player.Name.TextValue, player.HomeWorld.RowId); userContent = HostContext.Core.Plugin.ChatLogWindow.HidePlayerInString(userContent, player.Name.TextValue, player.HomeWorld.RowId);
} }
var isNotUrl = text.Link is not UriPayload; var isNotUrl = text.Link is not UriPayload;
@@ -102,30 +84,30 @@ public class Processing
return MessageTemplate.Empty; return MessageTemplate.Empty;
} }
private async Task<Messages> GetAllMessages() public async Task<Messages> GetAllMessages()
{ {
var messages = await WebserverUtil.FrameworkWrapper(ReadMessageList); var messages = await WebserverUtil.FrameworkWrapper(ReadMessageList);
return new Messages(messages); return new Messages(messages);
} }
private SwitchChannel GetCurrentChannel() public SwitchChannel GetCurrentChannel()
{ {
var channel = ReadChannelName(Plugin.ChatLogWindow.PreviousChannel); var channel = ReadChannelName(HostContext.Core.Plugin.ChatLogWindow.PreviousChannel);
return new SwitchChannel(channel); return new SwitchChannel(channel);
} }
private ChannelList GetValidChannels() public ChannelList GetValidChannels()
{ {
var channels = Plugin.ChatLogWindow.GetValidChannels(); var channels = HostContext.Core.Plugin.ChatLogWindow.GetValidChannels();
return new ChannelList(channels.ToDictionary(pair => pair.Key, pair => (uint)pair.Value)); return new ChannelList(channels.ToDictionary(pair => pair.Key, pair => (uint)pair.Value));
} }
private ChatTab GetCurrentTab() public ChatTab GetCurrentTab()
{ {
return new ChatTab(Plugin.CurrentTab.Name, Plugin.LastTab); return new ChatTab(HostContext.Core.Plugin.CurrentTab.Name, HostContext.Core.Plugin.LastTab);
} }
private ChatTabList GetAllTabs() public ChatTabList GetAllTabs()
{ {
var tabs = Plugin.Config.Tabs.Select((tab, idx) => new ChatTab(tab.Name, idx)).ToArray(); var tabs = Plugin.Config.Tabs.Select((tab, idx) => new ChatTab(tab.Name, idx)).ToArray();
return new ChatTabList(tabs); return new ChatTabList(tabs);
+28 -30
View File
@@ -12,8 +12,7 @@ namespace ChatTwo.Http;
public class RouteController public class RouteController
{ {
private readonly Plugin Plugin; private readonly HostContext HostContext;
private readonly HostContext Core;
private readonly string AuthTemplate; private readonly string AuthTemplate;
private readonly string ChatBoxTemplate; private readonly string ChatBoxTemplate;
@@ -25,35 +24,34 @@ public class RouteController
Error = delegate(object? _, ErrorEventArgs args) { args.ErrorContext.Handled = true; } Error = delegate(object? _, ErrorEventArgs args) { args.ErrorContext.Handled = true; }
}; };
public RouteController(Plugin plugin, HostContext core) public RouteController(HostContext hostContext)
{ {
Plugin = plugin; HostContext = hostContext;
Core = core;
AuthTemplate = File.ReadAllText(Path.Combine(Core.StaticDir, "index.html")); AuthTemplate = File.ReadAllText(Path.Combine(HostContext.StaticDir, "index.html"));
ChatBoxTemplate = File.ReadAllText(Path.Combine(Core.StaticDir, "chat.html")); ChatBoxTemplate = File.ReadAllText(Path.Combine(HostContext.StaticDir, "chat.html"));
// Pre Auth // Pre Auth
Core.Host.Routes.PreAuthentication.Static.Add(HttpMethod.GET, "/", AuthRoute, ExceptionRoute); HostContext.Host.Routes.PreAuthentication.Static.Add(HttpMethod.GET, "/", AuthRoute, ExceptionRoute);
Core.Host.Routes.PreAuthentication.Static.Add(HttpMethod.POST, "/auth", AuthenticateClient, ExceptionRoute); HostContext.Host.Routes.PreAuthentication.Static.Add(HttpMethod.POST, "/auth", AuthenticateClient, ExceptionRoute);
Core.Host.Routes.PreAuthentication.Static.Add(HttpMethod.GET, "/files/gfdata.gfd", GetGfdData, ExceptionRoute); HostContext.Host.Routes.PreAuthentication.Static.Add(HttpMethod.GET, "/files/gfdata.gfd", GetGfdData, ExceptionRoute);
Core.Host.Routes.PreAuthentication.Static.Add(HttpMethod.GET, "/files/fonticon_ps5.tex", GetTexData, ExceptionRoute); HostContext.Host.Routes.PreAuthentication.Static.Add(HttpMethod.GET, "/files/fonticon_ps5.tex", GetTexData, ExceptionRoute);
Core.Host.Routes.PreAuthentication.Static.Add(HttpMethod.GET, "/files/FFXIV_Lodestone_SSF.ttf", GetLodestoneFont, ExceptionRoute); HostContext.Host.Routes.PreAuthentication.Static.Add(HttpMethod.GET, "/files/FFXIV_Lodestone_SSF.ttf", GetLodestoneFont, ExceptionRoute);
Core.Host.Routes.PreAuthentication.Static.Add(HttpMethod.GET, "/favicon.ico", GetFavicon, ExceptionRoute); HostContext.Host.Routes.PreAuthentication.Static.Add(HttpMethod.GET, "/favicon.ico", GetFavicon, ExceptionRoute);
Core.Host.Routes.PreAuthentication.Parameter.Add(HttpMethod.GET, "/emote/{name}", GetEmote, ExceptionRoute); HostContext.Host.Routes.PreAuthentication.Parameter.Add(HttpMethod.GET, "/emote/{name}", GetEmote, ExceptionRoute);
// Post Auth // Post Auth
Core.Host.Routes.PostAuthentication.Static.Add(HttpMethod.GET, "/chat", ChatBoxRoute, ExceptionRoute); HostContext.Host.Routes.PostAuthentication.Static.Add(HttpMethod.GET, "/chat", ChatBoxRoute, ExceptionRoute);
Core.Host.Routes.PostAuthentication.Static.Add(HttpMethod.POST, "/send", ReceiveMessage, ExceptionRoute); HostContext.Host.Routes.PostAuthentication.Static.Add(HttpMethod.POST, "/send", ReceiveMessage, ExceptionRoute);
Core.Host.Routes.PostAuthentication.Static.Add(HttpMethod.POST, "/channel", ReceiveChannelSwitch, ExceptionRoute); HostContext.Host.Routes.PostAuthentication.Static.Add(HttpMethod.POST, "/channel", ReceiveChannelSwitch, ExceptionRoute);
Core.Host.Routes.PostAuthentication.Static.Add(HttpMethod.POST, "/tab", ReceiveTabSwitch, ExceptionRoute); HostContext.Host.Routes.PostAuthentication.Static.Add(HttpMethod.POST, "/tab", ReceiveTabSwitch, ExceptionRoute);
// Ship all other static files dynamically // Ship all other static files dynamically
Core.Host.Routes.PreAuthentication.Content.Add("/_app/", true, ExceptionRoute); HostContext.Host.Routes.PreAuthentication.Content.Add("/_app/", true, ExceptionRoute);
Core.Host.Routes.PreAuthentication.Content.Add("/static/", true, ExceptionRoute); HostContext.Host.Routes.PreAuthentication.Content.Add("/static/", true, ExceptionRoute);
// Server-Sent Events Route // Server-Sent Events Route
Core.Host.Routes.PostAuthentication.Static.Add(HttpMethod.POST, "/sse", NewSSEConnection, ExceptionRoute); HostContext.Host.Routes.PostAuthentication.Static.Add(HttpMethod.POST, "/sse", NewSSEConnection, ExceptionRoute);
} }
private async Task ExceptionRoute(HttpContextBase ctx, Exception _) private async Task ExceptionRoute(HttpContextBase ctx, Exception _)
@@ -97,7 +95,7 @@ public class RouteController
private async Task GetLodestoneFont(HttpContextBase ctx) private async Task GetLodestoneFont(HttpContextBase ctx)
{ {
var data = Plugin.FontManager.GameSymFont; var data = HostContext.Core.Plugin.FontManager.GameSymFont;
await ctx.Response.Send(data); await ctx.Response.Send(data);
} }
@@ -185,8 +183,8 @@ public class RouteController
await Plugin.Framework.RunOnFrameworkThread(() => await Plugin.Framework.RunOnFrameworkThread(() =>
{ {
Plugin.ChatLogWindow.Chat = content.Message; HostContext.Core.Plugin.ChatLogWindow.Chat = content.Message;
Plugin.ChatLogWindow.SendChatBox(Plugin.CurrentTab); HostContext.Core.Plugin.ChatLogWindow.SendChatBox(HostContext.Core.Plugin.CurrentTab);
}); });
ctx.Response.StatusCode = 201; ctx.Response.StatusCode = 201;
@@ -206,7 +204,7 @@ public class RouteController
return; return;
} }
await Plugin.Framework.RunOnFrameworkThread(() => { Plugin.ChatLogWindow.SetChannel(channel.Channel); }); await Plugin.Framework.RunOnFrameworkThread(() => { HostContext.Core.Plugin.ChatLogWindow.SetChannel(channel.Channel); });
ctx.Response.StatusCode = 201; ctx.Response.StatusCode = 201;
await ctx.Response.Send(JsonConvert.SerializeObject(new OkResponse("Channel switch was initiated."))); await ctx.Response.Send(JsonConvert.SerializeObject(new OkResponse("Channel switch was initiated.")));
@@ -225,7 +223,7 @@ public class RouteController
return; return;
} }
await Plugin.Framework.RunOnFrameworkThread(() => { Plugin.WantedTab = tab.Index; }); await Plugin.Framework.RunOnFrameworkThread(() => { HostContext.Core.Plugin.WantedTab = tab.Index; });
ctx.Response.StatusCode = 201; ctx.Response.StatusCode = 201;
await ctx.Response.Send(JsonConvert.SerializeObject(new OkResponse("Tab switch was initiated."))); await ctx.Response.Send(JsonConvert.SerializeObject(new OkResponse("Tab switch was initiated.")));
@@ -237,15 +235,15 @@ public class RouteController
{ {
Plugin.Log.Information($"Client connected: {ctx.Guid}"); Plugin.Log.Information($"Client connected: {ctx.Guid}");
var sse = new SSEConnection(Core.TokenSource.Token); var sse = new SSEConnection(HostContext.TokenSource.Token);
await Core.Processing.PrepareNewClient(sse); await HostContext.Core.PrepareNewClient(sse);
Core.EventConnections.Add(sse); HostContext.EventConnections.Add(sse);
await sse.HandleEventLoop(ctx); await sse.HandleEventLoop(ctx);
// It should always be done after return // It should always be done after return
if (sse.Done) if (sse.Done)
Core.EventConnections.Remove(sse); HostContext.EventConnections.Remove(sse);
} }
catch (Exception ex) catch (Exception ex)
{ {
+23 -7
View File
@@ -1,20 +1,37 @@
using ChatTwo.Code; using ChatTwo.Http.MessageProtocol;
using ChatTwo.Http.MessageProtocol;
namespace ChatTwo.Http; namespace ChatTwo.Http;
public class ServerCore : IAsyncDisposable public class ServerCore : IAsyncDisposable
{ {
private readonly Plugin Plugin; public readonly Plugin Plugin;
private readonly HostContext HostContext; private readonly HostContext HostContext;
public ServerCore(Plugin plugin) public ServerCore(Plugin plugin)
{ {
Plugin = plugin; Plugin = plugin;
HostContext = new HostContext(plugin); HostContext = new HostContext(this);
} }
#region SSE Helper #region SSE Helper
internal async Task PrepareNewClient(SSEConnection sse)
{
// This takes long, so keep it outside the next frame
var messages = await HostContext.Processing.GetAllMessages();
// Using the bulk message event to clear everything on the client side that may still exist
await Plugin.Framework.RunOnTick(() =>
{
sse.OutboundQueue.Enqueue(new BulkMessagesEvent(messages));
sse.OutboundQueue.Enqueue(new SwitchChannelEvent(HostContext.Processing.GetCurrentChannel()));
sse.OutboundQueue.Enqueue(new ChannelListEvent(HostContext.Processing.GetValidChannels()));
sse.OutboundQueue.Enqueue(new ChatTabSwitchedEvent(HostContext.Processing.GetCurrentTab()));
sse.OutboundQueue.Enqueue(new ChatTabListEvent(HostContext.Processing.GetAllTabs()));
});
}
internal void SendNewMessage(Message message) internal void SendNewMessage(Message message)
{ {
if (!HostContext.IsActive) if (!HostContext.IsActive)
@@ -83,8 +100,7 @@ public class ServerCore : IAsyncDisposable
{ {
Plugin.Framework.RunOnTick(() => Plugin.Framework.RunOnTick(() =>
{ {
var channels = Plugin.ChatLogWindow.GetValidChannels(); var bundledResponse = new ChannelListEvent(HostContext.Processing.GetValidChannels());
var bundledResponse = new ChannelListEvent(new ChannelList(channels.ToDictionary(pair => pair.Key, pair => (uint)pair.Value)));
foreach (var eventServer in HostContext.EventConnections) foreach (var eventServer in HostContext.EventConnections)
eventServer.OutboundQueue.Enqueue(bundledResponse); eventServer.OutboundQueue.Enqueue(bundledResponse);
}); });
@@ -105,7 +121,7 @@ public class ServerCore : IAsyncDisposable
Plugin.Framework.RunOnTick(async () => Plugin.Framework.RunOnTick(async () =>
{ {
foreach (var eventServer in HostContext.EventConnections) foreach (var eventServer in HostContext.EventConnections)
await HostContext.Processing.PrepareNewClient(eventServer); await HostContext.Core.PrepareNewClient(eventServer);
}); });
} }
catch (Exception ex) catch (Exception ex)