- Restructure imports to follow a more core architurcte
This commit is contained in:
+13
-12
@@ -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
@@ -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);
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user