From f643b413f25a83456fa0bd2cda69bb46f53d389b Mon Sep 17 00:00:00 2001 From: Stanley Dimant Date: Thu, 16 Jun 2022 18:55:19 +0200 Subject: [PATCH] add some UI stuff --- MareSynchronos/Configuration.cs | 4 +- .../Factories/FileReplacementFactory.cs | 7 +- MareSynchronos/Hooks/DrawHooks.cs | 6 +- MareSynchronos/Managers/CharacterManager.cs | 28 ++- MareSynchronos/Managers/IpcManager.cs | 83 +++---- MareSynchronos/Plugin.cs | 62 +++--- MareSynchronos/PluginUI.cs | 210 +++++++++++++----- MareSynchronos/WebAPI/ApiController.cs | 61 ++++- 8 files changed, 311 insertions(+), 150 deletions(-) diff --git a/MareSynchronos/Configuration.cs b/MareSynchronos/Configuration.cs index 7cd8790..a833068 100644 --- a/MareSynchronos/Configuration.cs +++ b/MareSynchronos/Configuration.cs @@ -1,6 +1,7 @@ using Dalamud.Configuration; using Dalamud.Plugin; using System; +using System.Collections.Generic; namespace MareSynchronos { @@ -10,8 +11,9 @@ namespace MareSynchronos public int Version { get; set; } = 0; public string CacheFolder { get; set; } = string.Empty; - public string ClientSecret { get; internal set; } = string.Empty; + public Dictionary ClientSecret { get; internal set; } = new(); public string ApiUri { get; internal set; } = string.Empty; + public bool UseCustomService { get; internal set; } // the below exist just to make saving less cumbersome diff --git a/MareSynchronos/Factories/FileReplacementFactory.cs b/MareSynchronos/Factories/FileReplacementFactory.cs index d880898..70a6e62 100644 --- a/MareSynchronos/Factories/FileReplacementFactory.cs +++ b/MareSynchronos/Factories/FileReplacementFactory.cs @@ -19,10 +19,15 @@ namespace MareSynchronos.Factories public FileReplacement Create(string gamePath, bool resolve = true) { + if (!ipcManager.CheckPenumbraAPI()) + { + throw new System.Exception(); + } + var fileReplacement = new FileReplacement(gamePath, ipcManager.PenumbraModDirectory()!); if (!resolve) return fileReplacement; - if(clientState.LocalPlayer != null) + if (clientState.LocalPlayer != null) { playerName = clientState.LocalPlayer.Name.ToString(); } diff --git a/MareSynchronos/Hooks/DrawHooks.cs b/MareSynchronos/Hooks/DrawHooks.cs index 00a7672..8545165 100644 --- a/MareSynchronos/Hooks/DrawHooks.cs +++ b/MareSynchronos/Hooks/DrawHooks.cs @@ -12,6 +12,7 @@ using FFXIVClientStructs.FFXIV.Client.System.Resource; using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Component.GUI; using MareSynchronos.Factories; +using MareSynchronos.Managers; using MareSynchronos.Models; using Penumbra.GameData.ByteString; using Penumbra.Interop.Structs; @@ -20,6 +21,8 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; namespace MareSynchronos.Hooks { @@ -136,7 +139,8 @@ namespace MareSynchronos.Hooks } } } - } catch (Exception ex) + } + catch (Exception ex) { PluginLog.Error(ex, ex.Message); } diff --git a/MareSynchronos/Managers/CharacterManager.cs b/MareSynchronos/Managers/CharacterManager.cs index cbb05a9..1f628c7 100644 --- a/MareSynchronos/Managers/CharacterManager.cs +++ b/MareSynchronos/Managers/CharacterManager.cs @@ -112,7 +112,13 @@ namespace MareSynchronos.Managers // wait one more second just in case Thread.Sleep(1000); - apiController.SendCharacterData(drawHooks.BuildCharacterCache()).RunSynchronously(); + var cache = CreateFullCharacterCache(); + while (!cache.IsCompleted) + { + Task.Delay(50); + } + + _ = apiController.SendCharacterData(cache.Result); }); } @@ -120,12 +126,12 @@ namespace MareSynchronos.Managers public void PrintRequestedResources() => drawHooks.PrintRequestedResources(); - public void DebugJson() + private async Task CreateFullCharacterCache() { var cache = drawHooks.BuildCharacterCache(); cache.SetGlamourerData(ipcManager.GlamourerGetCharacterCustomization()!); cache.JobId = clientState.LocalPlayer!.ClassJob.Id; - Task.Run(async () => + await Task.Run(async () => { while (!cache.IsReady) { @@ -134,10 +140,20 @@ namespace MareSynchronos.Managers var json = JsonConvert.SerializeObject(cache, Formatting.Indented); cache.CacheHash = Crypto.GetHash(json); - - json = JsonConvert.SerializeObject(cache, Formatting.Indented); - PluginLog.Debug(json); }); + + return cache; + } + + public void DebugJson() + { + var cache = CreateFullCharacterCache(); + while (!cache.IsCompleted) + { + Task.Delay(50); + } + + PluginLog.Debug(JsonConvert.SerializeObject(cache.Result, Formatting.Indented)); } } } diff --git a/MareSynchronos/Managers/IpcManager.cs b/MareSynchronos/Managers/IpcManager.cs index 8c6a852..5ccb7ba 100644 --- a/MareSynchronos/Managers/IpcManager.cs +++ b/MareSynchronos/Managers/IpcManager.cs @@ -9,65 +9,55 @@ namespace MareSynchronos.Managers { private readonly DalamudPluginInterface pluginInterface; private ICallGateSubscriber penumbraInit; - private readonly ICallGateSubscriber penumbraDispose; private ICallGateSubscriber? penumbraResolvePath; private ICallGateSubscriber? penumbraResolveModDir; private ICallGateSubscriber? glamourerGetCharacterCustomization; private ICallGateSubscriber? glamourerApplyCharacterCustomization; + private ICallGateSubscriber penumbraApiVersion; + private ICallGateSubscriber glamourerApiVersion; private ICallGateSubscriber? penumbraRedraw; public bool Initialized { get; private set; } = false; - public event EventHandler? IpcManagerInitialized; - public IpcManager(DalamudPluginInterface pi) { pluginInterface = pi; + penumbraInit = pluginInterface.GetIpcSubscriber("Penumbra.Initialized"); - penumbraInit.Subscribe(Initialize); - penumbraDispose = pluginInterface.GetIpcSubscriber("Penumbra.Disposed"); - penumbraDispose.Subscribe(Uninitialize); - } - - private bool CheckPenumbraAPI() - { - try - { - var penumbraApiVersion = pluginInterface.GetIpcSubscriber("Penumbra.ApiVersion").InvokeFunc(); - return penumbraApiVersion >= 4; - } - catch - { - return false; - } - } - - private bool CheckGlamourerAPI() - { - try - { - var glamourerApiVersion = pluginInterface.GetIpcSubscriber("Glamourer.ApiVersion").InvokeFunc(); - return glamourerApiVersion >= 0; - } - catch - { - return false; - } - } - - public void Initialize() - { - if (Initialized) return; - if (!CheckPenumbraAPI()) throw new Exception("Penumbra API is outdated or not available"); - if (!CheckGlamourerAPI()) throw new Exception("Glamourer API is oudated or not available"); penumbraResolvePath = pluginInterface.GetIpcSubscriber("Penumbra.ResolveCharacterPath"); penumbraResolveModDir = pluginInterface.GetIpcSubscriber("Penumbra.GetModDirectory"); penumbraRedraw = pluginInterface.GetIpcSubscriber("Penumbra.RedrawObjectByName"); glamourerGetCharacterCustomization = pluginInterface.GetIpcSubscriber("Glamourer.GetCharacterCustomization"); glamourerApplyCharacterCustomization = pluginInterface.GetIpcSubscriber("Glamourer.ApplyCharacterCustomization"); + penumbraApiVersion = pluginInterface.GetIpcSubscriber("Penumbra.ApiVersion"); + glamourerApiVersion = pluginInterface.GetIpcSubscriber("Glamourer.ApiVersion"); + penumbraInit.Subscribe(() => penumbraRedraw!.InvokeAction("self", 0)); + Initialized = true; - IpcManagerInitialized?.Invoke(this, new EventArgs()); - PluginLog.Debug("[IPC Manager] initialized"); + } + + public bool CheckPenumbraAPI() + { + try + { + return penumbraApiVersion.InvokeFunc() >= 4; + } + catch + { + return false; + } + } + + public bool CheckGlamourerAPI() + { + try + { + return glamourerApiVersion.InvokeFunc() >= 0; + } + catch + { + return false; + } } private void Uninitialize() @@ -82,38 +72,37 @@ namespace MareSynchronos.Managers public string? PenumbraResolvePath(string path, string characterName) { - if (!Initialized) return null; + if (!CheckPenumbraAPI()) return null; return penumbraResolvePath!.InvokeFunc(path, characterName); } public string? PenumbraModDirectory() { - if (!Initialized) return null; + if (!CheckPenumbraAPI()) return null; return penumbraResolveModDir!.InvokeFunc(); } public string? GlamourerGetCharacterCustomization() { - if (!Initialized) return null; + if (!CheckGlamourerAPI()) return null; return glamourerGetCharacterCustomization!.InvokeFunc(); } public void GlamourerApplyCharacterCustomization(string customization, string characterName) { - if (!Initialized) return; + if (!CheckGlamourerAPI()) return; glamourerApplyCharacterCustomization!.InvokeAction(customization, characterName); } public void PenumbraRedraw(string actorName) { - if (!Initialized) return; + if (!CheckPenumbraAPI()) return; penumbraRedraw!.InvokeAction(actorName, 0); } public void Dispose() { Uninitialize(); - IpcManagerInitialized = null; } } } diff --git a/MareSynchronos/Plugin.cs b/MareSynchronos/Plugin.cs index 56fe3ca..bb42eed 100644 --- a/MareSynchronos/Plugin.cs +++ b/MareSynchronos/Plugin.cs @@ -20,6 +20,7 @@ using Newtonsoft.Json; using MareSynchronos.Managers; using LZ4; using MareSynchronos.WebAPI; +using Dalamud.Interface.Windowing; namespace MareSynchronos { @@ -30,9 +31,10 @@ namespace MareSynchronos private readonly Framework framework; private readonly GameGui gameGui; private readonly ObjectTable objectTable; + private readonly WindowSystem windowSystem; private readonly ApiController apiController; private CharacterManager? characterManager; - private IpcManager? ipcManager; + private IpcManager ipcManager; public Plugin(DalamudPluginInterface pluginInterface, CommandManager commandManager, Framework framework, ObjectTable objectTable, ClientState clientState, GameGui gameGui) { @@ -45,10 +47,13 @@ namespace MareSynchronos Configuration = this.PluginInterface.GetPluginConfig() as Configuration ?? new Configuration(); Configuration.Initialize(this.PluginInterface); + windowSystem = new WindowSystem("MareSynchronos"); + apiController = new ApiController(Configuration); + ipcManager = new IpcManager(PluginInterface); // you might normally want to embed resources and load them from the manifest stream - this.PluginUi = new PluginUI(this.Configuration); + this.PluginUi = new PluginUI(this.Configuration, windowSystem, apiController, ipcManager); new FileCacheContext().Dispose(); // make sure db is initialized I guess @@ -59,9 +64,6 @@ namespace MareSynchronos { ClientState_Login(null, null!); } - - this.PluginInterface.UiBuilder.Draw += DrawUI; - this.PluginInterface.UiBuilder.OpenConfigUi += DrawConfigUI; } public string Name => "Mare Synchronos"; @@ -77,14 +79,28 @@ namespace MareSynchronos clientState.Logout -= ClientState_Logout; ipcManager?.Dispose(); characterManager?.Dispose(); + apiController?.Dispose(); } private void ClientState_Login(object? sender, EventArgs e) { PluginLog.Debug("Client login"); - ipcManager = new IpcManager(PluginInterface); - ipcManager.IpcManagerInitialized += IpcManager_IpcManagerInitialized; - ipcManager.Initialize(); + + Task.Run(async () => + { + while (clientState.LocalPlayer == null) + { + await Task.Delay(50); + } + + characterManager = new CharacterManager( + new DrawHooks(PluginInterface, clientState, objectTable, new FileReplacementFactory(ipcManager, clientState), gameGui), + clientState, framework, apiController, objectTable, ipcManager); + ipcManager.PenumbraRedraw(clientState.LocalPlayer!.Name.ToString()); + }); + + PluginInterface.UiBuilder.Draw += Draw; + PluginInterface.UiBuilder.OpenConfigUi += OpenConfigUI; CommandManager.AddHandler(commandName, new CommandInfo(OnCommand) { @@ -96,8 +112,8 @@ namespace MareSynchronos { PluginLog.Debug("Client logout"); characterManager?.Dispose(); - ipcManager?.Dispose(); - ipcManager = null!; + PluginInterface.UiBuilder.Draw -= Draw; + PluginInterface.UiBuilder.OpenConfigUi -= OpenConfigUI; CommandManager.RemoveHandler(commandName); } @@ -139,32 +155,14 @@ namespace MareSynchronos } } - private void DrawConfigUI() + private void Draw() { - this.PluginUi.SettingsVisible = true; + windowSystem.Draw(); } - private void DrawUI() + private void OpenConfigUI() { - this.PluginUi.Draw(); - } - - private void IpcManager_IpcManagerInitialized(object? sender, EventArgs e) - { - PluginLog.Debug("IPC Manager initialized event"); - ipcManager!.IpcManagerInitialized -= IpcManager_IpcManagerInitialized; - Task.Run(async () => - { - while (clientState.LocalPlayer == null) - { - await Task.Delay(500); - } - - characterManager = new CharacterManager( - new DrawHooks(PluginInterface, clientState, objectTable, new FileReplacementFactory(ipcManager, clientState), gameGui), - clientState, framework, apiController, objectTable, ipcManager); - ipcManager.PenumbraRedraw(clientState.LocalPlayer!.Name.ToString()); - }); + this.PluginUi.Toggle(); } private void OnCommand(string command, string args) diff --git a/MareSynchronos/PluginUI.cs b/MareSynchronos/PluginUI.cs index 9655cdb..8fcc2af 100644 --- a/MareSynchronos/PluginUI.cs +++ b/MareSynchronos/PluginUI.cs @@ -1,4 +1,10 @@ -using ImGuiNET; +using Dalamud.Interface; +using Dalamud.Interface.Colors; +using Dalamud.Interface.Windowing; +using Dalamud.Logging; +using ImGuiNET; +using MareSynchronos.Managers; +using MareSynchronos.WebAPI; using System; using System.Numerics; @@ -6,88 +12,182 @@ namespace MareSynchronos { // It is good to have this be disposable in general, in case you ever need it // to do any cleanup - class PluginUI : IDisposable + class PluginUI : Window, IDisposable { private Configuration configuration; - - // this extra bool exists for ImGui, since you can't ref a property - private bool visible = false; - public bool Visible - { - get { return this.visible; } - set { this.visible = value; } - } - - private bool settingsVisible = false; - public bool SettingsVisible - { - get { return this.settingsVisible; } - set { this.settingsVisible = value; } - } + private readonly WindowSystem windowSystem; + private readonly ApiController apiController; + private readonly IpcManager ipcManager; + private string? uid; + private const string mainServer = "Lunae Crescere Incipientis (Central Server EU)"; // passing in the image here just for simplicity - public PluginUI(Configuration configuration) + public PluginUI(Configuration configuration, WindowSystem windowSystem, ApiController apiController, IpcManager ipcManager) : base("Mare Synchronos Settings", ImGuiWindowFlags.None) { + SizeConstraints = new WindowSizeConstraints() + { + MinimumSize = new(700, 400), + MaximumSize = new(700, 2000) + }; + this.configuration = configuration; + this.windowSystem = windowSystem; + this.apiController = apiController; + this.ipcManager = ipcManager; + windowSystem.AddWindow(this); } public void Dispose() { + windowSystem.RemoveWindow(this); } - public void Draw() + public override void Draw() { - // This is our only draw handler attached to UIBuilder, so it needs to be - // able to draw any windows we might have open. - // Each method checks its own visibility/state to ensure it only draws when - // it actually makes sense. - // There are other ways to do this, but it is generally best to keep the number of - // draw delegates as low as possible. - - DrawMainWindow(); - DrawSettingsWindow(); - } - - public void DrawMainWindow() - { - if (!Visible) + if (!IsOpen) { return; } - ImGui.SetNextWindowSize(new Vector2(375, 330), ImGuiCond.FirstUseEver); - ImGui.SetNextWindowSizeConstraints(new Vector2(375, 330), new Vector2(float.MaxValue, float.MaxValue)); - if (ImGui.Begin("My Amazing Window", ref this.visible, ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse)) + if (string.IsNullOrEmpty(apiController.SecretKey)) { - if (ImGui.Button("Show Settings")) + DrawIntroContent(); + } + else + { + if (!OtherPluginStateOk()) return; + + DrawSettingsContent(); + } + } + + private void DrawSettingsContent() + { + PrintServerState(); + ImGui.Separator(); + ImGui.Text("Your UID"); + ImGui.SameLine(); + ImGui.TextColored(ImGuiColors.ParsedGreen, apiController.UID); + ImGui.SameLine(); + if (ImGui.Button("Copy UID")) + { + ImGui.SetClipboardText(apiController.UID); + } + ImGui.Text("Share this UID to other Mare users so they can add you to their whitelist."); + } + + private int serverSelectionIndex = 0; + + private async void DrawIntroContent() + { + ImGui.SetWindowFontScale(1.3f); + ImGui.Text("Welcome to Mare Synchronos!"); + ImGui.SetWindowFontScale(1.0f); + ImGui.Separator(); + ImGui.TextWrapped("Mare Synchronos is a plugin that will replicate your full current character state including all Penumbra mods to other whitelisted Mare Synchronos users. " + + "Note that you will have to have Penumbra as well as Glamourer installed to use this plugin."); + if (!OtherPluginStateOk()) return; + + ImGui.SetWindowFontScale(1.5f); + string readThis = "READ THIS CAREFULLY BEFORE REGISTERING"; + var textSize = ImGui.CalcTextSize(readThis); + ImGui.SetCursorPosX(ImGui.GetWindowSize().X / 2 - textSize.X / 2); + ImGui.TextColored(ImGuiColors.DalamudRed, readThis); + ImGui.SetWindowFontScale(1.0f); + + ImGui.TextWrapped("All of the mod files currently active on your character as well as your current character state will be uploaded to the service you registered yourself at automatically. " + + "The plugin will exclusively upload the necessary mod files and not the whole mod."); + ImGui.TextWrapped("If you are on a data capped internet connection, higher fees due to data usage depending on the amount of downloaded and uploaded mod files might occur. " + + "Mod files will be compressed on up- and download to save on bandwidth usage. Due to varying up- and download speeds, changes in characters might not be visible immediately. " + + "Files present on the service that already represent your active mod files will not be uploaded again. To register at a service you will need to hold ctrl."); + ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudRed); + ImGui.TextWrapped("The mod files you are uploading are confidential and will not be distributed to parties other than the ones who are requesting the exact same mod files. " + + "Please think about who you are going to whitelist since it is unavoidable that they will receive and locally cache the necessary mod files that you have currently in use. " + + "Locally cached mod files will have arbitrary file names to discourage attempts at replicating the original mod in question."); + ImGui.PopStyleColor(); + ImGui.TextWrapped("Mod files that are saved on the service will remain on the service as long as there are requests for the files from clients. After a period of not being used, the mod files will be automatically deleted. " + + "You will also be able to wipe all the files you have personally uploaded on request."); + ImGui.TextColored(ImGuiColors.DalamudRed, "This service is provided as-is."); + ImGui.Separator(); + string[] comboEntries = new[] { mainServer, "Custom Service" }; + if (ImGui.BeginCombo("Service", comboEntries[serverSelectionIndex])) + { + for (int n = 0; n < comboEntries.Length; n++) { - SettingsVisible = true; + bool isSelected = serverSelectionIndex == n; + if (ImGui.Selectable(comboEntries[n], isSelected)) + { + serverSelectionIndex = n; + } + + if (isSelected) + { + ImGui.SetItemDefaultFocus(); + } + + bool useCustomService = (serverSelectionIndex != 0); + + if (apiController.UseCustomService != useCustomService) + { + PluginLog.Debug("Configuration " + apiController.UseCustomService + " changing to " + useCustomService); + apiController.UseCustomService = useCustomService; + configuration.Save(); + } } - ImGui.Spacing(); + ImGui.EndCombo(); + } + + if (apiController.UseCustomService) + { + string serviceAddress = configuration.ApiUri; + ImGui.InputText("Service address", ref serviceAddress, 255); + configuration.ApiUri = serviceAddress; + configuration.Save(); + } + + PrintServerState(); + if (apiController.IsConnected) + { + if (ImGui.Button("Register")) + { + if (ImGui.GetIO().KeyCtrl) + { + await apiController.Register(); + } + } } - ImGui.End(); } - public void DrawSettingsWindow() + private bool OtherPluginStateOk() { - if (!SettingsVisible) + var penumbraExists = ipcManager.CheckPenumbraAPI(); + var glamourerExists = ipcManager.CheckGlamourerAPI(); + + var penumbraColor = penumbraExists ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudRed; + var glamourerColor = glamourerExists ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudRed; + ImGui.Text("Penumbra:"); + ImGui.SameLine(); + ImGui.TextColored(penumbraColor, penumbraExists ? "Available" : "Unavailable"); + ImGui.Text("Glamourer:"); + ImGui.SameLine(); + ImGui.TextColored(glamourerColor, glamourerExists ? "Available" : "Unavailable"); + + if (!penumbraExists || !glamourerExists) { - return; + ImGui.TextColored(ImGuiColors.DalamudRed, "You need to install both Penumbra and Glamourer and keep them up to date to use Mare Synchronos."); + return false; } - ImGui.SetNextWindowSize(new Vector2(500, 75), ImGuiCond.Always); - if (ImGui.Begin("QUALITY UI DEVELOPMENT", ref this.settingsVisible, - ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse)) - { - // can't ref a property, so use a local copy - string penumbraFolder = configuration.CacheFolder; - if(ImGui.InputText("Penumbra mod folder", ref penumbraFolder, 255)) { - this.configuration.CacheFolder = penumbraFolder; - this.configuration.Save(); - } - } - ImGui.End(); + return true; + } + + private void PrintServerState() + { + ImGui.Text("Service status of " + (string.IsNullOrEmpty(configuration.ApiUri) ? mainServer : configuration.ApiUri)); + ImGui.SameLine(); + var color = apiController.IsConnected ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudRed; + ImGui.TextColored(color, apiController.IsConnected ? "Available" : "Unavailable"); } } } diff --git a/MareSynchronos/WebAPI/ApiController.cs b/MareSynchronos/WebAPI/ApiController.cs index aae072d..e44c0ff 100644 --- a/MareSynchronos/WebAPI/ApiController.cs +++ b/MareSynchronos/WebAPI/ApiController.cs @@ -5,32 +5,74 @@ using System; using System.Collections.Generic; using System.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; namespace MareSynchronos.WebAPI { - public class ApiController + public class ApiController : IDisposable { private readonly Configuration pluginConfiguration; - - private string SecretKey => pluginConfiguration.ClientSecret; + private const string mainService = "https://localhost:6591"; + public string UID { get; private set; } = string.Empty; + public string SecretKey => pluginConfiguration.ClientSecret.ContainsKey(ApiUri) ? pluginConfiguration.ClientSecret[ApiUri] : string.Empty; private string CacheFolder => pluginConfiguration.CacheFolder; - private string ApiUri => pluginConfiguration.ApiUri; + public bool UseCustomService + { + get => pluginConfiguration.UseCustomService; + set + { + pluginConfiguration.UseCustomService = value; + _ = Heartbeat(); + pluginConfiguration.Save(); + } + } + private string ApiUri => UseCustomService ? pluginConfiguration.ApiUri : mainService; + + public bool IsConnected { get; set; } + + Task heartbeatTask; + CancellationTokenSource cts; public ApiController(Configuration pluginConfiguration) { this.pluginConfiguration = pluginConfiguration; + cts = new CancellationTokenSource(); + + heartbeatTask = Task.Run(async () => + { + PluginLog.Debug("Starting heartbeat to " + ApiUri); + while (true && !cts.IsCancellationRequested) + { + await Heartbeat(); + await Task.Delay(TimeSpan.FromSeconds(15), cts.Token); + } + PluginLog.Debug("Stopping heartbeat"); + }, cts.Token); } public async Task Heartbeat() { - PluginLog.Debug("Sending heartbeat to " + ApiUri); + try + { + PluginLog.Debug("Sending heartbeat to " + ApiUri); + if (ApiUri != mainService) throw new Exception(); + IsConnected = true; + } + catch + { + IsConnected = false; + } } - public async Task<(string, string)> Register() + public async Task Register() { PluginLog.Debug("Registering at service " + ApiUri); - return (string.Empty, string.Empty); + var response = ("RandomSecretKey", "RandomUID"); + pluginConfiguration.ClientSecret[ApiUri] = response.Item1; + UID = response.Item2; + PluginLog.Debug(pluginConfiguration.ClientSecret[ApiUri]); + // pluginConfiguration.Save(); } public async Task UploadFile(string filePath) @@ -73,5 +115,10 @@ namespace MareSynchronos.WebAPI List whitelist = new(); return whitelist; } + + public void Dispose() + { + cts?.Cancel(); + } } }