@using System.Diagnostics @using ArcaneLibs.Blazor.Components.Services @using ArcaneLibs.Extensions @using Spacebar.Client.WebCore @using Spacebar.Client.WebCore.Client @using Spacebar.Models.Api @using Spacebar.Models.Gateway @using Spacebar.Sdk.Core @inject SessionStore sessionStore @inject SpacebarClientProviderService clientProvider @inject JsConsoleService jsConsole @ChildContent @code { private DebugBanner _dbgBanner = null!; private bool _readyReceived = false; public ClientManager() { ClientAvailable = Task.Run(async () => { while (Client is null) await Task.Delay(50); ClientAvailable = null; }); ClientReady = Task.Run(async () => { while (!_readyReceived) await Task.Delay(50); ClientAvailable = null; }); } public AuthenticatedSpacebarClient? Client { get; set; } public ClientStateContainer ClientState { get; set; } = new(); [Parameter] public required RenderFragment ChildContent { get; set; } public Task? ClientAvailable { get; set; } public Task? ClientReady { get; set; } protected override async Task OnAfterRenderAsync(bool firstRender) { if (!firstRender) return; await _dbgBanner.SetStatus("Preparing for launch!"); await Task.Delay(125); var session = await sessionStore.GetCurrentSessionAsync(); if (session != null) { Client = await clientProvider.GetAuthenticatedClientAsync(session.ServerName, session.AccessToken); await _dbgBanner.SetStatus($"Got authenticated client for {session.ProfileCache.Username}#{session.ProfileCache.Discriminator} on {session.ServerName}! Connecting to gateway..."); Client.Gateway.IdentifyData.ClientProperties = new IdentifyClientProperties() { HasClientMods = false, ApplicationArchitecture = "wasm", }.ToJsonNode().AsObject(); StateHasChanged(); _ = RunInnerLoopAsync(); Client.Gateway.OnceGatewayMessage.Add(async msg => { if (msg is { Opcode: GatewayOpcode.S2CDispatch, DispatchEventType: "READY" }) { await _dbgBanner.SetStatus($"Got READY from gateway, deserializing..."); var content = msg.GetData(); await _dbgBanner.SetStatus($"Deserialized READY from gateway, handling..."); // ClientState.Guilds.AddRange(content.Guilds.ToDictionary(x=>x.Id, x=>x)); foreach (var guild in content.Guilds) { ClientState.Guilds.Add(guild.Id, guild); await _dbgBanner.SetStatus($"Deserialized READY from gateway, handling... guilds ({ClientState.Guilds.Count})"); await Task.Delay(1); } foreach (var guild in content.Relationships) { // ClientState.Relationships.Add(guild.Id, guild); await _dbgBanner.SetStatus($"Deserialized READY from gateway, handling... guilds ({ClientState.Guilds.Count}), relationships (0)"); await Task.Delay(1); } await jsConsole.Info("Parsed ready payload:", new { original = msg.EventData, parsed = content }); await _dbgBanner.SetStatus($"Done handling ready!"); _readyReceived = true; _ = _dbgBanner.SetStatus(null, 1750); return false; } if (msg is { Opcode: GatewayOpcode.S2CDispatch, DispatchEventType: "READY_SUPPLEMENTAL" }) { await _dbgBanner.SetStatus("Received READY_SUPPLEMENTAL..."); await jsConsole.Info("Parsed ready_supplemental payload", new { original = msg.EventData }); _ = _dbgBanner.SetStatus(null, 1750); return true; } return false; }); } else { await _dbgBanner.SetStatus("No session marked as current... :("); await _dbgBanner.SetStatus(null, 1750); } } private async Task RunInnerLoopAsync() { var tries = 1; while (true) { var uptime = Stopwatch.StartNew(); try { int delay = (int)((tries++ * 1.2) * 250); await jsConsole.Info("Starting gateway connection in", delay, "ms."); await Task.Delay(delay); await Client.Gateway.Connect(); await Client.Gateway.Start(); } catch (Exception e) { await Client.Gateway.Disconnect(); await jsConsole.Warn($"[ClientManager] Heartbeat loop exited! ({Client.Gateway.RawClientWebSocket.CloseStatus}: {Client.Gateway.RawClientWebSocket.CloseStatusDescription})"); await jsConsole.Error("Unhandled exception during gateway connection:", e.ToString()); _ = _dbgBanner.SetStatus("Unhandled exception during gateway connection: " + string.Join("\n", e.ToString().Split("\n").Where(x => !x.Trim().StartsWith("at ")))); if (uptime.Elapsed.TotalSeconds < 3) { try { await _dbgBanner.SetStatus("Attempting to validate token..."); await Client.GetCurrentUser(); } catch (Exception ve) { await _dbgBanner.SetStatus(ve.ToString()); await jsConsole.Error(ve.ToString()); if (ve is SpacebarApiException { Code: 40001 }) { await _dbgBanner.SetStatus("Invalid access token, please sign in again!"); return; } } } await Task.Delay(1); } } // ReSharper disable once FunctionNeverReturns } }