diff --git a/MareAPI b/MareAPI index 6645eaf..d361cfa 160000 --- a/MareAPI +++ b/MareAPI @@ -1 +1 @@ -Subproject commit 6645eaf63fe7c44669f0d62ab95003bcf1d3d04d +Subproject commit d361cfa3b983e8772c2bd08b3d638542ed57cd0f diff --git a/MareSynchronos/MareSynchronos.csproj b/MareSynchronos/MareSynchronos.csproj index f70f4d6..5cb559f 100644 --- a/MareSynchronos/MareSynchronos.csproj +++ b/MareSynchronos/MareSynchronos.csproj @@ -3,7 +3,7 @@ - 0.5.21 + 0.5.22 https://github.com/Penumbra-Sync/client diff --git a/MareSynchronos/Plugin.cs b/MareSynchronos/Plugin.cs index 61eb097..95962c8 100644 --- a/MareSynchronos/Plugin.cs +++ b/MareSynchronos/Plugin.cs @@ -14,7 +14,7 @@ using MareSynchronos.UI; using MareSynchronos.Utils; using Dalamud.Game.ClientState.Conditions; using MareSynchronos.FileCache; -using Dalamud.Logging; +using Dalamud.Game.Gui; namespace MareSynchronos; @@ -23,6 +23,7 @@ public sealed class Plugin : IDalamudPlugin private const string CommandName = "/mare"; private readonly ApiController _apiController; private readonly CommandManager _commandManager; + private readonly ChatGui _chatGui; private readonly Configuration _configuration; private readonly PeriodicFileScanner _periodicFileScanner; private readonly IntroUi _introUi; @@ -44,7 +45,8 @@ public sealed class Plugin : IDalamudPlugin public Plugin(DalamudPluginInterface pluginInterface, CommandManager commandManager, - Framework framework, ObjectTable objectTable, ClientState clientState, Condition condition) + Framework framework, ObjectTable objectTable, ClientState clientState, Condition condition, + ChatGui chatGui) { Logger.Debug("Launching " + Name); _pluginInterface = pluginInterface; @@ -59,7 +61,7 @@ public sealed class Plugin : IDalamudPlugin _windowSystem = new WindowSystem("MareSynchronos"); // those can be initialized outside of game login - _dalamudUtil = new DalamudUtil(clientState, objectTable, framework, condition); + _dalamudUtil = new DalamudUtil(clientState, objectTable, framework, condition, chatGui); _ipcManager = new IpcManager(_pluginInterface, _dalamudUtil); _fileDialogManager = new FileDialogManager(); diff --git a/MareSynchronos/UI/CompactUI.cs b/MareSynchronos/UI/CompactUI.cs index 6169094..75bec50 100644 --- a/MareSynchronos/UI/CompactUI.cs +++ b/MareSynchronos/UI/CompactUI.cs @@ -494,7 +494,7 @@ public class CompactUi : Window, IDisposable ImGui.AlignTextToFramePadding(); ImGui.TextColored(ImGuiColors.DalamudRed, "Not connected to any server"); } - + if (printShard) { ImGui.SetCursorPosY(ImGui.GetCursorPosY() - ImGui.GetStyle().ItemSpacing.Y); @@ -631,7 +631,7 @@ public class CompactUi : Window, IDisposable return _apiController.ServerState switch { ServerState.Disconnected => "You are currently disconnected from the Mare Synchronos server.", - ServerState.Unauthorized => "The account associated with your used secret key is not present on the server anymore or you are tempbanned. To check the latter disconnect and reconnect in 15 minutes.", + ServerState.Unauthorized => "Server Response: " + _apiController.AuthFailureMessage, ServerState.Offline => "Your selected Mare Synchronos server is currently offline.", ServerState.VersionMisMatch => "Your plugin or the server you are connecting to is out of date. Please update your plugin now. If you already did so, contact the server provider to update their server to the latest version.", diff --git a/MareSynchronos/Utils/DalamudUtil.cs b/MareSynchronos/Utils/DalamudUtil.cs index bcf19e1..6d66c4a 100644 --- a/MareSynchronos/Utils/DalamudUtil.cs +++ b/MareSynchronos/Utils/DalamudUtil.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Reflection.Metadata.Ecma335; using System.Threading; using System.Threading.Tasks; using Dalamud.Game; @@ -9,6 +8,8 @@ using Dalamud.Game.ClientState; using Dalamud.Game.ClientState.Conditions; using Dalamud.Game.ClientState.Objects; using Dalamud.Game.ClientState.Objects.SubKinds; +using Dalamud.Game.Gui; +using Dalamud.Game.Text.SeStringHandling; using FFXIVClientStructs.FFXIV.Client.Game.Character; using GameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject; @@ -29,6 +30,7 @@ public class DalamudUtil : IDisposable private readonly ObjectTable _objectTable; private readonly Framework _framework; private readonly Condition _condition; + private readonly ChatGui _chatGui; public event LogIn? LogIn; public event LogOut? LogOut; @@ -54,12 +56,13 @@ public class DalamudUtil : IDisposable return false; } - public DalamudUtil(ClientState clientState, ObjectTable objectTable, Framework framework, Condition condition) + public DalamudUtil(ClientState clientState, ObjectTable objectTable, Framework framework, Condition condition, ChatGui chatGui) { _clientState = clientState; _objectTable = objectTable; _framework = framework; _condition = condition; + _chatGui = chatGui; _clientState.Login += ClientStateOnLogin; _clientState.Logout += ClientStateOnLogout; _framework.Update += FrameworkOnUpdate; @@ -70,6 +73,24 @@ public class DalamudUtil : IDisposable } } + public void PrintInfoChat(string message) + { + SeStringBuilder se = new SeStringBuilder().AddText("[Mare Synchronos] Info: ").AddItalics(message); + _chatGui.Print(se.BuiltString); + } + + public void PrintWarnChat(string message) + { + SeStringBuilder se = new SeStringBuilder().AddText("[Mare Synchronos] ").AddUiForeground("Warning: " + message, 31).AddUiForegroundOff(); + _chatGui.Print(se.BuiltString); + } + + public void PrintErrorChat(string message) + { + SeStringBuilder se = new SeStringBuilder().AddText("[Mare Synchronos] ").AddUiForeground("Error: ", 534).AddItalicsOn().AddUiForeground(message, 534).AddUiForegroundOff().AddItalicsOff(); + _chatGui.Print(se.BuiltString); + } + private void FrameworkOnUpdate(Framework framework) { if (_condition[ConditionFlag.BetweenAreas] || _condition[ConditionFlag.BetweenAreas51] || IsInGpose) diff --git a/MareSynchronos/Utils/Logger.cs b/MareSynchronos/Utils/Logger.cs index 9384a54..0a819a2 100644 --- a/MareSynchronos/Utils/Logger.cs +++ b/MareSynchronos/Utils/Logger.cs @@ -30,12 +30,24 @@ internal class Logger : ILogger } } + public static void Error(string msg, Exception ex) + { + var caller = new StackTrace().GetFrame(1)?.GetMethod()?.ReflectedType?.Name ?? "Unknown"; + PluginLog.Error($"[{caller}] {msg} {Environment.NewLine} Exception: {ex.Message} {Environment.NewLine} {ex.StackTrace}"); + } + public static void Warn(string msg, Exception ex) { var caller = new StackTrace().GetFrame(1)?.GetMethod()?.ReflectedType?.Name ?? "Unknown"; PluginLog.Warning($"[{caller}] {msg} {Environment.NewLine} Exception: {ex.Message} {Environment.NewLine} {ex.StackTrace}"); } + public static void Error(string msg) + { + var caller = new StackTrace().GetFrame(1)?.GetMethod()?.ReflectedType?.Name ?? "Unknown"; + PluginLog.Error($"[{caller}] {msg}"); + } + public static void Warn(string warn) { var caller = new StackTrace().GetFrame(1)?.GetMethod()?.ReflectedType?.Name ?? "Unknown"; diff --git a/MareSynchronos/WebAPI/ApiController.Functions.Callbacks.cs b/MareSynchronos/WebAPI/ApiController.Functions.Callbacks.cs index 582cae4..d644ec5 100644 --- a/MareSynchronos/WebAPI/ApiController.Functions.Callbacks.cs +++ b/MareSynchronos/WebAPI/ApiController.Functions.Callbacks.cs @@ -78,6 +78,12 @@ public partial class ApiController _mareHub!.On(nameof(Client_AdminUpdateOrAddForbiddenFile), act); } + public void OnReceiveServerMessage(Action act) + { + if (_initialized) return; + _mareHub!.On(nameof(Client_ReceiveServerMessage), act); + } + public Task Client_UserUpdateClientPairs(ClientPairDto dto) { var entry = PairedClients.SingleOrDefault(e => string.Equals(e.OtherUID, dto.OtherUID, System.StringComparison.Ordinal)); @@ -213,4 +219,25 @@ public partial class ApiController return Task.CompletedTask; } + + public Task Client_ReceiveServerMessage(MessageSeverity severity, string message) + { + switch (severity) + { + case MessageSeverity.Error: + Logger.Error(message); + _dalamudUtil.PrintErrorChat(message); + break; + case MessageSeverity.Warning: + Logger.Warn(message); + _dalamudUtil.PrintWarnChat(message); + break; + case MessageSeverity.Information: + Logger.Info(message); + _dalamudUtil.PrintInfoChat(message); + break; + } + + return Task.CompletedTask; + } } diff --git a/MareSynchronos/WebAPI/ApiController.cs b/MareSynchronos/WebAPI/ApiController.cs index fdbb44a..b63cd2d 100644 --- a/MareSynchronos/WebAPI/ApiController.cs +++ b/MareSynchronos/WebAPI/ApiController.cs @@ -12,7 +12,6 @@ using MareSynchronos.FileCache; using MareSynchronos.Utils; using MareSynchronos.WebAPI.Utils; using Microsoft.AspNetCore.Http.Connections; -using Microsoft.AspNetCore.SignalR; using Microsoft.AspNetCore.SignalR.Client; using Microsoft.Extensions.Logging; @@ -20,6 +19,8 @@ namespace MareSynchronos.WebAPI; public delegate void SimpleStringDelegate(string str); +public record JwtCache(string ApiUrl, string CharaIdent, string SecretKey); + public partial class ApiController : IDisposable, IMareHubClient { public const string MainServer = "Lunae Crescere Incipientis (Central Server EU)"; @@ -31,8 +32,8 @@ public partial class ApiController : IDisposable, IMareHubClient private readonly DalamudUtil _dalamudUtil; private readonly FileCacheManager _fileDbManager; private CancellationTokenSource _connectionCancellationTokenSource; - private Dictionary _jwtToken = new(StringComparer.Ordinal); - private KeyValuePair AuthorizationJwtHeader => new("Authorization", "Bearer " + _jwtToken[SecretKey]); + private Dictionary _jwtToken = new(); + private KeyValuePair AuthorizationJwtHeader => new("Authorization", "Bearer " + _jwtToken.GetValueOrDefault(new JwtCache(ApiUri, _dalamudUtil.PlayerNameHashed, SecretKey), string.Empty)); private HubConnection? _mareHub; @@ -41,6 +42,7 @@ public partial class ApiController : IDisposable, IMareHubClient private ConnectionDto? _connectionDto; public ServerInfoDto ServerInfo => _connectionDto?.ServerInfo ?? new ServerInfoDto(); + public string AuthFailureMessage { get; private set; } = string.Empty; public SystemInfoDto SystemInfoDto { get; private set; } = new(); public bool IsModerator => (_connectionDto?.IsAdmin ?? false) || (_connectionDto?.IsModerator ?? false); @@ -167,15 +169,17 @@ public partial class ApiController : IDisposable, IMareHubClient continue; } + AuthFailureMessage = string.Empty; + await StopConnection(token).ConfigureAwait(false); try { Logger.Debug("Building connection"); - if (!_jwtToken.TryGetValue(SecretKey, out var jwtToken) || forceGetToken) + if (!_jwtToken.TryGetValue(new JwtCache(ApiUri, _dalamudUtil.PlayerNameHashed, SecretKey), out var jwtToken) || forceGetToken) { - Logger.Debug("Requesting new JWT token"); + Logger.Debug("Requesting new JWT"); using HttpClient httpClient = new(); var postUri = new Uri(new Uri(ApiUri .Replace("wss://", "https://", StringComparison.OrdinalIgnoreCase) @@ -184,11 +188,13 @@ public partial class ApiController : IDisposable, IMareHubClient var auth = BitConverter.ToString(sha256.ComputeHash(Encoding.UTF8.GetBytes(SecretKey))).Replace("-", "", StringComparison.OrdinalIgnoreCase); var result = await httpClient.PostAsync(postUri, new FormUrlEncodedContent(new[] { - new KeyValuePair("auth", auth) + new KeyValuePair("auth", auth), + new KeyValuePair("charaIdent", _dalamudUtil.PlayerNameHashed) })).ConfigureAwait(false); + AuthFailureMessage = await result.Content.ReadAsStringAsync().ConfigureAwait(false); result.EnsureSuccessStatusCode(); - _jwtToken[SecretKey] = await result.Content.ReadAsStringAsync().ConfigureAwait(false); - Logger.Debug("JWT Token Success"); + _jwtToken[new JwtCache(ApiUri, _dalamudUtil.PlayerNameHashed, SecretKey)] = await result.Content.ReadAsStringAsync().ConfigureAwait(false); + Logger.Debug("JWT Success"); } while (!_dalamudUtil.IsPlayerPresent && !token.IsCancellationRequested) @@ -203,9 +209,10 @@ public partial class ApiController : IDisposable, IMareHubClient await _mareHub.StartAsync(token).ConfigureAwait(false); + OnReceiveServerMessage((sev, msg) => Client_ReceiveServerMessage(sev, msg)); OnUpdateSystemInfo((dto) => Client_UpdateSystemInfo(dto)); - _connectionDto = await Heartbeat(_dalamudUtil.PlayerNameHashed).ConfigureAwait(false); + _connectionDto = await GetConnectionDto().ConfigureAwait(false); ServerState = ServerState.Connected; @@ -225,24 +232,6 @@ public partial class ApiController : IDisposable, IMareHubClient _mareHub.Reconnected += MareHubOnReconnected; } } - catch (HubException ex) - { - if (ex.Message.Contains("unauthorized", StringComparison.OrdinalIgnoreCase)) - { - Logger.Warn(ex.Message); - ServerState = ServerState.Unauthorized; - await StopConnection(token).ConfigureAwait(false); - return; - } - - Logger.Warn(ex.GetType().ToString()); - Logger.Warn(ex.Message); - Logger.Warn(ex.StackTrace ?? string.Empty); - - ServerState = ServerState.RateLimited; - await StopConnection(token).ConfigureAwait(false); - return; - } catch (HttpRequestException ex) { Logger.Warn(ex.GetType().ToString()); @@ -423,6 +412,11 @@ public partial class ApiController : IDisposable, IMareHubClient return await _mareHub!.InvokeAsync(nameof(Heartbeat), characterIdentification).ConfigureAwait(false); } + public async Task GetConnectionDto() + { + return await _mareHub!.InvokeAsync(nameof(GetConnectionDto)).ConfigureAwait(false); + } + public async Task CheckClientHealth() { return await _mareHub!.InvokeAsync(nameof(CheckClientHealth)).ConfigureAwait(false);