add some UI stuff
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
using Dalamud.Configuration;
|
using Dalamud.Configuration;
|
||||||
using Dalamud.Plugin;
|
using Dalamud.Plugin;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace MareSynchronos
|
namespace MareSynchronos
|
||||||
{
|
{
|
||||||
@@ -10,8 +11,9 @@ namespace MareSynchronos
|
|||||||
public int Version { get; set; } = 0;
|
public int Version { get; set; } = 0;
|
||||||
|
|
||||||
public string CacheFolder { get; set; } = string.Empty;
|
public string CacheFolder { get; set; } = string.Empty;
|
||||||
public string ClientSecret { get; internal set; } = string.Empty;
|
public Dictionary<string, string> ClientSecret { get; internal set; } = new();
|
||||||
public string ApiUri { get; internal set; } = string.Empty;
|
public string ApiUri { get; internal set; } = string.Empty;
|
||||||
|
public bool UseCustomService { get; internal set; }
|
||||||
|
|
||||||
// the below exist just to make saving less cumbersome
|
// the below exist just to make saving less cumbersome
|
||||||
|
|
||||||
|
|||||||
@@ -19,10 +19,15 @@ namespace MareSynchronos.Factories
|
|||||||
|
|
||||||
public FileReplacement Create(string gamePath, bool resolve = true)
|
public FileReplacement Create(string gamePath, bool resolve = true)
|
||||||
{
|
{
|
||||||
|
if (!ipcManager.CheckPenumbraAPI())
|
||||||
|
{
|
||||||
|
throw new System.Exception();
|
||||||
|
}
|
||||||
|
|
||||||
var fileReplacement = new FileReplacement(gamePath, ipcManager.PenumbraModDirectory()!);
|
var fileReplacement = new FileReplacement(gamePath, ipcManager.PenumbraModDirectory()!);
|
||||||
if (!resolve) return fileReplacement;
|
if (!resolve) return fileReplacement;
|
||||||
|
|
||||||
if(clientState.LocalPlayer != null)
|
if (clientState.LocalPlayer != null)
|
||||||
{
|
{
|
||||||
playerName = clientState.LocalPlayer.Name.ToString();
|
playerName = clientState.LocalPlayer.Name.ToString();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ using FFXIVClientStructs.FFXIV.Client.System.Resource;
|
|||||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||||
using MareSynchronos.Factories;
|
using MareSynchronos.Factories;
|
||||||
|
using MareSynchronos.Managers;
|
||||||
using MareSynchronos.Models;
|
using MareSynchronos.Models;
|
||||||
using Penumbra.GameData.ByteString;
|
using Penumbra.GameData.ByteString;
|
||||||
using Penumbra.Interop.Structs;
|
using Penumbra.Interop.Structs;
|
||||||
@@ -20,6 +21,8 @@ using System.Collections.Concurrent;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace MareSynchronos.Hooks
|
namespace MareSynchronos.Hooks
|
||||||
{
|
{
|
||||||
@@ -136,7 +139,8 @@ namespace MareSynchronos.Hooks
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception ex)
|
}
|
||||||
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
PluginLog.Error(ex, ex.Message);
|
PluginLog.Error(ex, ex.Message);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,7 +112,13 @@ namespace MareSynchronos.Managers
|
|||||||
// wait one more second just in case
|
// wait one more second just in case
|
||||||
Thread.Sleep(1000);
|
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 PrintRequestedResources() => drawHooks.PrintRequestedResources();
|
||||||
|
|
||||||
public void DebugJson()
|
private async Task<CharacterCache> CreateFullCharacterCache()
|
||||||
{
|
{
|
||||||
var cache = drawHooks.BuildCharacterCache();
|
var cache = drawHooks.BuildCharacterCache();
|
||||||
cache.SetGlamourerData(ipcManager.GlamourerGetCharacterCustomization()!);
|
cache.SetGlamourerData(ipcManager.GlamourerGetCharacterCustomization()!);
|
||||||
cache.JobId = clientState.LocalPlayer!.ClassJob.Id;
|
cache.JobId = clientState.LocalPlayer!.ClassJob.Id;
|
||||||
Task.Run(async () =>
|
await Task.Run(async () =>
|
||||||
{
|
{
|
||||||
while (!cache.IsReady)
|
while (!cache.IsReady)
|
||||||
{
|
{
|
||||||
@@ -134,10 +140,20 @@ namespace MareSynchronos.Managers
|
|||||||
var json = JsonConvert.SerializeObject(cache, Formatting.Indented);
|
var json = JsonConvert.SerializeObject(cache, Formatting.Indented);
|
||||||
|
|
||||||
cache.CacheHash = Crypto.GetHash(json);
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,65 +9,55 @@ namespace MareSynchronos.Managers
|
|||||||
{
|
{
|
||||||
private readonly DalamudPluginInterface pluginInterface;
|
private readonly DalamudPluginInterface pluginInterface;
|
||||||
private ICallGateSubscriber<object> penumbraInit;
|
private ICallGateSubscriber<object> penumbraInit;
|
||||||
private readonly ICallGateSubscriber<object> penumbraDispose;
|
|
||||||
private ICallGateSubscriber<string, string, string>? penumbraResolvePath;
|
private ICallGateSubscriber<string, string, string>? penumbraResolvePath;
|
||||||
private ICallGateSubscriber<string>? penumbraResolveModDir;
|
private ICallGateSubscriber<string>? penumbraResolveModDir;
|
||||||
private ICallGateSubscriber<string>? glamourerGetCharacterCustomization;
|
private ICallGateSubscriber<string>? glamourerGetCharacterCustomization;
|
||||||
private ICallGateSubscriber<string, string, object>? glamourerApplyCharacterCustomization;
|
private ICallGateSubscriber<string, string, object>? glamourerApplyCharacterCustomization;
|
||||||
|
private ICallGateSubscriber<int> penumbraApiVersion;
|
||||||
|
private ICallGateSubscriber<int> glamourerApiVersion;
|
||||||
private ICallGateSubscriber<string, int, object>? penumbraRedraw;
|
private ICallGateSubscriber<string, int, object>? penumbraRedraw;
|
||||||
|
|
||||||
public bool Initialized { get; private set; } = false;
|
public bool Initialized { get; private set; } = false;
|
||||||
|
|
||||||
public event EventHandler? IpcManagerInitialized;
|
|
||||||
|
|
||||||
public IpcManager(DalamudPluginInterface pi)
|
public IpcManager(DalamudPluginInterface pi)
|
||||||
{
|
{
|
||||||
pluginInterface = pi;
|
pluginInterface = pi;
|
||||||
|
|
||||||
penumbraInit = pluginInterface.GetIpcSubscriber<object>("Penumbra.Initialized");
|
penumbraInit = pluginInterface.GetIpcSubscriber<object>("Penumbra.Initialized");
|
||||||
penumbraInit.Subscribe(Initialize);
|
|
||||||
penumbraDispose = pluginInterface.GetIpcSubscriber<object>("Penumbra.Disposed");
|
|
||||||
penumbraDispose.Subscribe(Uninitialize);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool CheckPenumbraAPI()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var penumbraApiVersion = pluginInterface.GetIpcSubscriber<int>("Penumbra.ApiVersion").InvokeFunc();
|
|
||||||
return penumbraApiVersion >= 4;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool CheckGlamourerAPI()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var glamourerApiVersion = pluginInterface.GetIpcSubscriber<int>("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<string, string, string>("Penumbra.ResolveCharacterPath");
|
penumbraResolvePath = pluginInterface.GetIpcSubscriber<string, string, string>("Penumbra.ResolveCharacterPath");
|
||||||
penumbraResolveModDir = pluginInterface.GetIpcSubscriber<string>("Penumbra.GetModDirectory");
|
penumbraResolveModDir = pluginInterface.GetIpcSubscriber<string>("Penumbra.GetModDirectory");
|
||||||
penumbraRedraw = pluginInterface.GetIpcSubscriber<string, int, object>("Penumbra.RedrawObjectByName");
|
penumbraRedraw = pluginInterface.GetIpcSubscriber<string, int, object>("Penumbra.RedrawObjectByName");
|
||||||
glamourerGetCharacterCustomization = pluginInterface.GetIpcSubscriber<string>("Glamourer.GetCharacterCustomization");
|
glamourerGetCharacterCustomization = pluginInterface.GetIpcSubscriber<string>("Glamourer.GetCharacterCustomization");
|
||||||
glamourerApplyCharacterCustomization = pluginInterface.GetIpcSubscriber<string, string, object>("Glamourer.ApplyCharacterCustomization");
|
glamourerApplyCharacterCustomization = pluginInterface.GetIpcSubscriber<string, string, object>("Glamourer.ApplyCharacterCustomization");
|
||||||
|
penumbraApiVersion = pluginInterface.GetIpcSubscriber<int>("Penumbra.ApiVersion");
|
||||||
|
glamourerApiVersion = pluginInterface.GetIpcSubscriber<int>("Glamourer.ApiVersion");
|
||||||
|
penumbraInit.Subscribe(() => penumbraRedraw!.InvokeAction("self", 0));
|
||||||
|
|
||||||
Initialized = true;
|
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()
|
private void Uninitialize()
|
||||||
@@ -82,38 +72,37 @@ namespace MareSynchronos.Managers
|
|||||||
|
|
||||||
public string? PenumbraResolvePath(string path, string characterName)
|
public string? PenumbraResolvePath(string path, string characterName)
|
||||||
{
|
{
|
||||||
if (!Initialized) return null;
|
if (!CheckPenumbraAPI()) return null;
|
||||||
return penumbraResolvePath!.InvokeFunc(path, characterName);
|
return penumbraResolvePath!.InvokeFunc(path, characterName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string? PenumbraModDirectory()
|
public string? PenumbraModDirectory()
|
||||||
{
|
{
|
||||||
if (!Initialized) return null;
|
if (!CheckPenumbraAPI()) return null;
|
||||||
return penumbraResolveModDir!.InvokeFunc();
|
return penumbraResolveModDir!.InvokeFunc();
|
||||||
}
|
}
|
||||||
|
|
||||||
public string? GlamourerGetCharacterCustomization()
|
public string? GlamourerGetCharacterCustomization()
|
||||||
{
|
{
|
||||||
if (!Initialized) return null;
|
if (!CheckGlamourerAPI()) return null;
|
||||||
return glamourerGetCharacterCustomization!.InvokeFunc();
|
return glamourerGetCharacterCustomization!.InvokeFunc();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void GlamourerApplyCharacterCustomization(string customization, string characterName)
|
public void GlamourerApplyCharacterCustomization(string customization, string characterName)
|
||||||
{
|
{
|
||||||
if (!Initialized) return;
|
if (!CheckGlamourerAPI()) return;
|
||||||
glamourerApplyCharacterCustomization!.InvokeAction(customization, characterName);
|
glamourerApplyCharacterCustomization!.InvokeAction(customization, characterName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PenumbraRedraw(string actorName)
|
public void PenumbraRedraw(string actorName)
|
||||||
{
|
{
|
||||||
if (!Initialized) return;
|
if (!CheckPenumbraAPI()) return;
|
||||||
penumbraRedraw!.InvokeAction(actorName, 0);
|
penumbraRedraw!.InvokeAction(actorName, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Uninitialize();
|
Uninitialize();
|
||||||
IpcManagerInitialized = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ using Newtonsoft.Json;
|
|||||||
using MareSynchronos.Managers;
|
using MareSynchronos.Managers;
|
||||||
using LZ4;
|
using LZ4;
|
||||||
using MareSynchronos.WebAPI;
|
using MareSynchronos.WebAPI;
|
||||||
|
using Dalamud.Interface.Windowing;
|
||||||
|
|
||||||
namespace MareSynchronos
|
namespace MareSynchronos
|
||||||
{
|
{
|
||||||
@@ -30,9 +31,10 @@ namespace MareSynchronos
|
|||||||
private readonly Framework framework;
|
private readonly Framework framework;
|
||||||
private readonly GameGui gameGui;
|
private readonly GameGui gameGui;
|
||||||
private readonly ObjectTable objectTable;
|
private readonly ObjectTable objectTable;
|
||||||
|
private readonly WindowSystem windowSystem;
|
||||||
private readonly ApiController apiController;
|
private readonly ApiController apiController;
|
||||||
private CharacterManager? characterManager;
|
private CharacterManager? characterManager;
|
||||||
private IpcManager? ipcManager;
|
private IpcManager ipcManager;
|
||||||
public Plugin(DalamudPluginInterface pluginInterface, CommandManager commandManager,
|
public Plugin(DalamudPluginInterface pluginInterface, CommandManager commandManager,
|
||||||
Framework framework, ObjectTable objectTable, ClientState clientState, GameGui gameGui)
|
Framework framework, ObjectTable objectTable, ClientState clientState, GameGui gameGui)
|
||||||
{
|
{
|
||||||
@@ -45,10 +47,13 @@ namespace MareSynchronos
|
|||||||
Configuration = this.PluginInterface.GetPluginConfig() as Configuration ?? new Configuration();
|
Configuration = this.PluginInterface.GetPluginConfig() as Configuration ?? new Configuration();
|
||||||
Configuration.Initialize(this.PluginInterface);
|
Configuration.Initialize(this.PluginInterface);
|
||||||
|
|
||||||
|
windowSystem = new WindowSystem("MareSynchronos");
|
||||||
|
|
||||||
apiController = new ApiController(Configuration);
|
apiController = new ApiController(Configuration);
|
||||||
|
ipcManager = new IpcManager(PluginInterface);
|
||||||
|
|
||||||
// you might normally want to embed resources and load them from the manifest stream
|
// 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
|
new FileCacheContext().Dispose(); // make sure db is initialized I guess
|
||||||
|
|
||||||
@@ -59,9 +64,6 @@ namespace MareSynchronos
|
|||||||
{
|
{
|
||||||
ClientState_Login(null, null!);
|
ClientState_Login(null, null!);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.PluginInterface.UiBuilder.Draw += DrawUI;
|
|
||||||
this.PluginInterface.UiBuilder.OpenConfigUi += DrawConfigUI;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Name => "Mare Synchronos";
|
public string Name => "Mare Synchronos";
|
||||||
@@ -77,14 +79,28 @@ namespace MareSynchronos
|
|||||||
clientState.Logout -= ClientState_Logout;
|
clientState.Logout -= ClientState_Logout;
|
||||||
ipcManager?.Dispose();
|
ipcManager?.Dispose();
|
||||||
characterManager?.Dispose();
|
characterManager?.Dispose();
|
||||||
|
apiController?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ClientState_Login(object? sender, EventArgs e)
|
private void ClientState_Login(object? sender, EventArgs e)
|
||||||
{
|
{
|
||||||
PluginLog.Debug("Client login");
|
PluginLog.Debug("Client login");
|
||||||
ipcManager = new IpcManager(PluginInterface);
|
|
||||||
ipcManager.IpcManagerInitialized += IpcManager_IpcManagerInitialized;
|
Task.Run(async () =>
|
||||||
ipcManager.Initialize();
|
{
|
||||||
|
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)
|
CommandManager.AddHandler(commandName, new CommandInfo(OnCommand)
|
||||||
{
|
{
|
||||||
@@ -96,8 +112,8 @@ namespace MareSynchronos
|
|||||||
{
|
{
|
||||||
PluginLog.Debug("Client logout");
|
PluginLog.Debug("Client logout");
|
||||||
characterManager?.Dispose();
|
characterManager?.Dispose();
|
||||||
ipcManager?.Dispose();
|
PluginInterface.UiBuilder.Draw -= Draw;
|
||||||
ipcManager = null!;
|
PluginInterface.UiBuilder.OpenConfigUi -= OpenConfigUI;
|
||||||
CommandManager.RemoveHandler(commandName);
|
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();
|
this.PluginUi.Toggle();
|
||||||
}
|
|
||||||
|
|
||||||
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());
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnCommand(string command, string args)
|
private void OnCommand(string command, string args)
|
||||||
|
|||||||
@@ -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;
|
||||||
using System.Numerics;
|
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
|
// It is good to have this be disposable in general, in case you ever need it
|
||||||
// to do any cleanup
|
// to do any cleanup
|
||||||
class PluginUI : IDisposable
|
class PluginUI : Window, IDisposable
|
||||||
{
|
{
|
||||||
private Configuration configuration;
|
private Configuration configuration;
|
||||||
|
private readonly WindowSystem windowSystem;
|
||||||
// this extra bool exists for ImGui, since you can't ref a property
|
private readonly ApiController apiController;
|
||||||
private bool visible = false;
|
private readonly IpcManager ipcManager;
|
||||||
public bool Visible
|
private string? uid;
|
||||||
{
|
private const string mainServer = "Lunae Crescere Incipientis (Central Server EU)";
|
||||||
get { return this.visible; }
|
|
||||||
set { this.visible = value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool settingsVisible = false;
|
|
||||||
public bool SettingsVisible
|
|
||||||
{
|
|
||||||
get { return this.settingsVisible; }
|
|
||||||
set { this.settingsVisible = value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
// passing in the image here just for simplicity
|
// 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.configuration = configuration;
|
||||||
|
this.windowSystem = windowSystem;
|
||||||
|
this.apiController = apiController;
|
||||||
|
this.ipcManager = ipcManager;
|
||||||
|
windowSystem.AddWindow(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
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
|
if (!IsOpen)
|
||||||
// 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)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.SetNextWindowSize(new Vector2(375, 330), ImGuiCond.FirstUseEver);
|
if (string.IsNullOrEmpty(apiController.SecretKey))
|
||||||
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 (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);
|
return true;
|
||||||
if (ImGui.Begin("QUALITY UI DEVELOPMENT", ref this.settingsVisible,
|
}
|
||||||
ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse))
|
|
||||||
{
|
private void PrintServerState()
|
||||||
// can't ref a property, so use a local copy
|
{
|
||||||
string penumbraFolder = configuration.CacheFolder;
|
ImGui.Text("Service status of " + (string.IsNullOrEmpty(configuration.ApiUri) ? mainServer : configuration.ApiUri));
|
||||||
if(ImGui.InputText("Penumbra mod folder", ref penumbraFolder, 255)) {
|
ImGui.SameLine();
|
||||||
this.configuration.CacheFolder = penumbraFolder;
|
var color = apiController.IsConnected ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudRed;
|
||||||
this.configuration.Save();
|
ImGui.TextColored(color, apiController.IsConnected ? "Available" : "Unavailable");
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGui.End();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,32 +5,74 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace MareSynchronos.WebAPI
|
namespace MareSynchronos.WebAPI
|
||||||
{
|
{
|
||||||
public class ApiController
|
public class ApiController : IDisposable
|
||||||
{
|
{
|
||||||
private readonly Configuration pluginConfiguration;
|
private readonly Configuration pluginConfiguration;
|
||||||
|
private const string mainService = "https://localhost:6591";
|
||||||
private string SecretKey => pluginConfiguration.ClientSecret;
|
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 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)
|
public ApiController(Configuration pluginConfiguration)
|
||||||
{
|
{
|
||||||
this.pluginConfiguration = 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()
|
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);
|
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)
|
public async Task UploadFile(string filePath)
|
||||||
@@ -73,5 +115,10 @@ namespace MareSynchronos.WebAPI
|
|||||||
List<string> whitelist = new();
|
List<string> whitelist = new();
|
||||||
return whitelist;
|
return whitelist;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
cts?.Cancel();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user